diff options
Diffstat (limited to 'InCallUI/src/com/android')
4 files changed, 220 insertions, 28 deletions
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java index e820929a0..b3deb641e 100644 --- a/InCallUI/src/com/android/incallui/AnswerPresenter.java +++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java @@ -64,6 +64,11 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> } @Override + public void onDisconnect(Call call) { + // no-op + } + + @Override public void onIncomingCall(Call call) { // TODO: Ui is being destroyed when the fragment detaches. Need clean up step to stop // getting updates here. diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index 53288de03..ba123b0c1 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -83,10 +83,15 @@ public class CallList { public void onDisconnect(Call call) { Log.d(this, "onDisconnect: ", call); - updateCallInMap(call); + boolean updated = updateCallInMap(call); - notifyCallUpdateListeners(call); - notifyListenersOfChange(); + if (updated) { + // notify those listening for changes on this specific change + notifyCallUpdateListeners(call); + + // notify those listening for all disconnects + notifyListenersOfDisconnect(call); + } } /** @@ -232,7 +237,6 @@ public class CallList { } public Call getFirstCall() { - // TODO: should we switch to a simple list and pull the first one? Call result = getIncomingCall(); if (result == null) { result = getOutgoingCall(); @@ -240,6 +244,12 @@ public class CallList { if (result == null) { result = getFirstCallWithState(Call.State.ACTIVE); } + if (result == null) { + result = getDisconnectingCall(); + } + if (result == null) { + result = getDisconnectedCall(); + } return result; } @@ -317,9 +327,21 @@ public class CallList { } } - private void updateCallInMap(Call call) { + private void notifyListenersOfDisconnect(Call call) { + for (Listener listener : mListeners) { + listener.onDisconnect(call); + } + } + + /** + * Updates the call entry in the local map. + * @return false if no call previously existed and no call was added, otherwise true. + */ + private boolean updateCallInMap(Call call) { Preconditions.checkNotNull(call); + boolean updated = false; + final Integer id = new Integer(call.getCallId()); if (call.getState() == Call.State.DISCONNECTED) { @@ -334,12 +356,17 @@ public class CallList { mHandler.sendMessageDelayed(msg, getDelayForDisconnect(call)); mCallMap.put(id, call); + updated = true; } } else if (!isCallDead(call)) { mCallMap.put(id, call); + updated = true; } else if (mCallMap.containsKey(id)) { mCallMap.remove(id); + updated = true; } + + return updated; } private int getDelayForDisconnect(Call call) { @@ -419,8 +446,28 @@ public class CallList { * to the call list. */ public interface Listener { - public void onCallListChange(CallList callList); + /** + * Called when a new incoming call comes in. + * This is the only method that gets called for incoming calls. Listeners + * that want to perform an action on incoming call should respond in this method + * because {@link #onCallListChange} does not automatically get called for + * incoming calls. + */ public void onIncomingCall(Call call); + + /** + * Called anytime there are changes to the call list. The change can be switching call + * states, updating information, etc. This method will NOT be called for new incoming + * calls and for calls that switch to disconnected state. Listeners must add actions + * to those method implementations if they want to deal with those actions. + */ + public void onCallListChange(CallList callList); + + /** + * Called when a call switches to the disconnected state. This is the only method + * that will get called upon disconnection. + */ + public void onDisconnect(Call call); } public interface CallUpdateListener { diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java index c3793ad2e..761691f4d 100644 --- a/InCallUI/src/com/android/incallui/InCallActivity.java +++ b/InCallUI/src/com/android/incallui/InCallActivity.java @@ -20,6 +20,10 @@ import com.android.services.telephony.common.Call; import com.android.services.telephony.common.Call.State; import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; @@ -37,12 +41,15 @@ public class InCallActivity extends Activity { public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad"; + private static final int INVALID_RES_ID = -1; + private CallButtonFragment mCallButtonFragment; private CallCardFragment mCallCardFragment; private AnswerFragment mAnswerFragment; private DialpadFragment mDialpadFragment; private ConferenceManagerFragment mConferenceManagerFragment; private boolean mIsForegroundActivity; + private AlertDialog mDialog; /** Use to pass 'showDialpad' from {@link #onNewIntent} to {@link #onResume} */ private boolean mShowDialpadRequested; @@ -145,8 +152,12 @@ public class InCallActivity extends Activity { */ @Override public void finish() { - Log.d(this, "finish()..."); - super.finish(); + Log.i(this, "finish(). Dialog showing: " + (mDialog != null)); + + // skip finish if we are still showing a dialog. + if (mDialog == null) { + super.finish(); + } } @Override @@ -352,4 +363,72 @@ public class InCallActivity extends Activity { } return super.dispatchPopulateAccessibilityEvent(event); } + + public void maybeShowErrorDialogOnDisconnect(Call.DisconnectCause cause) { + Log.d(this, "maybeShowErrorDialogOnDisconnect"); + + if (!isFinishing()) { + final int resId = getResIdForDisconnectCause(cause); + if (resId != INVALID_RES_ID) { + showErrorDialog(resId); + } + } + } + + public void dismissPendingDialogs() { + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + } + + /** + * Utility function to bring up a generic "error" dialog. + */ + private void showErrorDialog(int resId) { + final CharSequence msg = getResources().getText(resId); + Log.i(this, "Show Dialog: " + msg); + + dismissPendingDialogs(); + + mDialog = new AlertDialog.Builder(this) + .setMessage(msg) + .setPositiveButton(R.string.ok, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + onDialogDismissed(); + }}) + .setOnCancelListener(new OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + onDialogDismissed(); + }}) + .create(); + + mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + mDialog.show(); + } + + private int getResIdForDisconnectCause(Call.DisconnectCause cause) { + int resId = INVALID_RES_ID; + + if (cause == Call.DisconnectCause.CALL_BARRED) { + resId = R.string.callFailed_cb_enabled; + } else if (cause == Call.DisconnectCause.FDN_BLOCKED) { + resId = R.string.callFailed_fdn_only; + } else if (cause == Call.DisconnectCause.CS_RESTRICTED) { + resId = R.string.callFailed_dsac_restricted; + } else if (cause == Call.DisconnectCause.CS_RESTRICTED_EMERGENCY) { + resId = R.string.callFailed_dsac_restricted_emergency; + } else if (cause == Call.DisconnectCause.CS_RESTRICTED_NORMAL) { + resId = R.string.callFailed_dsac_restricted_normal; + } + + return resId; + } + + private void onDialogDismissed() { + mDialog = null; + InCallPresenter.getInstance().onDismissDialog(); + } } diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index 47fe91194..02ac0bec3 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -119,6 +119,15 @@ public class InCallPresenter implements CallList.Listener { attemptCleanup(); } + private void attemptFinishActivity() { + final boolean doFinish = (mInCallActivity != null && isActivityStarted()); + Log.i(this, "Hide in call UI: " + doFinish); + + if (doFinish) { + mInCallActivity.finish(); + } + } + /** * 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 @@ -128,16 +137,6 @@ public class InCallPresenter implements CallList.Listener { boolean updateListeners = false; if (inCallActivity != null) { - // When the UI comes up, we need to first check the state of the Service. - // If the service is not attached, that means that a call probably connected and - // then immediately disconnected before the UI was able to come up. A disconnected - // service means we dont have calls, so start tearing down the UI instead. - if (mServiceConnected == false) { - inCallActivity.finish(); - attemptCleanup(); - return; - } - if (mInCallActivity == null) { updateListeners = true; Log.i(this, "UI Initialized"); @@ -150,6 +149,24 @@ public class InCallPresenter implements CallList.Listener { } mInCallActivity = inCallActivity; + + // By the time the UI finally comes up, the call may already be disconnected. + // If that's the case, we may need to show an error dialog. + if (mCallList != null && mCallList.getDisconnectedCall() != null) { + maybeShowErrorDialogOnDisconnect(mCallList.getDisconnectedCall()); + } + + // When the UI comes up, we need to first check the in-call state. + // If we are showing NO_CALLS, that means that a call probably connected and + // then immediately disconnected before the UI was able to come up. + // If we dont have any calls, start tearing down the UI instead. + // NOTE: This code relies on {@link #mInCallActivity} being set so we run it after + // it has been set. + if (mInCallState == InCallState.NO_CALLS) { + Log.i(this, "UI Intialized, but no calls left. shut down."); + attemptFinishActivity(); + return; + } } else { Log.i(this, "UI Destroyed)"); updateListeners = true; @@ -224,6 +241,18 @@ public class InCallPresenter implements CallList.Listener { } /** + * Called when a call becomes disconnected. Called everytime an existing call + * changes from being connected (incoming/outgoing/active) to disconnected. + */ + @Override + public void onDisconnect(Call call) { + maybeShowErrorDialogOnDisconnect(call); + + // We need to do the run the same code as onCallListChange. + onCallListChange(CallList.getInstance()); + } + + /** * Given the call list, return the state in which the in-call screen should be. */ public static InCallState getPotentialStateFromCallList(CallList callList) { @@ -359,6 +388,9 @@ public class InCallPresenter implements CallList.Listener { // "Swap calls", or can be a no-op, depending on the current state // of the Phone. + /** + * INCOMING CALL + */ final CallList calls = CallList.getInstance(); final Call incomingCall = calls.getIncomingCall(); Log.v(this, "incomingCall: " + incomingCall); @@ -369,8 +401,10 @@ public class InCallPresenter implements CallList.Listener { return true; } + /** + * ACTIVE CALL + */ final Call activeCall = calls.getActiveCall(); - if (activeCall != null) { // TODO: This logic is repeated from CallButtonPresenter.java. We should // consolidate this logic. @@ -399,8 +433,10 @@ public class InCallPresenter implements CallList.Listener { } } + /** + * BACKGROUND CALL + */ final Call heldCall = calls.getBackgroundCall(); - if (heldCall != null) { // We have a hold call so presumeable it will always support HOLD...but // there is no harm in double checking. @@ -420,6 +456,30 @@ public class InCallPresenter implements CallList.Listener { } /** + * A dialog could have prevented in-call screen from being previously finished. + * This function checks to see if there should be any UI left and if not attempts + * to tear down the UI. + */ + public void onDismissDialog() { + Log.i(this, "Dialog dismissed"); + if (mInCallState == InCallState.NO_CALLS) { + attemptFinishActivity(); + attemptCleanup(); + } + } + + /** + * For some disconnected causes, we show a dialog. This calls into the activity to show + * the dialog if appropriate for the call. + */ + private void maybeShowErrorDialogOnDisconnect(Call call) { + // For newly disconnected calls, we may want to show a dialog on specific error conditions + if (isActivityStarted() && call.getState() == Call.State.DISCONNECTED) { + mInCallActivity.maybeShowErrorDialogOnDisconnect(call.getDisconnectCause()); + } + } + + /** * When the state of in-call changes, this is the first method to get called. It determines if * the UI needs to be started or finished depending on the new state and does it. */ @@ -486,16 +546,16 @@ public class InCallPresenter implements CallList.Listener { showInCall(false); } else if (startStartupSequence) { Log.i(this, "Start Full Screen in call UI"); + + // We're about the bring up the in-call UI for an incoming call. If we still have + // dialogs up, we need to clear them out before showing incoming screen. + if (isActivityStarted()) { + mInCallActivity.dismissPendingDialogs(); + } mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(newState, mCallList); } else if (newState == InCallState.NO_CALLS) { - Log.e(this, "Hide in call UI", new Exception()); - // The new state is the no calls state. Tear everything down. - if (mInCallActivity != null) { - if (isActivityStarted()) { - mInCallActivity.finish(); - } - } + attemptFinishActivity(); } return newState; @@ -506,7 +566,8 @@ public class InCallPresenter implements CallList.Listener { * down. */ private void attemptCleanup() { - boolean shouldCleanup = (mInCallActivity == null && !mServiceConnected); + boolean shouldCleanup = (mInCallActivity == null && !mServiceConnected && + mInCallState == InCallState.NO_CALLS); Log.i(this, "attemptCleanup? " + shouldCleanup); if (shouldCleanup) { |