我尝试使用本机会话启动协议在Android应用程序上实现SIP客户端。
如果我创建一个Activity并输入以下代码,那么一切都很好:
public class WalkieTalkieActivity extends AppCompatActivity implements View.OnTouchListener {
public String sipAddress = null;
public SipManager manager = null;
public SipProfile me = null;
public SipAudioCall call = null;
public IncomingCallReceiver callReceiver;
private static final int CALL_ADDRESS = 1;
private static final int SET_AUTH_INFO = 2;
private static final int UPDATE_SETTINGS_DIALOG = 3;
private static final int HANG_UP = 4;
private final int PERMISSIONS_REQUEST_RECORD_AUDIO = 1;
private final int PERMISSIONS_REQUEST_USE_SIP = 2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_walkietalkie);
//initRecorderParameters(new int[]{8000, 11025, 16000, 22050, 44100});
//Richiedo i vari permessi
if (!hasUseSipPermission()) { requestUseSipPermission(); }
//Rchiedo i vari permessi
if (!hasRecordAudioPermission()) { requestRecordAudioPermission(); }
if (!SipManager.isVoipSupported(this)) {
//Visualizzo il messaggio voip non supportato e torno alla dahboard
Toast.makeText(WalkieTalkieActivity.this, "Voip non supportato su questo telefono", Toast.LENGTH_LONG).show();
Intent dashboard = new Intent(WalkieTalkieActivity.this,MainActivity.class);
// passo all'attivazione dell'activity page1.java
startActivity(dashboard);
finish();
}
if (!SipManager.isApiSupported(this)) {
//Visualizzo il messaggio voip non supportato e torno alla dahboard
Toast.makeText(WalkieTalkieActivity.this, "API non supportate su questo telefono", Toast.LENGTH_LONG).show();
Intent dashboard = new Intent(WalkieTalkieActivity.this,MainActivity.class);
// passo all'attivazione dell'activity page1.java
startActivity(dashboard);
finish();
}
// "Push to talk" can be a serious pain when the screen keeps turning off.
// Let's prevent that.
//getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// use this to start and trigger a service
Intent i = new Intent(this, SipEngine.class);
// potentially add data to the intent
i.putExtra("KEY1", "Value to be used by the service");
this.startService(i);
initializeManager();
}
@Override
public void onStart() {
super.onStart();
// When we get back from the preference setting Activity, assume
// settings have changed, and re-login with new auth info.
initializeManager();
}
@Override
public void onDestroy() {
super.onDestroy();
if (call != null) {
call.close();
}
closeLocalProfile();
if (callReceiver != null) {
this.unregisterReceiver(callReceiver);
}
}
public void initializeManager() {
if(manager == null) {
manager = SipManager.newInstance(this);
}
initializeLocalProfile();
}
/**
* Logs you into your SIP provider, registering this device as the location to
* send SIP calls to for your SIP address.
*/
public void initializeLocalProfile() {
if (manager == null) {
return;
}
if (me != null) {
closeLocalProfile();
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
//String username = prefs.getString("namePref", "");
//String domain = prefs.getString("domainPref", "");
//String password = prefs.getString("passPref", "");
String username = "username";
String domain = "domain";
String password = "password";
// Set up the intent filter. This will be used to fire an
// IncomingCallReceiver when someone calls the SIP address used by this
// application.
IntentFilter filter = new IntentFilter();
filter.addAction("android.vohippo.INCOMING_CALL");
callReceiver = new IncomingCallReceiver();
this.registerReceiver(callReceiver, filter);
try {
Log.d("VOHIPPOSERVICE", "PROVO A REGISTRARE");
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
//Costruisco il builder
me = builder.build();
//Intent
Intent i = new Intent();
i.setAction("android.vohippo.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
// This listener must be added AFTER manager.open is called,
// Otherwise the methods aren't guaranteed to fire.
manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
updateStatus("Registering with SIP Server...");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Attivazione in corso...");
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
updateStatus("Ready");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio disponibile");
}
public void onRegistrationFailed(String localProfileUri, int errorCode,
String errorMessage) {
updateStatus("Registration failed. Please check settings.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio non disponibile");
}
});
} catch (ParseException pe) {
updateStatus("Connection Error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore di connessione");
} catch (SipException se) {
updateStatus("Connection error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore connessione");
}
}
/**
* Closes out your local profile, freeing associated objects into memory
* and unregistering your device from the server.
*/
public void closeLocalProfile() {
if (manager == null) {
return;
}
try {
if (me != null) {
manager.close(me.getUriString());
}
} catch (Exception ee) {
Log.d("WalkieTalkieActivity", "Failed to close local profile.", ee);
}
}
/**
* Make an outgoing call.
*/
public void initiateCall() {
Toast.makeText(WalkieTalkieActivity.this, "Chiamata lanciata", Toast.LENGTH_SHORT).show();
updateStatus(sipAddress);
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
// Much of the client's interaction with the SIP Stack will
// happen via listeners. Even making an outgoing call, don't
// forget to set up a listener to set things up once the call is established.
@Override
public void onCallEstablished(SipAudioCall call) {
call.startAudio();
call.setSpeakerMode(true);
if (call.isMuted()) {
call.toggleMute();
}
updateStatus(call);
Toast.makeText(WalkieTalkieActivity.this, "Chiamata stabilita", Toast.LENGTH_SHORT).show();
}
@Override
public void onCallEnded(SipAudioCall call) {
Toast.makeText(WalkieTalkieActivity.this, "Chiamata terminata", Toast.LENGTH_SHORT).show();
updateStatus("Ready.");
}
};
call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);
}
catch (Exception e) {
Log.i("WalkieTalkieActivity", "Error when trying to close manager.", e);
if (me != null) {
try {
manager.close(me.getUriString());
} catch (Exception ee) {
Log.i("WalkieTalkieActivity",
"Error when trying to close manager.", ee);
ee.printStackTrace();
}
}
if (call != null) {
call.close();
}
}
}
/**
* Updates the status box at the top of the UI with a messege of your choice.
* @param status The String to display in the status box.
*/
public void updateStatus(final String status) {
// Be a good citizen. Make sure UI changes fire on the UI thread.
this.runOnUiThread(new Runnable() {
public void run() {
TextView labelView = (TextView) findViewById(R.id.sipLabel);
labelView.setText(status);
}
});
}
/**
* Updates the status box with the SIP address of the current call.
* @param call The current, active call.
*/
public void updateStatus(SipAudioCall call) {
String useName = call.getPeerProfile().getDisplayName();
if(useName == null) {
useName = call.getPeerProfile().getUserName();
}
updateStatus(useName + "@" + call.getPeerProfile().getSipDomain());
}
/**
* Updates whether or not the user's voice is muted, depending on whether the button is pressed.
* @param v The View where the touch event is being fired.
* @param event The motion to act on.
* @return boolean Returns false to indicate that the parent view should handle the touch event
* as it normally would.
*/
public boolean onTouch(View v, MotionEvent event) {
if (call == null) {
return false;
} else if (event.getAction() == MotionEvent.ACTION_DOWN && call != null && call.isMuted()) {
call.toggleMute();
} else if (event.getAction() == MotionEvent.ACTION_UP && !call.isMuted()) {
call.toggleMute();
}
return false;
}
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, CALL_ADDRESS, 0, "Call someone");
menu.add(0, SET_AUTH_INFO, 0, "Edit your SIP Info.");
menu.add(0, HANG_UP, 0, "End Current Call.");
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case CALL_ADDRESS:
showDialog(CALL_ADDRESS);
break;
case SET_AUTH_INFO:
updatePreferences();
break;
case HANG_UP:
if(call != null) {
try {
call.endCall();
} catch (SipException se) {
Log.d("WalkieTalkieActivity",
"Error ending call.", se);
}
call.close();
}
break;
}
return true;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case CALL_ADDRESS:
LayoutInflater factory = LayoutInflater.from(this);
final View textBoxView = factory.inflate(R.layout.call_address_dialog, null);
return new AlertDialog.Builder(this)
.setTitle("Call Someone.")
.setView(textBoxView)
.setPositiveButton(
android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
EditText textField = (EditText)
(textBoxView.findViewById(R.id.calladdress_edit));
sipAddress = textField.getText().toString();
initiateCall();
}
})
.setNegativeButton(
android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Noop.
}
})
.create();
case UPDATE_SETTINGS_DIALOG:
return new AlertDialog.Builder(this)
.setMessage("Please update your SIP Account Settings.")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
updatePreferences();
}
})
.setNegativeButton(
android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Noop.
}
})
.create();
}
return null;
}
public void updatePreferences() {
//Intent settingsActivity = new Intent(getBaseContext(),
// SipSettings.class);
//startActivity(settingsActivity);
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSIONS_REQUEST_USE_SIP:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(WalkieTalkieActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WalkieTalkieActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
}
case PERMISSIONS_REQUEST_RECORD_AUDIO:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(WalkieTalkieActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WalkieTalkieActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
}
}
}
//Controllo che il sistema abbia il permesso di registrare audio
private boolean hasRecordAudioPermission(){
boolean hasPermission = (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED);
Log.d("VOHIPPO", "Permesso di registrare audio? " + hasPermission);
return hasPermission;
}
private void requestRecordAudioPermission(){
String requiredPermission = Manifest.permission.RECORD_AUDIO;
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
requiredPermission)) {
Toast.makeText(WalkieTalkieActivity.this, "Quest applicazione richiede il permesso di registrare audio", Toast.LENGTH_SHORT).show();
}
// request the permission.
ActivityCompat.requestPermissions(this,
new String[]{requiredPermission},
PERMISSIONS_REQUEST_RECORD_AUDIO);
}
//Controllo che il sistema abbia il permesso di registrare audio
private boolean hasUseSipPermission(){
boolean hasPermission = (ContextCompat.checkSelfPermission(this,
Manifest.permission.USE_SIP) == PackageManager.PERMISSION_GRANTED);
Log.d("VOHIPPO", "Permesso di usare sip? " + hasPermission);
return hasPermission;
}
private void requestUseSipPermission(){
String requiredPermission = Manifest.permission.USE_SIP;
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
requiredPermission)) {
Toast.makeText(WalkieTalkieActivity.this, "Quest applicazione richiede il permesso di usare SIP", Toast.LENGTH_SHORT).show();
}
// request the permission.
ActivityCompat.requestPermissions(this,
new String[]{requiredPermission},
PERMISSIONS_REQUEST_USE_SIP);
}
但是如果我创建一个服务并尝试对此服务执行相同的操作我没有错误,但我的客户端没有注册服务器。
这是我投入服务的代码:
public class SipEngine extends IntentService {
public String sipAddress = null;
public SipManager manager = null;
public SipProfile me = null;
public SipAudioCall call = null;
public IncomingCallReceiver callReceiver;
private static final int CALL_ADDRESS = 1;
private static final int SET_AUTH_INFO = 2;
private static final int UPDATE_SETTINGS_DIALOG = 3;
private static final int HANG_UP = 4;
private int NOTIFICATION = 10002; //Any unique number for this notification
public SipEngine() {
super("SipEngine");
}
@Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
initializeManager();
sendNotification();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//@Override
//public void onStart() {
// super.onStart();
// When we get back from the preference setting Activity, assume
// settings have changed, and re-login with new auth info.
//initializeManager();
//}
@Override
public void onDestroy() {
super.onDestroy();
if (call != null) {
call.close();
}
closeLocalProfile();
if (callReceiver != null) {
this.unregisterReceiver(callReceiver);
}
}
public void initializeManager() {
if(manager == null) {
manager = SipManager.newInstance(this);
}
initializeLocalProfile();
}
/**
* Logs you into your SIP provider, registering this device as the location to
* send SIP calls to for your SIP address.
*/
public void initializeLocalProfile() {
if (manager == null) {
return;
}
if (me != null) {
closeLocalProfile();
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
//String username = prefs.getString("namePref", "");
//String domain = prefs.getString("domainPref", "");
//String password = prefs.getString("passPref", "");
String username = "username";
String domain = "server";
String password = "password";
// Set up the intent filter. This will be used to fire an
// IncomingCallReceiver when someone calls the SIP address used by this
// application.
IntentFilter filter = new IntentFilter();
filter.addAction("android.vohippo.INCOMING_CALL");
callReceiver = new IncomingCallReceiver();
this.registerReceiver(callReceiver, filter);
try {
Log.d("VOHIPPOSERVICE", "PROVO A REGISTRARE");
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
//Costruisco il builder
me = builder.build();
//Intent
Intent i = new Intent();
i.setAction("android.vohippo.INCOMING_CALL");
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
manager.open(me, pi, null);
// This listener must be added AFTER manager.open is called,
// Otherwise the methods aren't guaranteed to fire.
manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
//updateStatus("Registering with SIP Server...");
updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Attivazione in corso...");
}
public void onRegistrationDone(String localProfileUri, long expiryTime) {
//updateStatus("Ready");
updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio disponibile");
}
public void onRegistrationFailed(String localProfileUri, int errorCode,
String errorMessage) {
//updateStatus("Registration failed. Please check settings.");
updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Servizio non disponibile");
}
});
} catch (ParseException pe) {
//updateStatus("Connection Error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore di connessione");
} catch (SipException se) {
//updateStatus("Connection error.");
//updateNotification(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary), "Errore connessione");
}
}
/**
* Closes out your local profile, freeing associated objects into memory
* and unregistering your device from the server.
*/
public void closeLocalProfile() {
if (manager == null) {
return;
}
try {
if (me != null) {
manager.close(me.getUriString());
}
} catch (Exception ee) {
Log.d("WalkieTalkieActivity", "Failed to close local profile.", ee);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Send simple notification using the NotificationCompat API.
*/
public void sendNotification() {
// Use NotificationCompat.Builder to set up our notification.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
//icon appears in device notification bar and right hand corner of notification
builder.setSmallIcon(R.drawable.ic_service_sip);
// This intent is fired when notification is clicked
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://stacktips.com/"));
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
// Set the intent that will fire when the user taps the notification.
builder.setContentIntent(pendingIntent);
// Large icon appears on the left of the notification
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_service_sip));
// Content title, which appears in large type at the top of the notification
builder.setContentTitle("Vohippo");
int color = ContextCompat.getColor(this, R.color.colorPrimary);
builder.setColor(color);
// Content text, which appears in smaller text below the title
builder.setContentText("491023456");
// The subtext, whichppears under the text on newer devices.
// This will show-up in the devices with Android 4.2 and above only
builder.setSubText("Attendere...");
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Will display the notification in the notification bar
notificationManager.notify(NOTIFICATION, builder.build());
}
/**
* Send simple notification using the NotificationCompat API.
*/
public void updateNotification(int color, String status) {
// Use NotificationCompat.Builder to set up our notification.
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(color);
// The subtext, which appears under the text on newer devices.
// This will show-up in the devices with Android 4.2 and above only
builder.setSubText(status);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Will display the notification in the notification bar
notificationManager.notify(NOTIFICATION, builder.build());
}
我应该在服务中做一些不同的事情而不是正常的活动吗? 比你好。