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