From 061fc22101930c3c69f454c85fbb85ff2c5c92fb Mon Sep 17 00:00:00 2001 From: Tyler Gunn Date: Wed, 5 Aug 2015 08:20:34 -0700 Subject: Last forwarded number and incoming call subject. Last forwarded number: - Added assets for the "forward" icon for last forwarded number. - Modified InCall Call to store last forwarded number; also calls onLastForwardedNumberChange callback (the last forwarded number is received via a supp service update, so may change after call starts). Call subject (i.e. instant lettering): - Added placeholder subject_bubble asset to form the chat bubble for incoming calls with a subject. - Modified InCall Call to store the call subject (expected to be populated in extras at start of call. - Added code to hide the call status (e.g. "incoming call via XYZ") line and primary call label (e.g. a location "California", or the number type "Mobile" for the number). This was necessary to make room for the call subject bubble, and is in line with the UX mocks. - Change call subject text color to background color of call card (per UX mocks) - Modified call notification to show call subject if it is specified. - Moved code to show HD icon into common method. Bug: 22685114 Change-Id: I22d9dae16658490e3245cfdd9c936bb0584cd6db --- .../res/drawable-hdpi/ic_forward_white_24dp.png | Bin 0 -> 139 bytes .../res/drawable-mdpi/ic_forward_white_24dp.png | Bin 0 -> 117 bytes .../res/drawable-xhdpi/ic_forward_white_24dp.png | Bin 0 -> 159 bytes .../res/drawable-xxhdpi/ic_forward_white_24dp.png | Bin 0 -> 204 bytes .../res/drawable-xxxhdpi/ic_forward_white_24dp.png | Bin 0 -> 236 bytes InCallUI/res/drawable-xxxhdpi/subject_bubble.9.png | Bin 0 -> 611 bytes InCallUI/res/layout/primary_call_info.xml | 30 ++++++++ .../src/com/android/incallui/AnswerPresenter.java | 5 ++ InCallUI/src/com/android/incallui/Call.java | 67 +++++++++++++---- .../src/com/android/incallui/CallCardFragment.java | 62 ++++++++++++++-- .../com/android/incallui/CallCardPresenter.java | 79 ++++++++++++++++++--- InCallUI/src/com/android/incallui/CallList.java | 19 +++++ .../com/android/incallui/StatusBarNotifier.java | 32 ++++++--- 13 files changed, 260 insertions(+), 34 deletions(-) create mode 100644 InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png create mode 100644 InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png create mode 100644 InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png create mode 100644 InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png create mode 100644 InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png create mode 100644 InCallUI/res/drawable-xxxhdpi/subject_bubble.9.png diff --git a/InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png b/InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png new file mode 100644 index 000000000..a0711d377 Binary files /dev/null and b/InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png differ diff --git a/InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png b/InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png new file mode 100644 index 000000000..65f73299f Binary files /dev/null and b/InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png differ diff --git a/InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png b/InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png new file mode 100644 index 000000000..7a5df52bf Binary files /dev/null and b/InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png differ diff --git a/InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png b/InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png new file mode 100644 index 000000000..7bd5b1635 Binary files /dev/null and b/InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png differ diff --git a/InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png b/InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png new file mode 100644 index 000000000..428009cfe Binary files /dev/null and b/InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png differ diff --git a/InCallUI/res/drawable-xxxhdpi/subject_bubble.9.png b/InCallUI/res/drawable-xxxhdpi/subject_bubble.9.png new file mode 100644 index 000000000..2627bf8f7 Binary files /dev/null and b/InCallUI/res/drawable-xxxhdpi/subject_bubble.9.png differ diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml index c680ed6b9..d65b07fe0 100644 --- a/InCallUI/res/layout/primary_call_info.xml +++ b/InCallUI/res/layout/primary_call_info.xml @@ -33,6 +33,27 @@ android:animateLayoutChanges="true" android:gravity="center"> + + + + + + + } } + @Override + public void onLastForwardedNumberChange() { + // no-op + } + private boolean isVideoUpgradePending(Call call) { return call.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST; diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java index 807d43a13..7205b7326 100644 --- a/InCallUI/src/com/android/incallui/Call.java +++ b/InCallUI/src/com/android/incallui/Call.java @@ -18,13 +18,13 @@ package com.android.incallui; import com.android.contacts.common.CallUtil; import com.android.contacts.common.testing.NeededForTesting; -import com.android.incallui.CallList.Listener; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; import android.net.Uri; import android.os.Bundle; import android.os.Trace; +import android.telecom.Connection; import android.telecom.DisconnectCause; import android.telecom.GatewayInfo; import android.telecom.InCallService.VideoCall; @@ -43,13 +43,6 @@ import java.util.Objects; */ @NeededForTesting public class Call { - /** - * Call extras key used to store a child number associated with the current call. - * Used to communicate that the connection was received via a child phone number associated with - * the {@link PhoneAccount}'s primary number. - */ - public static final String EXTRA_CHILD_ADDRESS = "android.telecom.EXTRA_CHILD_ADDRESS"; - /* Defines different states of this call */ public static class State { public static final int INVALID = 0; @@ -264,6 +257,8 @@ public class Call { private InCallVideoCallCallback mVideoCallCallback; private String mChildNumber; + private String mLastForwardedNumber; + private String mCallSubject; /** * Used only to create mock calls for testing @@ -326,10 +321,44 @@ public class Call { } Bundle callExtras = mTelecommCall.getDetails().getExtras(); - if (callExtras != null && callExtras.containsKey(EXTRA_CHILD_ADDRESS)) { - String childNumber = callExtras.getString(EXTRA_CHILD_ADDRESS); - if (!Objects.equals(childNumber, mChildNumber)) { - mChildNumber = childNumber; + if (callExtras != null) { + // Child address arrives when the call is first set up, so we do not need to notify the + // UI of this. + if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) { + String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS); + if (!Objects.equals(childNumber, mChildNumber)) { + mChildNumber = childNumber; + } + } + + // Last forwarded number comes in as an array of strings. We want to choose the last + // item in the array. The forwarding numbers arrive independently of when the call is + // originally set up, so we need to notify the the UI of the change. + if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) { + ArrayList lastForwardedNumbers = + callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER); + + if (lastForwardedNumbers != null) { + String lastForwardedNumber = null; + if (!lastForwardedNumbers.isEmpty()) { + lastForwardedNumber = lastForwardedNumbers.get( + lastForwardedNumbers.size() - 1); + } + + if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) { + mLastForwardedNumber = lastForwardedNumber; + CallList.getInstance().onLastForwardedNumberChange(this); + } + } + } + + // Call subject is present in the extras at the start of call, so we do not need to + // notify any other listeners of this. + if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) { + String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT); + if (!Objects.equals(mCallSubject, callSubject)) { + mCallSubject = callSubject; + } } } } @@ -418,6 +447,20 @@ public class Call { return mChildNumber; } + /** + * @return The last forwarded number for the call, or {@code null} if none specified. + */ + public String getLastForwardedNumber() { + return mLastForwardedNumber; + } + + /** + * @return The call subject, or {@code null} if none specified. + */ + public String getCallSubject() { + return mCallSubject; + } + /** Returns call disconnect cause, defined by {@link DisconnectCause}. */ public DisconnectCause getDisconnectCause() { if (mState == State.DISCONNECTED || mState == State.IDLE) { diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 4121390fd..5f2896040 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -117,10 +117,12 @@ public class CallCardFragment extends BaseFragment private static final String TAG = CallCardPresenter.class.getSimpleName(); private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000; - private final EmergencyCallListener mEmergencyCallListener = ObjectFactory.newEmergencyCallListener(); + private final EmergencyCallListener mEmergencyCallListener = + ObjectFactory.newEmergencyCallListener(); private Call mPrimary; private Call mSecondary; @@ -206,6 +207,7 @@ public class CallCardPresenter extends Presenter Call.areSameNumber(mPrimary, primary)); final boolean secondaryChanged = !(Call.areSame(mSecondary, secondary) && Call.areSameNumber(mSecondary, secondary)); + final boolean shouldShowCallSubject = shouldShowCallSubject(mPrimary); mSecondary = secondary; Call previousPrimary = mPrimary; @@ -215,7 +217,8 @@ public class CallCardPresenter extends Presenter // 1. Primary call changed. // 2. The call's ability to manage conference has changed. if (mPrimary != null && (primaryChanged || - ui.isManageConferenceVisible() != shouldShowManageConference())) { + ui.isManageConferenceVisible() != shouldShowManageConference()) || + ui.isCallSubjectVisible() != shouldShowCallSubject) { // primary call has changed if (previousPrimary != null) { CallList.getInstance().removeCallUpdateListener(previousPrimary.getId(), this); @@ -320,6 +323,19 @@ public class CallCardPresenter extends Presenter updatePrimaryCallState(); } + /** + * Handles a change to the last forwarding number by refreshing the primary call info. + */ + @Override + public void onLastForwardedNumberChange() { + Log.v(this, "onLastForwardedNumberChange"); + + if (mPrimary == null) { + return; + } + updatePrimaryDisplayInfo(); + } + private String getSubscriptionNumber() { // If it's an emergency call, and they're not populating the callback number, // then try to fall back to the phone sub info (to hopefully get the SIM's @@ -348,14 +364,22 @@ public class CallCardPresenter extends Presenter mPrimary.hasProperty(Details.PROPERTY_WIFI), mPrimary.isConferenceCall()); - boolean showHdAudioIndicator = - isPrimaryCallActive() && mPrimary.hasProperty(Details.PROPERTY_HIGH_DEF_AUDIO); - getUi().showHdAudioIndicator(showHdAudioIndicator); - + maybeShowHdAudioIcon(); setCallbackNumber(); } } + /** + * Show the HD icon if the call is active and has {@link Details#PROPERTY_HIGH_DEF_AUDIO}, + * except if the call has a last forwarded number (we will show that icon instead). + */ + private void maybeShowHdAudioIcon() { + boolean showHdAudioIndicator = + isPrimaryCallActive() && mPrimary.hasProperty(Details.PROPERTY_HIGH_DEF_AUDIO) && + TextUtils.isEmpty(mPrimary.getLastForwardedNumber()); + getUi().showHdAudioIndicator(showHdAudioIndicator); + } + /** * Only show the conference call button if we can manage the conference. */ @@ -584,20 +608,36 @@ public class CallCardPresenter extends Presenter String name = getNameForCall(mPrimaryContactInfo); String number; - // If a child number is present, use it instead of the 2nd line. boolean isChildNumberShown = !TextUtils.isEmpty(mPrimary.getChildNumber()); - if (isChildNumberShown) { + boolean isForwardedNumberShown = !TextUtils.isEmpty(mPrimary.getLastForwardedNumber()); + boolean isCallSubjectShown = shouldShowCallSubject(mPrimary); + + if (isCallSubjectShown) { + ui.setCallSubject(mPrimary.getCallSubject()); + } else { + ui.setCallSubject(null); + } + + if (isCallSubjectShown) { + number = null; + } else if (isChildNumberShown) { number = mContext.getString(R.string.child_number, mPrimary.getChildNumber()); + } else if (isForwardedNumberShown) { + // Use last forwarded number instead of second line, if present. + number = mPrimary.getLastForwardedNumber(); } else { number = getNumberForCall(mPrimaryContactInfo); } + ui.showForwardIndicator(isForwardedNumberShown); + maybeShowHdAudioIcon(); + boolean nameIsNumber = name != null && name.equals(mPrimaryContactInfo.number); ui.setPrimary( number, name, nameIsNumber, - isChildNumberShown ? null : mPrimaryContactInfo.label, + isChildNumberShown || isCallSubjectShown ? null : mPrimaryContactInfo.label, mPrimaryContactInfo.photo, mPrimaryContactInfo.isSipCall); } else { @@ -857,6 +897,24 @@ public class CallCardPresenter extends Presenter } } + /** + * Determines whether the call subject should be visible on the UI. For the call subject to be + * visible, the call has to be in an incoming or waiting state, and the subject must not be + * empty. + * + * @param call The call. + * @return {@code true} if the subject should be shown, {@code false} otherwise. + */ + private boolean shouldShowCallSubject(Call call) { + if (call == null) { + return false; + } + + boolean isIncomingOrWaiting = mPrimary.getState() == Call.State.INCOMING || + mPrimary.getState() == Call.State.CALL_WAITING; + return isIncomingOrWaiting && !TextUtils.isEmpty(call.getCallSubject()); + } + public interface CallCardUi extends Ui { void setVisible(boolean on); void setCallCardVisible(boolean visible); @@ -875,10 +933,13 @@ public class CallCardPresenter extends Presenter void setPrimaryLabel(String label); void setEndCallButtonEnabled(boolean enabled, boolean animate); void setCallbackNumber(String number, boolean isEmergencyCalls); + void setCallSubject(String callSubject); void setProgressSpinnerVisible(boolean visible); void showHdAudioIndicator(boolean visible); + void showForwardIndicator(boolean visible); void showManageConferenceCallButton(boolean visible); boolean isManageConferenceVisible(); + boolean isCallSubjectVisible(); void animateForNewOutgoingCall(); void sendAccessibilityAnnouncement(); } diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index c0014bdfe..fbcc1cc61 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -160,6 +160,20 @@ public class CallList { } } + /** + * Called when a single call has changed session modification state. + * + * @param call The call. + */ + public void onLastForwardedNumberChange(Call call) { + final List listeners = mCallUpdateListenerMap.get(call.getId()); + if (listeners != null) { + for (CallUpdateListener listener : listeners) { + listener.onLastForwardedNumberChange(); + } + } + } + public void notifyCallUpdateListeners(Call call) { final List listeners = mCallUpdateListenerMap.get(call.getId()); if (listeners != null) { @@ -611,5 +625,10 @@ public class CallList { * @param sessionModificationState The new session modification state. */ public void onSessionModificationStateChange(int sessionModificationState); + + /** + * Notifies of a change to the last forwarded number for a call. + */ + public void onLastForwardedNumberChange(); } } diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java index 8df4520bf..e583434da 100644 --- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java +++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java @@ -40,6 +40,8 @@ import com.android.incallui.InCallPresenter.InCallState; import com.google.common.base.Preconditions; +import java.util.Objects; + /** * This class adds Notifications to the status bar for the in-call experience. */ @@ -60,7 +62,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, private int mCurrentNotification = NOTIFICATION_NONE; private int mCallState = Call.State.INVALID; private int mSavedIcon = 0; - private int mSavedContent = 0; + private String mSavedContent = null; private Bitmap mSavedLargeIcon; private String mSavedContentTitle; private String mCallId = null; @@ -206,7 +208,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, // Check if data has changed; if nothing is different, don't issue another notification. final int iconResId = getIconToDisplay(call); Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call); - final int contentResId = getContentString(call); + final String content = getContentString(call); final String contentTitle = getContentTitle(contactInfo, call); final int notificationType; @@ -218,7 +220,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, notificationType = NOTIFICATION_IN_CALL; } - if (!checkForChangeAndSaveData(iconResId, contentResId, largeIcon, contentTitle, state, + if (!checkForChangeAndSaveData(iconResId, content, largeIcon, contentTitle, state, notificationType)) { return; } @@ -244,7 +246,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, } // Set the content - builder.setContentText(mContext.getString(contentResId)); + builder.setContentText(content); builder.setSmallIcon(iconResId); builder.setContentTitle(contentTitle); builder.setLargeIcon(largeIcon); @@ -306,7 +308,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, * are already displaying. If the data is exactly the same, we return false so that * we do not issue a new notification for the exact same data. */ - private boolean checkForChangeAndSaveData(int icon, int content, Bitmap largeIcon, + private boolean checkForChangeAndSaveData(int icon, String content, Bitmap largeIcon, String contentTitle, int state, int notificationType) { // The two are different: @@ -317,7 +319,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, (contentTitle == null && mSavedContentTitle != null); // any change means we are definitely updating - boolean retval = (mSavedIcon != icon) || (mSavedContent != content) || + boolean retval = (mSavedIcon != icon) || !Objects.equals(mSavedContent, content) || (mCallState != state) || (mSavedLargeIcon != largeIcon) || contentTitleChanged; @@ -419,13 +421,20 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, /** * Returns the message to use with the notification. */ - private int getContentString(Call call) { + private String getContentString(Call call) { + boolean isIncomingOrWaiting = call.getState() == Call.State.INCOMING || + call.getState() == Call.State.CALL_WAITING; + + if (isIncomingOrWaiting && !TextUtils.isEmpty(call.getCallSubject())) { + return call.getCallSubject(); + } + int resId = R.string.notification_ongoing_call; if (call.hasProperty(Details.PROPERTY_WIFI)) { resId = R.string.notification_ongoing_call_wifi; } - if (call.getState() == Call.State.INCOMING || call.getState() == Call.State.CALL_WAITING) { + if (isIncomingOrWaiting) { if (call.hasProperty(Details.PROPERTY_WIFI)) { resId = R.string.notification_incoming_call_wifi; } else { @@ -440,7 +449,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, resId = R.string.notification_requesting_video_call; } - return resId; + return mContext.getString(resId); } /** @@ -637,4 +646,9 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener, updateNotification(mInCallState, CallList.getInstance()); } } + + @Override + public void onLastForwardedNumberChange() { + // no-op + } } -- cgit v1.2.3