1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.sip; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.Dialog; 22 import android.app.PendingIntent; 23 import android.content.DialogInterface; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.SharedPreferences; 27 import android.os.Bundle; 28 import android.preference.PreferenceManager; 29 import android.util.Log; 30 import android.view.*; 31 import android.net.sip.*; 32 import android.widget.EditText; 33 import android.widget.TextView; 34 import android.widget.ToggleButton; 35 36 import java.text.ParseException; 37 38 /** 39 * Handles all calling, receiving calls, and UI interaction in the WalkieTalkie app. 40 */ 41 public class WalkieTalkieActivity extends Activity implements View.OnTouchListener { 42 43 public String sipAddress = null; 44 45 public SipManager manager = null; 46 public SipProfile me = null; 47 public SipAudioCall call = null; 48 public IncomingCallReceiver callReceiver; 49 50 private static final int CALL_ADDRESS = 1; 51 private static final int SET_AUTH_INFO = 2; 52 private static final int UPDATE_SETTINGS_DIALOG = 3; 53 private static final int HANG_UP = 4; 54 55 56 @Override onCreate(Bundle savedInstanceState)57 public void onCreate(Bundle savedInstanceState) { 58 59 super.onCreate(savedInstanceState); 60 setContentView(R.layout.walkietalkie); 61 62 ToggleButton pushToTalkButton = (ToggleButton) findViewById(R.id.pushToTalk); 63 pushToTalkButton.setOnTouchListener(this); 64 65 // Set up the intent filter. This will be used to fire an 66 // IncomingCallReceiver when someone calls the SIP address used by this 67 // application. 68 IntentFilter filter = new IntentFilter(); 69 filter.addAction("android.SipDemo.INCOMING_CALL"); 70 callReceiver = new IncomingCallReceiver(); 71 this.registerReceiver(callReceiver, filter); 72 73 // "Push to talk" can be a serious pain when the screen keeps turning off. 74 // Let's prevent that. 75 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 76 77 initializeManager(); 78 } 79 80 @Override onStart()81 public void onStart() { 82 super.onStart(); 83 // When we get back from the preference setting Activity, assume 84 // settings have changed, and re-login with new auth info. 85 initializeManager(); 86 } 87 88 @Override onDestroy()89 public void onDestroy() { 90 super.onDestroy(); 91 if (call != null) { 92 call.close(); 93 } 94 95 closeLocalProfile(); 96 97 if (callReceiver != null) { 98 this.unregisterReceiver(callReceiver); 99 } 100 } 101 initializeManager()102 public void initializeManager() { 103 if(manager == null) { 104 manager = SipManager.newInstance(this); 105 } 106 107 initializeLocalProfile(); 108 } 109 110 /** 111 * Logs you into your SIP provider, registering this device as the location to 112 * send SIP calls to for your SIP address. 113 */ initializeLocalProfile()114 public void initializeLocalProfile() { 115 if (manager == null) { 116 return; 117 } 118 119 if (me != null) { 120 closeLocalProfile(); 121 } 122 123 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); 124 String username = prefs.getString("namePref", ""); 125 String domain = prefs.getString("domainPref", ""); 126 String password = prefs.getString("passPref", ""); 127 128 if (username.length() == 0 || domain.length() == 0 || password.length() == 0) { 129 showDialog(UPDATE_SETTINGS_DIALOG); 130 return; 131 } 132 133 try { 134 SipProfile.Builder builder = new SipProfile.Builder(username, domain); 135 builder.setPassword(password); 136 me = builder.build(); 137 138 Intent i = new Intent(); 139 i.setAction("android.SipDemo.INCOMING_CALL"); 140 PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA); 141 manager.open(me, pi, null); 142 143 144 // This listener must be added AFTER manager.open is called, 145 // Otherwise the methods aren't guaranteed to fire. 146 147 manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() { 148 public void onRegistering(String localProfileUri) { 149 updateStatus("Registering with SIP Server..."); 150 } 151 152 public void onRegistrationDone(String localProfileUri, long expiryTime) { 153 updateStatus("Ready"); 154 } 155 156 public void onRegistrationFailed(String localProfileUri, int errorCode, 157 String errorMessage) { 158 updateStatus("Registration failed. Please check settings."); 159 } 160 }); 161 } catch (ParseException pe) { 162 updateStatus("Connection Error."); 163 } catch (SipException se) { 164 updateStatus("Connection error."); 165 } 166 } 167 168 /** 169 * Closes out your local profile, freeing associated objects into memory 170 * and unregistering your device from the server. 171 */ closeLocalProfile()172 public void closeLocalProfile() { 173 if (manager == null) { 174 return; 175 } 176 try { 177 if (me != null) { 178 manager.close(me.getUriString()); 179 } 180 } catch (Exception ee) { 181 Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee); 182 } 183 } 184 185 /** 186 * Make an outgoing call. 187 */ initiateCall()188 public void initiateCall() { 189 190 updateStatus(sipAddress); 191 192 try { 193 SipAudioCall.Listener listener = new SipAudioCall.Listener() { 194 // Much of the client's interaction with the SIP Stack will 195 // happen via listeners. Even making an outgoing call, don't 196 // forget to set up a listener to set things up once the call is established. 197 @Override 198 public void onCallEstablished(SipAudioCall call) { 199 call.startAudio(); 200 call.setSpeakerMode(true); 201 call.toggleMute(); 202 updateStatus(call); 203 } 204 205 @Override 206 public void onCallEnded(SipAudioCall call) { 207 updateStatus("Ready."); 208 } 209 }; 210 211 call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30); 212 213 } 214 catch (Exception e) { 215 Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", e); 216 if (me != null) { 217 try { 218 manager.close(me.getUriString()); 219 } catch (Exception ee) { 220 Log.i("WalkieTalkieActivity/InitiateCall", 221 "Error when trying to close manager.", ee); 222 ee.printStackTrace(); 223 } 224 } 225 if (call != null) { 226 call.close(); 227 } 228 } 229 } 230 231 /** 232 * Updates the status box at the top of the UI with a messege of your choice. 233 * @param status The String to display in the status box. 234 */ updateStatus(final String status)235 public void updateStatus(final String status) { 236 // Be a good citizen. Make sure UI changes fire on the UI thread. 237 this.runOnUiThread(new Runnable() { 238 public void run() { 239 TextView labelView = (TextView) findViewById(R.id.sipLabel); 240 labelView.setText(status); 241 } 242 }); 243 } 244 245 /** 246 * Updates the status box with the SIP address of the current call. 247 * @param call The current, active call. 248 */ updateStatus(SipAudioCall call)249 public void updateStatus(SipAudioCall call) { 250 String useName = call.getPeerProfile().getDisplayName(); 251 if(useName == null) { 252 useName = call.getPeerProfile().getUserName(); 253 } 254 updateStatus(useName + "@" + call.getPeerProfile().getSipDomain()); 255 } 256 257 /** 258 * Updates whether or not the user's voice is muted, depending on whether the button is pressed. 259 * @param v The View where the touch event is being fired. 260 * @param event The motion to act on. 261 * @return boolean Returns false to indicate that the parent view should handle the touch event 262 * as it normally would. 263 */ onTouch(View v, MotionEvent event)264 public boolean onTouch(View v, MotionEvent event) { 265 if (call == null) { 266 return false; 267 } else if (event.getAction() == MotionEvent.ACTION_DOWN && call != null && call.isMuted()) { 268 call.toggleMute(); 269 } else if (event.getAction() == MotionEvent.ACTION_UP && !call.isMuted()) { 270 call.toggleMute(); 271 } 272 return false; 273 } 274 onCreateOptionsMenu(Menu menu)275 public boolean onCreateOptionsMenu(Menu menu) { 276 menu.add(0, CALL_ADDRESS, 0, "Call someone"); 277 menu.add(0, SET_AUTH_INFO, 0, "Edit your SIP Info."); 278 menu.add(0, HANG_UP, 0, "End Current Call."); 279 280 return true; 281 } 282 onOptionsItemSelected(MenuItem item)283 public boolean onOptionsItemSelected(MenuItem item) { 284 switch (item.getItemId()) { 285 case CALL_ADDRESS: 286 showDialog(CALL_ADDRESS); 287 break; 288 case SET_AUTH_INFO: 289 updatePreferences(); 290 break; 291 case HANG_UP: 292 if(call != null) { 293 try { 294 call.endCall(); 295 } catch (SipException se) { 296 Log.d("WalkieTalkieActivity/onOptionsItemSelected", 297 "Error ending call.", se); 298 } 299 call.close(); 300 } 301 break; 302 } 303 return true; 304 } 305 306 @Override onCreateDialog(int id)307 protected Dialog onCreateDialog(int id) { 308 switch (id) { 309 case CALL_ADDRESS: 310 311 LayoutInflater factory = LayoutInflater.from(this); 312 final View textBoxView = factory.inflate(R.layout.call_address_dialog, null); 313 return new AlertDialog.Builder(this) 314 .setTitle("Call Someone.") 315 .setView(textBoxView) 316 .setPositiveButton( 317 android.R.string.ok, new DialogInterface.OnClickListener() { 318 public void onClick(DialogInterface dialog, int whichButton) { 319 EditText textField = (EditText) 320 (textBoxView.findViewById(R.id.calladdress_edit)); 321 sipAddress = textField.getText().toString(); 322 initiateCall(); 323 324 } 325 }) 326 .setNegativeButton( 327 android.R.string.cancel, new DialogInterface.OnClickListener() { 328 public void onClick(DialogInterface dialog, int whichButton) { 329 // Noop. 330 } 331 }) 332 .create(); 333 334 case UPDATE_SETTINGS_DIALOG: 335 return new AlertDialog.Builder(this) 336 .setMessage("Please update your SIP Account Settings.") 337 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 338 public void onClick(DialogInterface dialog, int whichButton) { 339 updatePreferences(); 340 } 341 }) 342 .setNegativeButton( 343 android.R.string.cancel, new DialogInterface.OnClickListener() { 344 public void onClick(DialogInterface dialog, int whichButton) { 345 // Noop. 346 } 347 }) 348 .create(); 349 } 350 return null; 351 } 352 353 public void updatePreferences() { 354 Intent settingsActivity = new Intent(getBaseContext(), 355 SipSettings.class); 356 startActivity(settingsActivity); 357 } 358 } 359