summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorSantos Cordon <santoscordon@google.com>2013-09-20 17:06:18 -0700
committerSantos Cordon <santoscordon@google.com>2013-09-23 19:33:43 -0700
commit9ccf74a1e34b0a08b8d74cbb5a494eea879fd15e (patch)
tree22ecd3d50053702e84483a8f45234007e858ec2b /InCallUI
parentcd72227f331b862d70c1271b1c9a5f3a1ab30bee (diff)
Add generic error code dialogs for failed calls.
This change listens for disconnected calls and if the disconnection cause matches a set of predefined causes, we show a dialog to the user. New dialog messages: - You can\'t make outgoing calls while call barring is on. - All calls are restricted by access control. - Emergency calls are restricted by access control. - Normal calls are restricted by access control. - Outgoing calls are restricted by FDN. Several changes were necessary to support this: 1) Dialog code (InCallActivity.java) 2) Notifications for a new disconnect (CallList.java) - previously we sent a large update for ondisconnect, now we just send the notice for that particular call. InCallPresenter was written so that the same thing that used to happen still happens. 3) Check if we need to show a disconnect dialog when the UI comes up. - I found that FDN disconnection happens so quickly that the disconnect came before the UI was up, so this was necessary. (InCallPresenter.setActivity()) 4) CallCard should be initialized with disconnecting/disconnecting calls. - Call Card presenter intializes itself when it starts up using live calls, but as with the FDN case above, a disconnected call should be used if no live calls exist. (CallList.getFirstCall()) 5) If a dialog is up while an incoming call comes up, dismiss it automatically so that users can see incoming UI. - (InCallPresenter.startOrFinishUi()) 6) Keep the UI up as long as there is a dialog for the incoming call. - Previously, UI would come down once all calls were shut down but in the case of present dialogs, we want it to stay up. (InCallActivity.finish()) - When the dialog is dismissed, check if we want to tear down UI. (InCallActivity.onDialogDismissed()) 7) Only tear down UI once we are in the NO_CALL state. - We used to tear down when the activity was null (closed) and the service was disconnected. This is problematic because a call that immediately disconnects (like FDN errors) would shut down the service before the UI had a chance to come up. We added a check to the conditional for the state NO_CALL before shutting down. (InCallPresenter.attemptCleanup()). bug: 10680210 Change-Id: I1fd33dd53dc0eec5a02b7a940cfd018cec3d20c0
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/src/com/android/incallui/AnswerPresenter.java5
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java59
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java83
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java101
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) {