|  | /* | 
|  | * Copyright (C) 2010 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package com.example.android.sip; | 
|  |  | 
|  | import android.app.Activity; | 
|  | import android.app.AlertDialog; | 
|  | import android.app.Dialog; | 
|  | import android.app.PendingIntent; | 
|  | import android.content.DialogInterface; | 
|  | import android.content.Intent; | 
|  | import android.content.IntentFilter; | 
|  | import android.content.SharedPreferences; | 
|  | import android.os.Bundle; | 
|  | import android.preference.PreferenceManager; | 
|  | import android.util.Log; | 
|  | import android.view.*; | 
|  | import android.net.sip.*; | 
|  | import android.widget.EditText; | 
|  | import android.widget.TextView; | 
|  | import android.widget.ToggleButton; | 
|  |  | 
|  | import java.text.ParseException; | 
|  |  | 
|  | /** | 
|  | * Handles all calling, receiving calls, and UI interaction in the WalkieTalkie app. | 
|  | */ | 
|  | public class WalkieTalkieActivity extends Activity 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; | 
|  |  | 
|  |  | 
|  | @Override | 
|  | public void onCreate(Bundle savedInstanceState) { | 
|  |  | 
|  | super.onCreate(savedInstanceState); | 
|  | setContentView(R.layout.walkietalkie); | 
|  |  | 
|  | ToggleButton pushToTalkButton = (ToggleButton) findViewById(R.id.pushToTalk); | 
|  | pushToTalkButton.setOnTouchListener(this); | 
|  |  | 
|  | // 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.SipDemo.INCOMING_CALL"); | 
|  | callReceiver = new IncomingCallReceiver(); | 
|  | this.registerReceiver(callReceiver, filter); | 
|  |  | 
|  | // "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); | 
|  |  | 
|  | 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", ""); | 
|  |  | 
|  | if (username.length() == 0 || domain.length() == 0 || password.length() == 0) { | 
|  | showDialog(UPDATE_SETTINGS_DIALOG); | 
|  | return; | 
|  | } | 
|  |  | 
|  | try { | 
|  | SipProfile.Builder builder = new SipProfile.Builder(username, domain); | 
|  | builder.setPassword(password); | 
|  | me = builder.build(); | 
|  |  | 
|  | Intent i = new Intent(); | 
|  | i.setAction("android.SipDemo.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..."); | 
|  | } | 
|  |  | 
|  | public void onRegistrationDone(String localProfileUri, long expiryTime) { | 
|  | updateStatus("Ready"); | 
|  | } | 
|  |  | 
|  | public void onRegistrationFailed(String localProfileUri, int errorCode, | 
|  | String errorMessage) { | 
|  | updateStatus("Registration failed.  Please check settings."); | 
|  | } | 
|  | }); | 
|  | } catch (ParseException pe) { | 
|  | updateStatus("Connection Error."); | 
|  | } catch (SipException se) { | 
|  | updateStatus("Connection error."); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * 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/onDestroy", "Failed to close local profile.", ee); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Make an outgoing call. | 
|  | */ | 
|  | public void initiateCall() { | 
|  |  | 
|  | 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); | 
|  | call.toggleMute(); | 
|  | updateStatus(call); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void onCallEnded(SipAudioCall call) { | 
|  | updateStatus("Ready."); | 
|  | } | 
|  | }; | 
|  |  | 
|  | call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30); | 
|  |  | 
|  | } | 
|  | catch (Exception e) { | 
|  | Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", e); | 
|  | if (me != null) { | 
|  | try { | 
|  | manager.close(me.getUriString()); | 
|  | } catch (Exception ee) { | 
|  | Log.i("WalkieTalkieActivity/InitiateCall", | 
|  | "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/onOptionsItemSelected", | 
|  | "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); | 
|  | } | 
|  | } |