am b4ce875c: (-s ours) am 74573496: Merge "Fix time and details alignment in RTL." into mnc-dev
* commit 'b4ce875c72d7ffe1a484a4b48c6c620df8ae2428':
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 0000000..a0711d3
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/ic_forward_white_24dp.png
Binary files 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 0000000..65f7329
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/ic_forward_white_24dp.png
Binary files 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 0000000..7a5df52
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/ic_forward_white_24dp.png
Binary files 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 0000000..7bd5b16
--- /dev/null
+++ b/InCallUI/res/drawable-xxhdpi/ic_forward_white_24dp.png
Binary files 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 0000000..428009c
--- /dev/null
+++ b/InCallUI/res/drawable-xxxhdpi/ic_forward_white_24dp.png
Binary files differ
diff --git a/InCallUI/res/drawable/subject_bubble.xml b/InCallUI/res/drawable/subject_bubble.xml
new file mode 100644
index 0000000..adab678
--- /dev/null
+++ b/InCallUI/res/drawable/subject_bubble.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ffffff" />
+ <padding android:left="10dp" android:top="10dp" android:right="10dp" android:bottom="10dp" />
+ <corners android:topLeftRadius="6dp" android:topRightRadius="6dp"
+ android:bottomLeftRadius="0dp" android:bottomRightRadius="6dp"/>
+</shape>
\ No newline at end of file
diff --git a/InCallUI/res/layout/primary_call_info.xml b/InCallUI/res/layout/primary_call_info.xml
index 89943df..8f78440 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">
+ <LinearLayout android:id="@+id/callSubjectLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <TextView android:id="@+id/callSubject"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/incall_call_banner_background_color"
+ android:textSize="@dimen/call_label_text_size"
+ android:background="@drawable/subject_bubble"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:singleLine="false"
+ android:visibility="gone" />
+ </LinearLayout>
+
<LinearLayout android:id="@+id/callStateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -108,6 +129,15 @@
android:scaleType="fitCenter"
android:visibility="gone" />
+ <ImageView android:id="@+id/forwardIcon"
+ android:src="@drawable/ic_forward_white_24dp"
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="8dp"
+ android:tint="@color/incall_call_banner_subtext_color"
+ android:scaleType="fitCenter"
+ android:visibility="gone" />
+
<!-- Label (like "Mobile" or "Work", if present) and phone number, side by side -->
<LinearLayout android:id="@+id/labelAndNumber"
android:layout_width="wrap_content"
diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml
index cadef73..e59867c 100644
--- a/InCallUI/res/values/strings.xml
+++ b/InCallUI/res/values/strings.xml
@@ -456,4 +456,12 @@
<!-- Description of the "camera off" icon displayed when the device's camera is disabled during
a video call. [CHAR LIMIT=NONE] -->
<string name="camera_off_description">Camera off</string>
+
+ <!-- Used to inform the user that a call was received via a number other than the primary
+ phone number associated with their device. [CHAR LIMIT=16] -->
+ <string name="child_number">via <xliff:g id="child_number" example="650-555-1212">%s</xliff:g></string>
+
+ <!-- Used to inform the user that the note associated with an outgoing call has been sent.
+ [CHAR LIMIT=32] -->
+ <string name="note_sent">Note sent</string>
</resources>
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index a5a88ff..fc75bf0 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -108,6 +108,11 @@
}
}
+ @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 ee73db2..f08653e 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -18,23 +18,26 @@
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;
+import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.VideoProfile;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
/**
* Describes a single call and its state.
@@ -242,6 +245,8 @@
};
private android.telecom.Call mTelecommCall;
+ private boolean mIsEmergencyCall;
+ private Uri mHandle;
private final String mId;
private int mState = State.INVALID;
private DisconnectCause mDisconnectCause;
@@ -254,6 +259,9 @@
private int mModifyToVideoState = VideoProfile.STATE_AUDIO_ONLY;
private InCallVideoCallCallback mVideoCallCallback;
+ private String mChildNumber;
+ private String mLastForwardedNumber;
+ private String mCallSubject;
/**
* Used only to create mock calls for testing
@@ -268,6 +276,7 @@
public Call(android.telecom.Call telecommCall) {
mTelecommCall = telecommCall;
mId = ID_PREFIX + Integer.toString(sIdCounter++);
+
updateFromTelecommCall();
mTelecommCall.registerCallback(mTelecomCallCallback);
}
@@ -314,6 +323,56 @@
CallList.getInstance().getCallByTelecommCall(
mTelecommCall.getChildren().get(i)).getId());
}
+
+ Bundle callExtras = mTelecommCall.getDetails().getExtras();
+ 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<String> 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;
+ }
+ }
+ }
+
+ // If the handle of the call has changed, update state for the call determining if it is an
+ // emergency call.
+ Uri newHandle = mTelecommCall.getDetails().getHandle();
+ if (!Objects.equals(mHandle, newHandle)) {
+ mHandle = newHandle;
+ updateEmergencyCallState();
+ }
}
private static int translateState(int state) {
@@ -359,6 +418,10 @@
return mTelecommCall == null ? null : mTelecommCall.getDetails().getHandle();
}
+ public boolean isEmergencyCall() {
+ return mIsEmergencyCall;
+ }
+
public int getState() {
if (mTelecommCall != null && mTelecommCall.getParent() != null) {
return State.CONFERENCED;
@@ -393,6 +456,27 @@
return mTelecommCall == null ? null : mTelecommCall.getDetails().getExtras();
}
+ /**
+ * @return The child number for the call, or {@code null} if none specified.
+ */
+ public String getChildNumber() {
+ 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) {
@@ -506,7 +590,7 @@
public void setSessionModificationState(int state) {
if (state == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
Log.e(this,
- "setSessionModificationState not to be called for RECEIVED_UPGRADE_TO_VIDEO_REQUEST");
+ "setSessionModificationState not valid for RECEIVED_UPGRADE_TO_VIDEO_REQUEST");
return;
}
@@ -519,6 +603,16 @@
}
}
+ /**
+ * Determines if the call handle is an emergency number or not and caches the result to avoid
+ * repeated calls to isEmergencyNumber.
+ */
+ private void updateEmergencyCallState() {
+ Uri handle = mTelecommCall.getDetails().getHandle();
+ mIsEmergencyCall = PhoneNumberUtils.isEmergencyNumber(
+ handle == null ? "" : handle.getSchemeSpecificPart());
+ }
+
private void setModifyToVideoState(int newVideoState) {
mModifyToVideoState = newVideoState;
}
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 4121390..dc92ac3 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -47,6 +47,7 @@
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
+import android.widget.Toast;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
import com.android.contacts.common.widget.FloatingActionButtonController;
@@ -117,10 +118,12 @@
private TextView mCallStateLabel;
private TextView mCallTypeLabel;
private ImageView mHdAudioIcon;
+ private ImageView mForwardIcon;
private View mCallNumberAndLabel;
private ImageView mPhoto;
private TextView mElapsedTime;
private Drawable mPrimaryPhotoDrawable;
+ private TextView mCallSubject;
// Container view that houses the entire primary call card, including the call buttons
private View mPrimaryCallCardContainer;
@@ -231,6 +234,7 @@
mCallStateVideoCallIcon = (ImageView) view.findViewById(R.id.videoCallIcon);
mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);
mHdAudioIcon = (ImageView) view.findViewById(R.id.hdAudioIcon);
+ mForwardIcon = (ImageView) view.findViewById(R.id.forwardIcon);
mCallNumberAndLabel = view.findViewById(R.id.labelAndNumber);
mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel);
mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime);
@@ -281,6 +285,7 @@
mPrimaryName.setElegantTextHeight(false);
mCallStateLabel.setElegantTextHeight(false);
+ mCallSubject = (TextView) view.findViewById(R.id.callSubject);
}
@Override
@@ -339,7 +344,7 @@
float videoViewTranslation = 0f;
// Translate the call card to its pre-animation state.
- if (!mIsLandscape){
+ if (!mIsLandscape) {
mPrimaryCallCardContainer.setTranslationY(visible ?
-mPrimaryCallCardContainer.getHeight() : 0);
@@ -553,7 +558,12 @@
Log.v(this, "DisconnectCause " + disconnectCause.toString());
Log.v(this, "gateway " + connectionLabel + gatewayNumber);
- if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText())) {
+ // Check if the call subject is showing -- if it is, we want to bypass showing the call
+ // state.
+ boolean isSubjectShowing = mCallSubject.getVisibility() == View.VISIBLE;
+
+ if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText()) &&
+ !isSubjectShowing) {
// Nothing to do if the labels are the same
if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
mCallStateLabel.clearAnimation();
@@ -562,8 +572,14 @@
return;
}
- // Update the call state label and icon.
- setCallStateLabel(callStateLabel);
+ if (isSubjectShowing) {
+ changeCallStateLabel(null);
+ callStateIcon = null;
+ } else {
+ // Update the call state label and icon.
+ setCallStateLabel(callStateLabel);
+ }
+
if (!TextUtils.isEmpty(callStateLabel.getCallStateLabel())) {
if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
mCallStateLabel.clearAnimation();
@@ -677,6 +693,23 @@
mInCallMessageLabel.setVisibility(View.VISIBLE);
}
+ /**
+ * Sets and shows the call subject if it is not empty. Hides the call subject otherwise.
+ *
+ * @param callSubject The call subject.
+ */
+ @Override
+ public void setCallSubject(String callSubject) {
+ boolean showSubject = !TextUtils.isEmpty(callSubject);
+
+ mCallSubject.setVisibility(showSubject ? View.VISIBLE : View.GONE);
+ if (showSubject) {
+ mCallSubject.setText(callSubject);
+ } else {
+ mCallSubject.setText(null);
+ }
+ }
+
public boolean isAnimating() {
return mIsAnimating;
}
@@ -922,6 +955,17 @@
}
/**
+ * Changes the visibility of the forward icon.
+ *
+ * @param visible {@code true} if the UI should show the forward icon.
+ */
+ @Override
+ public void showForwardIndicator(boolean visible) {
+ mForwardIcon.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+
+ /**
* Changes the visibility of the "manage conference call" button.
*
* @param visible Whether to set the button to be visible or not.
@@ -942,6 +986,16 @@
}
/**
+ * Determines the current visibility of the call subject.
+ *
+ * @return {@code true} if the subject is visible.
+ */
+ @Override
+ public boolean isCallSubjectVisible() {
+ return mCallSubject.getVisibility() == View.VISIBLE;
+ }
+
+ /**
* Get the overall InCallUI background colors and apply to call card.
*/
public void updateColors() {
@@ -959,6 +1013,7 @@
mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor);
}
mCallButtonsContainer.setBackgroundColor(themeColors.mPrimaryColor);
+ mCallSubject.setTextColor(themeColors.mPrimaryColor);
mCurrentThemeColors = themeColors;
}
@@ -1034,6 +1089,11 @@
});
}
+ @Override
+ public void showNoteSentToast() {
+ Toast.makeText(getContext(), R.string.note_sent, Toast.LENGTH_LONG).show();
+ }
+
public void onDialpadVisibilityChange(boolean isShown) {
mIsDialpadShowing = isShown;
updateFabPosition();
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 3f9c567..6961e8a 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -66,7 +66,8 @@
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;
@@ -75,6 +76,7 @@
private CallTimer mCallTimer;
private Context mContext;
private boolean mSpinnerShowing = false;
+ private boolean mHasShownToast = false;
public static class ContactLookupCallback implements ContactInfoCacheCallback {
private final WeakReference<CallCardPresenter> mCallCardPresenter;
@@ -119,6 +121,12 @@
// Call may be null if disconnect happened already.
if (call != null) {
mPrimary = call;
+ if (shouldShowNoteSentToast(mPrimary)) {
+ final CallCardUi ui = getUi();
+ if (ui != null) {
+ ui.showNoteSentToast();
+ }
+ }
CallList.getInstance().addCallUpdateListener(call.getId(), this);
// start processing lookups right away.
@@ -206,16 +214,22 @@
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;
mPrimary = primary;
+ if (primaryChanged && shouldShowNoteSentToast(primary)) {
+ ui.showNoteSentToast();
+ }
+
// Refresh primary call information if either:
// 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 +334,19 @@
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,15 +375,23 @@
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.
*/
private void maybeShowManageConferenceCallButton() {
@@ -402,11 +437,9 @@
// 1. This is an emergency call.
// 2. The phone is in Emergency Callback Mode, which means we should show the callback
// number.
- boolean isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(
- getNumberFromHandle(mPrimary.getHandle()));
boolean showCallbackNumber = mPrimary.hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE);
- if (isEmergencyCall || showCallbackNumber) {
+ if (mPrimary.isEmergencyCall() || showCallbackNumber) {
callbackNumber = getSubscriptionNumber();
} else {
StatusHints statusHints = mPrimary.getTelecommCall().getDetails().getStatusHints();
@@ -426,7 +459,7 @@
callbackNumber = null;
}
- getUi().setCallbackNumber(callbackNumber, isEmergencyCall || showCallbackNumber);
+ getUi().setCallbackNumber(callbackNumber, mPrimary.isEmergencyCall() || showCallbackNumber);
}
public void updateCallTime() {
@@ -582,13 +615,38 @@
Log.d(TAG, "Update primary display info for " + mPrimaryContactInfo);
String name = getNameForCall(mPrimaryContactInfo);
- String number = getNumberForCall(mPrimaryContactInfo);
+ String number;
+
+ boolean isChildNumberShown = !TextUtils.isEmpty(mPrimary.getChildNumber());
+ 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,
- mPrimaryContactInfo.label,
+ isChildNumberShown || isCallSubjectShown ? null : mPrimaryContactInfo.label,
mPrimaryContactInfo.photo,
mPrimaryContactInfo.isSipCall);
} else {
@@ -597,8 +655,7 @@
}
if (mEmergencyCallListener != null) {
- boolean isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(
- getNumberFromHandle(mPrimary.getHandle()));
+ boolean isEmergencyCall = mPrimary.isEmergencyCall();
mEmergencyCallListener.onCallUpdated((BaseFragment) ui, isEmergencyCall);
}
}
@@ -848,6 +905,38 @@
}
}
+ /**
+ * 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());
+ }
+
+ /**
+ * Determines whether the "note sent" toast should be shown. It should be shown for a new
+ * outgoing call with a subject.
+ *
+ * @param call The call
+ * @return {@code true} if the toast should be shown, {@code false} otherwise.
+ */
+ private boolean shouldShowNoteSentToast(Call call) {
+ return call != null && !TextUtils
+ .isEmpty(call.getTelecommCall().getDetails().getIntentExtras().getString(
+ TelecomManager.EXTRA_CALL_SUBJECT)) &&
+ (call.getState() == Call.State.DIALING || call.getState() == Call.State.CONNECTING);
+ }
+
public interface CallCardUi extends Ui {
void setVisible(boolean on);
void setCallCardVisible(boolean visible);
@@ -866,11 +955,15 @@
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();
+ void showNoteSentToast();
}
}
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index c0014bd..666ba95 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -160,6 +160,22 @@
}
}
+ /**
+ * Called when the last forwarded number changes for a call. With IMS, the last forwarded
+ * number changes due to a supplemental service notification, so it is not pressent at the
+ * start of the call.
+ *
+ * @param call The call.
+ */
+ public void onLastForwardedNumberChange(Call call) {
+ final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
+ if (listeners != null) {
+ for (CallUpdateListener listener : listeners) {
+ listener.onLastForwardedNumberChange();
+ }
+ }
+ }
+
public void notifyCallUpdateListeners(Call call) {
final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId());
if (listeners != null) {
@@ -611,5 +627,10 @@
* @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/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index acb11b5..c6892b2 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -871,7 +871,11 @@
for (int i=0; i<tasks.size(); i++) {
ActivityManager.AppTask task = tasks.get(i);
if (task.getTaskInfo().id == taskId) {
- task.setExcludeFromRecents(exclude);
+ try {
+ task.setExcludeFromRecents(exclude);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "RuntimeException when excluding task from recents.", e);
+ }
}
}
}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index ff1a50f..2d7d40a 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -120,28 +120,34 @@
private final android.telecom.Call.Callback mCallCallback =
new android.telecom.Call.Callback() {
@Override
- public void onPostDialWait(android.telecom.Call call, String remainingPostDialSequence) {
- onPostDialCharWait(mCallList.getCallByTelecommCall(call).getId(),
- remainingPostDialSequence);
+ public void onPostDialWait(android.telecom.Call telecomCall,
+ String remainingPostDialSequence) {
+ final Call call = mCallList.getCallByTelecommCall(telecomCall);
+ if (call == null) {
+ Log.w(this, "Call not found in call list: " + telecomCall);
+ return;
+ }
+ onPostDialCharWait(call.getId(), remainingPostDialSequence);
}
@Override
- public void onDetailsChanged(android.telecom.Call call,
+ public void onDetailsChanged(android.telecom.Call telecomCall,
android.telecom.Call.Details details) {
+ final Call call = mCallList.getCallByTelecommCall(telecomCall);
+ if (call == null) {
+ Log.w(this, "Call not found in call list: " + telecomCall);
+ return;
+ }
for (InCallDetailsListener listener : mDetailsListeners) {
- listener.onDetailsChanged(mCallList.getCallByTelecommCall(call),
- details);
+ listener.onDetailsChanged(call, details);
}
}
@Override
- public void onConferenceableCallsChanged(
- android.telecom.Call call, List<android.telecom.Call> conferenceableCalls) {
- Log.i(this, "onConferenceableCallsChanged: " + call);
- for (InCallDetailsListener listener : mDetailsListeners) {
- listener.onDetailsChanged(mCallList.getCallByTelecommCall(call),
- call.getDetails());
- }
+ public void onConferenceableCallsChanged(android.telecom.Call telecomCall,
+ List<android.telecom.Call> conferenceableCalls) {
+ Log.i(this, "onConferenceableCallsChanged: " + telecomCall);
+ onDetailsChanged(telecomCall, telecomCall.getDetails());
}
};
@@ -1132,7 +1138,7 @@
* call.
*/
public static boolean isCallWithNoValidAccounts(Call call) {
- if (call != null && !isEmergencyCall(call)) {
+ if (call != null && !call.isEmergencyCall()) {
Bundle extras = call.getIntentExtras();
if (extras == null) {
@@ -1151,14 +1157,6 @@
return false;
}
- private static boolean isEmergencyCall(Call call) {
- final Uri handle = call.getHandle();
- if (handle == null) {
- return false;
- }
- return PhoneNumberUtils.isEmergencyNumber(handle.getSchemeSpecificPart());
- }
-
/**
* Sets the DisconnectCause for a call that was disconnected because it was missing a
* PhoneAccount or PhoneAccounts to select from.
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 8df4520..e583434 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -40,6 +40,8 @@
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 @@
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 @@
// 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 @@
notificationType = NOTIFICATION_IN_CALL;
}
- if (!checkForChangeAndSaveData(iconResId, contentResId, largeIcon, contentTitle, state,
+ if (!checkForChangeAndSaveData(iconResId, content, largeIcon, contentTitle, state,
notificationType)) {
return;
}
@@ -244,7 +246,7 @@
}
// Set the content
- builder.setContentText(mContext.getString(contentResId));
+ builder.setContentText(content);
builder.setSmallIcon(iconResId);
builder.setContentTitle(contentTitle);
builder.setLargeIcon(largeIcon);
@@ -306,7 +308,7 @@
* 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 @@
(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 @@
/**
* 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 @@
resId = R.string.notification_requesting_video_call;
}
- return resId;
+ return mContext.getString(resId);
}
/**
@@ -637,4 +646,9 @@
updateNotification(mInCallState, CallList.getInstance());
}
}
+
+ @Override
+ public void onLastForwardedNumberChange() {
+ // no-op
+ }
}