summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonPresenter.java1
-rw-r--r--InCallUI/src/com/android/incallui/CallCommandClient.java60
-rw-r--r--InCallUI/src/com/android/incallui/CallHandlerService.java39
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java19
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java12
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java90
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());