diff options
Diffstat (limited to 'InCallUI')
6 files changed, 175 insertions, 46 deletions
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java index 3c1ada1fc..12cc65663 100644 --- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java +++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java @@ -54,7 +54,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto @Override public void onStateChange(InCallState state, CallList callList) { - if (state == InCallState.OUTGOING) { mCall = callList.getOutgoingCall(); } else if (state == InCallState.INCALL) { diff --git a/InCallUI/src/com/android/incallui/CallCommandClient.java b/InCallUI/src/com/android/incallui/CallCommandClient.java index 280bab32c..308776cb6 100644 --- a/InCallUI/src/com/android/incallui/CallCommandClient.java +++ b/InCallUI/src/com/android/incallui/CallCommandClient.java @@ -28,27 +28,27 @@ public class CallCommandClient { private static CallCommandClient sInstance; - public static CallCommandClient getInstance() { + public static synchronized CallCommandClient getInstance() { if (sInstance == null) { - throw new IllegalStateException("CallCommandClient has not been initialized."); + sInstance = new CallCommandClient(); } return sInstance; } - // TODO(klp): Not sure if static call is ok. Might need to switch to normal service binding. - public static void init(ICallCommandService service) { - Preconditions.checkState(sInstance == null); - sInstance = new CallCommandClient(service); - } - - private ICallCommandService mCommandService; - private CallCommandClient(ICallCommandService service) { + private CallCommandClient() { + } + + public void setService(ICallCommandService service) { mCommandService = service; } public void answerCall(int callId) { + if (mCommandService == null) { + Logger.e(this, "Cannot answer call; CallCommandService == null"); + return; + } try { mCommandService.answerCall(callId); } catch (RemoteException e) { @@ -57,6 +57,10 @@ public class CallCommandClient { } public void rejectCall(int callId, boolean rejectWithMessage, String message) { + if (mCommandService == null) { + Logger.e(this, "Cannot reject call; CallCommandService == null"); + return; + } try { mCommandService.rejectCall(callId, rejectWithMessage, message); } catch (RemoteException e) { @@ -65,6 +69,10 @@ public class CallCommandClient { } public void disconnectCall(int callId) { + if (mCommandService == null) { + Logger.e(this, "Cannot disconnect call; CallCommandService == null"); + return; + } try { mCommandService.disconnectCall(callId); } catch (RemoteException e) { @@ -73,6 +81,10 @@ public class CallCommandClient { } public void mute(boolean onOff) { + if (mCommandService == null) { + Logger.e(this, "Cannot mute call; CallCommandService == null"); + return; + } try { mCommandService.mute(onOff); } catch (RemoteException e) { @@ -81,6 +93,10 @@ public class CallCommandClient { } public void hold(int callId, boolean onOff) { + if (mCommandService == null) { + Logger.e(this, "Cannot hold call; CallCommandService == null"); + return; + } try { mCommandService.hold(callId, onOff); } catch (RemoteException e) { @@ -89,6 +105,10 @@ public class CallCommandClient { } public void merge() { + if (mCommandService == null) { + Logger.e(this, "Cannot merge call; CallCommandService == null"); + return; + } try { mCommandService.merge(); } catch (RemoteException e) { @@ -97,6 +117,10 @@ public class CallCommandClient { } public void swap() { + if (mCommandService == null) { + Logger.e(this, "Cannot swap call; CallCommandService == null"); + return; + } try { mCommandService.swap(); } catch (RemoteException e) { @@ -105,6 +129,10 @@ public class CallCommandClient { } public void addCall() { + if (mCommandService == null) { + Logger.e(this, "Cannot add call; CallCommandService == null"); + return; + } try { mCommandService.addCall(); } catch (RemoteException e) { @@ -113,6 +141,10 @@ public class CallCommandClient { } public void setAudioMode(int mode) { + if (mCommandService == null) { + Logger.e(this, "Cannot set audio mode; CallCommandService == null"); + return; + } try { mCommandService.setAudioMode(mode); } catch (RemoteException e) { @@ -121,6 +153,10 @@ public class CallCommandClient { } public void playDtmfTone(char digit, boolean timedShortTone) { + if (mCommandService == null) { + Logger.e(this, "Cannot start dtmf tone; CallCommandService == null"); + return; + } try { Logger.v(this, "Sending dtmf tone " + digit); mCommandService.playDtmfTone(digit, timedShortTone); @@ -131,6 +167,10 @@ public class CallCommandClient { } public void stopDtmfTone() { + if (mCommandService == null) { + Logger.e(this, "Cannot stop dtmf tone; CallCommandService == null"); + return; + } try { Logger.v(this, "Stop dtmf tone "); mCommandService.stopDtmfTone(); diff --git a/InCallUI/src/com/android/incallui/CallHandlerService.java b/InCallUI/src/com/android/incallui/CallHandlerService.java index bdee328f8..f0df1cdc0 100644 --- a/InCallUI/src/com/android/incallui/CallHandlerService.java +++ b/InCallUI/src/com/android/incallui/CallHandlerService.java @@ -44,6 +44,8 @@ public class CallHandlerService extends Service { private static final int ON_SUPPORTED_AUDIO_MODE = 5; private static final int ON_DISCONNECT_CALL = 6; + private static final int LARGEST_MSG_ID = ON_DISCONNECT_CALL; + private CallList mCallList; private Handler mMainHandler; @@ -52,6 +54,7 @@ public class CallHandlerService extends Service { @Override public void onCreate() { + Logger.d(this, "onCreate started"); super.onCreate(); mCallList = new CallList(); @@ -59,28 +62,52 @@ public class CallHandlerService extends Service { mAudioModeProvider = new AudioModeProvider(); mInCallPresenter = InCallPresenter.getInstance(); mInCallPresenter.setUp(getApplicationContext(), mCallList, mAudioModeProvider); + Logger.d(this, "onCreate finished"); } @Override public void onDestroy() { + Logger.d(this, "onDestroy started"); + + // Remove all pending messages before nulling out handler + for (int i = 1; i <= LARGEST_MSG_ID; i++) { + mMainHandler.removeMessages(i); + } + mMainHandler = null; + + // The service gets disconnected under two circumstances: + // 1. When there are no more calls + // 2. When the phone app crashes. + // If (2) happens, we can't leave the UI thinking that there are still + // live calls. So we will tell the callList to clear as a final request. + mCallList.clearOnDisconnect(); + mCallList = null; + mInCallPresenter.tearDown(); mInCallPresenter = null; mAudioModeProvider = null; - mMainHandler = null; - mCallList = null; + + Logger.d(this, "onDestroy finished"); } @Override public IBinder onBind(Intent intent) { + Logger.d(this, "onBind"); return mBinder; } + @Override + public boolean onUnbind(Intent intent) { + Logger.d(this, "onUnbind"); + return true; + } + private final ICallHandlerService.Stub mBinder = new ICallHandlerService.Stub() { @Override public void setCallCommandService(ICallCommandService service) { Logger.d(CallHandlerService.this, "onConnected: " + service.toString()); - CallCommandClient.init(service); + CallCommandClient.getInstance().setService(service); } @Override @@ -146,6 +173,12 @@ public class CallHandlerService extends Service { } private void executeMessage(Message msg) { + if (msg.what > LARGEST_MSG_ID) { + // If you got here, you may have added a new message and forgotten to + // update LARGEST_MSG_ID + Logger.wtf(this, "Cannot handle message larger than LARGEST_MSG_ID."); + } + Logger.d(this, "executeMessage " + msg.what); switch (msg.what) { diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index f692bf683..a3cee5f98 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -225,6 +225,25 @@ public class CallList { } /** + * This is called when the service disconnects, either expectedly or unexpectedly. + * For the expected case, it's because we have no calls left. For the unexpected case, + * it is likely a crash of phone and we need to clean up our calls manually. Without phone, + * there can be no active calls, so this is relatively safe thing to do. + */ + public void clearOnDisconnect() { + for (Call call : mCallMap.values()) { + final int state = call.getState(); + if (state != Call.State.IDLE && + state != Call.State.INVALID && + state != Call.State.DISCONNECTED) { + call.setState(Call.State.DISCONNECTED); + updateCallInMap(call); + } + } + notifyListenersOfChange(); + } + + /** * Sends a generic notification to all listeners that something has changed. * It is up to the listeners to call back to determine what changed. */ diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java index e1a979693..5edfaa32a 100644 --- a/InCallUI/src/com/android/incallui/InCallActivity.java +++ b/InCallUI/src/com/android/incallui/InCallActivity.java @@ -88,6 +88,9 @@ public class InCallActivity extends Activity { @Override protected void onDestroy() { Logger.d(this, "onDestroy()... this = " + this); + + tearDownPresenters(); + super.onDestroy(); } @@ -116,13 +119,7 @@ public class InCallActivity extends Activity { @Override public void finish() { Logger.d(this, "finish()..."); - tearDownPresenters(); - super.finish(); - - // TODO(klp): Actually finish the activity for now. Revisit performance implications of - // this before launch. - // moveTaskToBack(true); } @Override @@ -299,11 +296,14 @@ public class InCallActivity extends Activity { } private void tearDownPresenters() { + Logger.d(this, "Tearing down presenters."); InCallPresenter mainPresenter = InCallPresenter.getInstance(); mainPresenter.removeListener(mCallButtonFragment.getPresenter()); mainPresenter.removeListener(mCallCardFragment.getPresenter()); mainPresenter.removeListener(mAnswerFragment.getPresenter()); + + mainPresenter.setActivity(null); } private void toast(String text) { diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index d709ba49f..3b400ef7b 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -47,7 +47,7 @@ public class InCallPresenter implements CallList.Listener { private Context mContext; private CallList mCallList; private InCallActivity mInCallActivity; - + private boolean mServiceConnected = false; private InCallState mInCallState = InCallState.HIDDEN; public static synchronized InCallPresenter getInstance() { @@ -71,34 +71,44 @@ public class InCallPresenter implements CallList.Listener { mAudioModeProvider = audioModeProvider; + // This only gets called by the service so this is okay. + mServiceConnected = true; + Logger.d(this, "Finished InCallPresenter.setUp"); } + /** + * Called when the telephony service has disconnected from us. This will happen when there are + * no more active calls. However, we may still want to continue showing the UI for + * certain cases like showing "Call Ended". + * What we really want is to wait for the activity and the service to both disconnect before we + * tear things down. This method sets a serviceConnected boolean and calls a secondary method + * that performs the aforementioned logic. + */ public void tearDown() { - mAudioModeProvider = null; - - removeListener(mStatusBarNotifier); - mStatusBarNotifier = null; - - mCallList.removeListener(this); - mCallList = null; - - mContext = null; - mInCallActivity = null; - - mListeners.clear(); - - Logger.d(this, "Finished InCallPresenter.tearDown"); + Logger.d(this, "tearDown"); + mServiceConnected = false; + attemptCleanup(); } + /** + * Called when the UI begins or ends. Starts the callstate callbacks if the UI just began. + * Attempts to tear down everything if the UI just ended. See #tearDown for more insight on + * the tear-down process. + */ public void setActivity(InCallActivity inCallActivity) { mInCallActivity = inCallActivity; - Logger.d(this, "UI Initialized"); + if (mInCallActivity != null) { + Logger.d(this, "UI Initialized"); - // Since the UI just came up, imitate an update from the call list - // to set the proper UI state. - onCallListChange(mCallList); + // Since the UI just came up, imitate an update from the call list + // to set the proper UI state. + onCallListChange(mCallList); + } else { + Logger.d(this, "setActivity(null)"); + attemptCleanup(); + } } /** @@ -187,7 +197,9 @@ public class InCallPresenter implements CallList.Listener { public void onUiShowing(boolean showing) { // We need to update the notification bar when we leave the UI because that // could trigger it to show again. - mStatusBarNotifier.updateNotification(mInCallState, mCallList); + if (mStatusBarNotifier != null) { + mStatusBarNotifier.updateNotification(mInCallState, mCallList); + } } /** @@ -195,7 +207,7 @@ public class InCallPresenter implements CallList.Listener { * the UI needs to be started or finished depending on the new state and does it. */ private InCallState startOrFinishUi(InCallState newState) { - Logger.d(this, "startOrFInishUi: " + newState.toString()); + Logger.d(this, "startOrFinishUi: " + newState.toString()); // TODO(klp): Consider a proper state machine implementation @@ -228,8 +240,10 @@ public class InCallPresenter implements CallList.Listener { // [ AND NOW YOU'RE IN THE CALL. voila! ] // // Our app is started using a fullScreen notification. We need to do this whenever - // we get an incoming call. - final boolean startStartupSequence = (InCallState.INCOMING == newState); + // we get an incoming call or if this is the first time we are displaying (the previous + // state was HIDDEN). + final boolean startStartupSequence = (InCallState.INCOMING == newState || + InCallState.HIDDEN == mInCallState); // A new outgoing call indicates that the user just now dialed a number and when that // happens we need to display the screen immediateley. @@ -242,10 +256,10 @@ public class InCallPresenter implements CallList.Listener { Logger.v(this, "startStartupSequence: ", startStartupSequence); - if (startStartupSequence) { - mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(newState, mCallList); - } else if (showCallUi) { + if (showCallUi) { showInCall(); + } else if (startStartupSequence) { + mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(newState, mCallList); } else if (newState == InCallState.HIDDEN) { // The new state is the hidden state (no calls). Tear everything down. @@ -265,6 +279,30 @@ public class InCallPresenter implements CallList.Listener { return newState; } + /** + * Checks to see if both the UI is gone and the service is disconnected. If so, tear it all + * down. + */ + private void attemptCleanup() { + if (mInCallActivity == null && !mServiceConnected) { + Logger.d(this, "Start InCallPresenter.CleanUp"); + mAudioModeProvider = null; + + removeListener(mStatusBarNotifier); + mStatusBarNotifier = null; + + mCallList.removeListener(this); + mCallList = null; + + mContext = null; + mInCallActivity = null; + + mListeners.clear(); + + Logger.d(this, "Finished InCallPresenter.CleanUp"); + } + } + private void showInCall() { Logger.d(this, "Showing in call manually."); mContext.startActivity(getInCallIntent()); |