From 82f5d003e675dfd56735c745744c3d968883b23c Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Wed, 7 Aug 2013 12:12:58 -0700 Subject: Add UI to show when the Call disconnects. When the call disconnects, we need to show UI for a short time before existing the UI. Change-Id: Iee648b8c54ee4b5ea09cfaec508e1bf8fb8f1643 --- InCallUI/res/values/strings.xml | 49 +++++++++ .../src/com/android/incallui/AnswerFragment.java | 1 + .../com/android/incallui/CallButtonFragment.java | 1 + .../src/com/android/incallui/CallCardFragment.java | 111 ++++++++++++++++++++- .../com/android/incallui/CallCardPresenter.java | 59 +++++++++-- .../com/android/incallui/CallHandlerService.java | 6 +- InCallUI/src/com/android/incallui/CallList.java | 68 ++++++++++++- .../src/com/android/incallui/InCallPresenter.java | 3 +- InCallUI/src/com/android/incallui/Logger.java | 16 ++- 9 files changed, 294 insertions(+), 20 deletions(-) (limited to 'InCallUI') diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml index 2ecc84270..882102a7e 100755 --- a/InCallUI/res/values/strings.xml +++ b/InCallUI/res/values/strings.xml @@ -32,6 +32,55 @@ Pay phone + + Line busy + + Network busy + + No response, timed out + + Server unreachable + + Number unreachable + + Incorrect username or password + + Called from out-of-network + + Server error. Try again later. + + No signal + + ACM limit exceeded + + Radio off + + No SIM or SIM error + + Out of service area + + Outgoing calls are restricted by FDN. + + 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. + + Invalid number + + Conference call + + Call has been lost. + + + OK + + + + Speaker diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java index 79247c56c..d1a1df0a6 100644 --- a/InCallUI/src/com/android/incallui/AnswerFragment.java +++ b/InCallUI/src/com/android/incallui/AnswerFragment.java @@ -70,6 +70,7 @@ public class AnswerFragment extends BaseFragment implements @Override public void onDestroyView() { + super.onDestroyView(); getPresenter().onUiUnready(this); } diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java index 175fe4d95..1b9465de7 100644 --- a/InCallUI/src/com/android/incallui/CallButtonFragment.java +++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java @@ -119,6 +119,7 @@ public class CallButtonFragment extends BaseFragment @Override public void onDestroyView() { + super.onDestroyView(); getPresenter().onUiUnready(this); } diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index d4782c5fe..19c32f4fd 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -22,6 +22,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextUtils; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -29,6 +30,8 @@ import android.view.ViewStub; import android.widget.ImageView; import android.widget.TextView; +import com.android.services.telephony.common.Call; + /** * Fragment for call card. */ @@ -39,8 +42,7 @@ public class CallCardFragment extends BaseFragment private TextView mNumberLabel; private TextView mName; private ImageView mPhoto; - - + private TextView mCallStateLabel; private ViewStub mSecondaryCallInfo; private TextView mSecondaryCallName; @@ -63,6 +65,7 @@ public class CallCardFragment extends BaseFragment mNumberLabel = (TextView) view.findViewById(R.id.label); mSecondaryCallInfo = (ViewStub) view.findViewById(R.id.secondary_call_info); mPhoto = (ImageView) view.findViewById(R.id.photo); + mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel); // This method call will begin the callbacks on CallCardUi. We need to ensure // everything needed for the callbacks is set up before this is called. @@ -71,6 +74,7 @@ public class CallCardFragment extends BaseFragment @Override public void onDestroyView() { + super.onDestroyView(); getPresenter().onUiUnready(this); } @@ -139,6 +143,109 @@ public class CallCardFragment extends BaseFragment } } + @Override + public void setCallState(int state, Call.DisconnectCause cause) { + String callStateLabel = null; + + // States other than disconnected not yet supported + if (state == Call.State.DISCONNECTED) { + callStateLabel = getCallFailedString(cause); + } + + Logger.d(this, "setCallState ", callStateLabel); + + if (!TextUtils.isEmpty(callStateLabel)) { + mCallStateLabel.setVisibility(View.VISIBLE); + mCallStateLabel.setText(callStateLabel); + } else { + mCallStateLabel.setVisibility(View.GONE); + // Gravity is aligned left when receiving an incoming call in landscape. + // In that rare case, the gravity needs to be reset to the right. + // Also, setText("") is used since there is a delay in making the view GONE, + // so the user will otherwise see the text jump to the right side before disappearing. + if(mCallStateLabel.getGravity() != Gravity.END) { + mCallStateLabel.setText(""); + mCallStateLabel.setGravity(Gravity.END); + } + } + } + + /** + * Maps the disconnect cause to a resource string. + */ + private String getCallFailedString(Call.DisconnectCause cause) { + int resID = R.string.card_title_call_ended; + + // TODO: The card *title* should probably be "Call ended" in all + // cases, but if the DisconnectCause was an error condition we should + // probably also display the specific failure reason somewhere... + + switch (cause) { + case BUSY: + resID = R.string.callFailed_userBusy; + break; + + case CONGESTION: + resID = R.string.callFailed_congestion; + break; + + case TIMED_OUT: + resID = R.string.callFailed_timedOut; + break; + + case SERVER_UNREACHABLE: + resID = R.string.callFailed_server_unreachable; + break; + + case NUMBER_UNREACHABLE: + resID = R.string.callFailed_number_unreachable; + break; + + case INVALID_CREDENTIALS: + resID = R.string.callFailed_invalid_credentials; + break; + + case SERVER_ERROR: + resID = R.string.callFailed_server_error; + break; + + case OUT_OF_NETWORK: + resID = R.string.callFailed_out_of_network; + break; + + case LOST_SIGNAL: + case CDMA_DROP: + resID = R.string.callFailed_noSignal; + break; + + case LIMIT_EXCEEDED: + resID = R.string.callFailed_limitExceeded; + break; + + case POWER_OFF: + resID = R.string.callFailed_powerOff; + break; + + case ICC_ERROR: + resID = R.string.callFailed_simError; + break; + + case OUT_OF_SERVICE: + resID = R.string.callFailed_outOfService; + break; + + case INVALID_NUMBER: + case UNOBTAINABLE_NUMBER: + resID = R.string.callFailed_unobtainable_number; + break; + + default: + resID = R.string.card_title_call_ended; + break; + } + return this.getView().getContext().getString(resID); + } + private void showAndInitializeSecondaryCallInfo() { mSecondaryCallInfo.setVisibility(View.VISIBLE); diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index 5f67a51e2..d97c22e57 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -75,17 +75,12 @@ public class CallCardPresenter extends Presenter i } else if (state == InCallState.OUTGOING) { primary = callList.getOutgoingCall(); - // Safe to assume that the primary call is valid since we would not be in the - // OUTGOING state without an outgoing call. - secondary = callList.getBackgroundCall(); + // getCallToDisplay doesn't go through outgoing or incoming calls. It will return the + // highest priority call to display as the secondary call. + secondary = getCallToDisplay(callList, null); } else if (state == InCallState.INCALL) { - primary = callList.getActiveCall(); - if (primary != null) { - secondary = callList.getBackgroundCall(); - } else { - primary = callList.getBackgroundCall(); - secondary = callList.getSecondBackgroundCall(); - } + primary = getCallToDisplay(callList, null); + secondary = getCallToDisplay(callList, primary); } Logger.d(this, "Primary call: " + primary); @@ -96,6 +91,14 @@ public class CallCardPresenter extends Presenter i null, this); updateDisplayByCallerInfo(primary, primaryCallInfo, primary.getNumberPresentation(), true); + if (primary != null) { + ui.setNumber(primary.getNumber()); + ui.setCallState(primary.getState(), primary.getDisconnectCause()); + } else { + ui.setNumber(""); + ui.setCallState(Call.State.INVALID, Call.DisconnectCause.UNKNOWN); + } + // Set secondary call data if (secondary != null) { ui.setSecondaryCallInfo(true, secondary.getNumber()); @@ -104,6 +107,41 @@ public class CallCardPresenter extends Presenter i } } + /** + * Get the highest priority call to display. + * Goes through the calls and chooses which to return based on priority of which type of call + * to display to the user. Callers can use the "ignore" feature to get the second best call + * by passing a previously found primary call as ignore. + * + * @param ignore A call to ignore if found. + */ + private Call getCallToDisplay(CallList callList, Call ignore) { + + // Disconnected calls get primary position to let user know quickly + // what call has disconnected. Disconnected calls are very short lived. + Call retval = callList.getDisconnectedCall(); + if (retval != null && retval != ignore) { + return retval; + } + + // Active calls come second. An active call always gets precedent. + retval = callList.getActiveCall(); + if (retval != null && retval != ignore) { + return retval; + } + + // Then we go to background call (calls on hold) + retval = callList.getBackgroundCall(); + if (retval != null && retval != ignore) { + return retval; + } + + // Lastly, we go to a second background call. + retval = callList.getSecondBackgroundCall(); + + return retval; + } + public interface CallCardUi extends Ui { // TODO(klp): Consider passing in the Call object directly in these methods. void setVisible(boolean on); @@ -115,6 +153,7 @@ public class CallCardPresenter extends Presenter i void setImage(Drawable drawable); void setImage(Bitmap bitmap); void setSecondaryCallInfo(boolean show, String number); + void setCallState(int state, Call.DisconnectCause cause); } @Override diff --git a/InCallUI/src/com/android/incallui/CallHandlerService.java b/InCallUI/src/com/android/incallui/CallHandlerService.java index f0d98b2ed..0b2be5663 100644 --- a/InCallUI/src/com/android/incallui/CallHandlerService.java +++ b/InCallUI/src/com/android/incallui/CallHandlerService.java @@ -42,6 +42,7 @@ public class CallHandlerService extends Service { private static final int ON_UPDATE_CALL_WITH_TEXT_RESPONSES = 3; private static final int ON_AUDIO_MODE = 4; private static final int ON_SUPPORTED_AUDIO_MODE = 5; + private static final int ON_DISCONNECT_CALL = 6; private CallList mCallList; @@ -79,7 +80,7 @@ public class CallHandlerService extends Service { @Override public void onDisconnect(Call call) { Logger.d(CallHandlerService.this, "onDisconnected"); - mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_UPDATE_CALL, 0, 0, call)); + mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_DISCONNECT_CALL, 0, 0, call)); } @Override @@ -149,6 +150,9 @@ public class CallHandlerService extends Service { case ON_UPDATE_CALL_WITH_TEXT_RESPONSES: mCallList.onUpdate((AbstractMap.SimpleEntry >) msg.obj); break; + case ON_DISCONNECT_CALL: + mCallList.onDisconnect((Call) msg.obj); + break; case ON_AUDIO_MODE: mAudioModeProvider.onAudioModeChange(msg.arg1); break; diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index 5ea4fc559..bd631ce9f 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -23,6 +23,9 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import android.os.Handler; +import android.os.Message; + import com.android.services.telephony.common.Call; import java.util.AbstractMap; @@ -39,6 +42,10 @@ import java.util.Set; */ public class CallList { + private static final int DISCONNECTED_CALL_TIMEOUT_MS = 3000; + + private static final int EVENT_DISCONNECTED_TIMEOUT = 1; + private static CallList sInstance; private final HashMap mCallMap = Maps.newHashMap(); @@ -66,7 +73,17 @@ public class CallList { * Called when a single call has changed. */ public void onUpdate(Call call) { - Logger.d(this, "onUpdate - " + call); + Logger.d(this, "onUpdate - ", call); + + updateCallInMap(call); + notifyListenersOfChange(); + } + + /** + * Called when a single call disconnects. + */ + public void onDisconnect(Call call) { + Logger.d(this, "onDisconnect: ", call); updateCallInMap(call); @@ -141,6 +158,10 @@ public class CallList { return getFirstCallWithState(Call.State.ONHOLD); } + public Call getDisconnectedCall() { + return getFirstCallWithState(Call.State.DISCONNECTED); + } + public Call getSecondBackgroundCall() { return getCallWithState(Call.State.ONHOLD, 1); } @@ -200,7 +221,7 @@ public class CallList { } } - Logger.d(this, "Found call: " + retval); + Logger.v(this, "Found call: ", retval); return retval; } @@ -219,7 +240,21 @@ public class CallList { final Integer id = new Integer(call.getCallId()); - if (!isCallDead(call)) { + if (call.getState() == Call.State.DISCONNECTED) { + // For disconnected calls, we want to keep them alive for a few seconds so that the UI + // has a chance to display anything it needs when a call is disconnected. + + // Set up a timer to destroy the call after X seconds. + Message msg = mHandler.obtainMessage(EVENT_DISCONNECTED_TIMEOUT, call); + boolean sent = mHandler.sendMessageDelayed(msg, DISCONNECTED_CALL_TIMEOUT_MS); + + Logger.d(this, "Retval from sendMessageDelayed: ", Boolean.toString(sent)); + + // Don't add disconnected calls that do not already exist in the map + if (mCallMap.containsKey(id)) { + mCallMap.put(id, call); + } + } else if (!isCallDead(call)) { mCallMap.put(id, call); } else if (mCallMap.containsKey(id)) { mCallMap.remove(id); @@ -245,6 +280,33 @@ public class CallList { return Call.State.IDLE == state || Call.State.INVALID == state; } + /** + * Sets up a call for deletion and notifies listeners of change. + */ + private void finishDisconnectedCall(Call call) { + call.setState(Call.State.IDLE); + updateCallInMap(call); + notifyListenersOfChange(); + } + + /** + * Handles the timeout for destroying disconnected calls. + */ + private Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_DISCONNECTED_TIMEOUT: + Logger.d(this, "EVENT_DISCONNECTED_TIMEOUT ", msg.obj); + finishDisconnectedCall((Call) msg.obj); + break; + default: + Logger.wtf(this, "Message not expected: " + msg.what); + break; + } + } + }; + /** * Listener interface for any class that wants to be notified of changes * to the call list. diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index 72a880a70..63f0aa524 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -105,7 +105,8 @@ public class InCallPresenter implements CallList.Listener { } else if (callList.getOutgoingCall() != null) { newState = InCallState.OUTGOING; } else if (callList.getActiveCall() != null || - callList.getBackgroundCall() != null) { + callList.getBackgroundCall() != null || + callList.getDisconnectedCall() != null) { newState = InCallState.INCALL; } diff --git a/InCallUI/src/com/android/incallui/Logger.java b/InCallUI/src/com/android/incallui/Logger.java index 5f628d296..e7cbe2025 100644 --- a/InCallUI/src/com/android/incallui/Logger.java +++ b/InCallUI/src/com/android/incallui/Logger.java @@ -41,9 +41,9 @@ import android.util.Log; } } - public static void v(String tag, String msg) { - if (VERBOSE) { - Log.v(TAG, tag + msg); + public static void d(Object obj, String str1, Object str2) { + if (DEBUG) { + Log.d(TAG, getPrefix(obj) + str1 + str2); } } @@ -61,6 +61,12 @@ import android.util.Log; Log.e(TAG, tag + msg); } + public static void v(Object obj, String str1, Object str2) { + if (VERBOSE) { + Log.d(TAG, getPrefix(obj) + str1 + str2); + } + } + public static void e(Object obj, String msg, Exception e) { Log.e(TAG, getPrefix(obj) + msg, e); } @@ -77,6 +83,10 @@ import android.util.Log; Log.i(TAG, getPrefix(obj) + msg); } + public static void wtf(Object obj, String msg) { + Log.wtf(TAG, getPrefix(obj) + msg); + } + private static String getPrefix(Object obj) { return (obj == null ? "" : (obj.getClass().getSimpleName() + " - ")); } -- cgit v1.2.3