summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/incallui/AnswerPresenter.java11
-rw-r--r--src/com/android/incallui/AudioModeProvider.java4
-rw-r--r--src/com/android/incallui/BaseFragment.java14
-rw-r--r--src/com/android/incallui/Call.java12
-rw-r--r--src/com/android/incallui/CallButtonFragment.java254
-rw-r--r--src/com/android/incallui/CallButtonPresenter.java100
-rw-r--r--src/com/android/incallui/CallCardFragment.java157
-rw-r--r--src/com/android/incallui/CallCardPresenter.java92
-rw-r--r--src/com/android/incallui/CallList.java3
-rw-r--r--src/com/android/incallui/CallerInfo.java19
-rw-r--r--src/com/android/incallui/CallerInfoAsyncQuery.java5
-rw-r--r--src/com/android/incallui/CallerInfoUtils.java13
-rw-r--r--src/com/android/incallui/CircularRevealActivity.java169
-rw-r--r--src/com/android/incallui/ConferenceManagerFragment.java3
-rw-r--r--src/com/android/incallui/ConferenceManagerPresenter.java48
-rw-r--r--src/com/android/incallui/ConferenceParticipantListAdapter.java12
-rw-r--r--src/com/android/incallui/ContactInfoCache.java38
-rw-r--r--src/com/android/incallui/DialpadFragment.java42
-rw-r--r--src/com/android/incallui/InCallActivity.java107
-rw-r--r--src/com/android/incallui/InCallDateUtils.java56
-rw-r--r--src/com/android/incallui/InCallPresenter.java348
-rw-r--r--src/com/android/incallui/InCallServiceImpl.java23
-rw-r--r--src/com/android/incallui/InCallUIMaterialColorMapUtils.java52
-rw-r--r--src/com/android/incallui/Log.java8
-rw-r--r--src/com/android/incallui/Presenter.java6
-rw-r--r--src/com/android/incallui/SelectPhoneAccountDialogFragment.java150
-rw-r--r--src/com/android/incallui/StatusBarNotifier.java54
-rw-r--r--src/com/android/incallui/TelecomAdapter.java13
-rw-r--r--src/com/android/incallui/widget/multiwaveview/GlowPadView.java1
29 files changed, 1290 insertions, 524 deletions
diff --git a/src/com/android/incallui/AnswerPresenter.java b/src/com/android/incallui/AnswerPresenter.java
index 5af13f70..184b3096 100644
--- a/src/com/android/incallui/AnswerPresenter.java
+++ b/src/com/android/incallui/AnswerPresenter.java
@@ -16,10 +16,9 @@
package com.android.incallui;
-import android.telecom.PhoneCapabilities;
-import android.app.KeyguardManager;
import android.content.Context;
import android.os.SystemProperties;
+import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import java.util.List;
@@ -323,6 +322,7 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
public void onText() {
if (getUi() != null) {
+ InCallPresenter.getInstance().getTelecomManager().silenceRinger();
getUi().showMessageDialog();
}
}
@@ -343,7 +343,9 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
final Context context = getUi().getContext();
mHasTextMessages = textMsgs != null;
- boolean withSms = call.can(PhoneCapabilities.RESPOND_VIA_TEXT) && mHasTextMessages;
+ boolean withSms =
+ call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)
+ && mHasTextMessages;
if (call.isVideoCall(context)) {
if (withSms) {
getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
@@ -380,7 +382,8 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
call.getId());
getUi().showAnswerUi(true);
- boolean withSms = call.can(PhoneCapabilities.RESPOND_VIA_TEXT) && textMsgs != null;
+ boolean withSms = call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)
+ && textMsgs != null;
if (call.isVideoCall(getUi().getContext())) {
if (withSms) {
getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
diff --git a/src/com/android/incallui/AudioModeProvider.java b/src/com/android/incallui/AudioModeProvider.java
index df5a2333..7a91731b 100644
--- a/src/com/android/incallui/AudioModeProvider.java
+++ b/src/com/android/incallui/AudioModeProvider.java
@@ -41,8 +41,8 @@ import java.util.List;
private Phone.Listener mPhoneListener = new Phone.Listener() {
@Override
public void onAudioStateChanged(Phone phone, AudioState audioState) {
- onAudioModeChange(audioState.route, audioState.isMuted);
- onSupportedAudioModeChange(audioState.supportedRouteMask);
+ onAudioModeChange(audioState.getRoute(), audioState.isMuted());
+ onSupportedAudioModeChange(audioState.getSupportedRouteMask());
}
};
diff --git a/src/com/android/incallui/BaseFragment.java b/src/com/android/incallui/BaseFragment.java
index 6c2ba216..1ef3b151 100644
--- a/src/com/android/incallui/BaseFragment.java
+++ b/src/com/android/incallui/BaseFragment.java
@@ -50,8 +50,22 @@ public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends
}
@Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mPresenter.onRestoreInstanceState(savedInstanceState);
+ }
+ }
+
+ @Override
public void onDestroyView() {
super.onDestroyView();
mPresenter.onUiDestroy(getUi());
}
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mPresenter.onSaveInstanceState(outState);
+ }
}
diff --git a/src/com/android/incallui/Call.java b/src/com/android/incallui/Call.java
index 3bac3715..debe960e 100644
--- a/src/com/android/incallui/Call.java
+++ b/src/com/android/incallui/Call.java
@@ -24,7 +24,6 @@ import android.hardware.camera2.CameraCharacteristics;
import android.net.Uri;
import android.telecom.CallProperties;
import android.telecom.DisconnectCause;
-import android.telecom.PhoneCapabilities;
import android.telecom.GatewayInfo;
import android.telecom.InCallService.VideoCall;
import android.telecom.PhoneAccountHandle;
@@ -395,7 +394,7 @@ public final class Call {
public boolean can(int capabilities) {
int supportedCapabilities = mTelecommCall.getDetails().getCallCapabilities();
- if ((capabilities & PhoneCapabilities.MERGE_CONFERENCE) != 0) {
+ if ((capabilities & android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) {
// We allow you to merge if the capabilities allow it or if it is a call with
// conferenceable calls.
if (CallList.getInstance().isDsdaEnabled()) {
@@ -415,18 +414,18 @@ public final class Call {
}
}
if (!hasConfenceableCall &&
- ((PhoneCapabilities.MERGE_CONFERENCE & supportedCapabilities) == 0)) {
+ ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
// Cannot merge calls if there are no calls to merge with.
return false;
}
} else if (mTelecommCall.getConferenceableCalls().isEmpty() ||
- ((PhoneCapabilities.MERGE_CONFERENCE & supportedCapabilities) == 0)) {
+ ((android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) {
// Cannot merge calls if there are no calls to merge with or
// capability to merge is missing
return false;
}
// Clearing this bit means this capability is available
- capabilities &= ~PhoneCapabilities.MERGE_CONFERENCE;
+ capabilities &= ~android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE;
}
return (capabilities == (capabilities & mTelecommCall.getDetails().getCallCapabilities()));
}
@@ -603,7 +602,8 @@ public final class Call {
+ "VideoSettings:%s]",
mId,
State.toString(getState()),
- PhoneCapabilities.toString(mTelecommCall.getDetails().getCallCapabilities()),
+ android.telecom.Call.Details
+ .capabilitiesToString(mTelecommCall.getDetails().getCallCapabilities()),
mChildCallIds,
getParentId(),
mTelecommCall.getDetails().getVideoState(), mIsActiveSub,
diff --git a/src/com/android/incallui/CallButtonFragment.java b/src/com/android/incallui/CallButtonFragment.java
index a8d6ef5c..2c7fbe6f 100644
--- a/src/com/android/incallui/CallButtonFragment.java
+++ b/src/com/android/incallui/CallButtonFragment.java
@@ -19,20 +19,24 @@ package com.android.incallui;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.StateListDrawable;
import android.os.Bundle;
import android.telecom.AudioState;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import android.view.ContextThemeWrapper;
+import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.PopupMenu;
@@ -42,26 +46,29 @@ import android.widget.PopupMenu.OnMenuItemClickListener;
import java.util.ArrayList;
+import com.android.contacts.common.util.MaterialColorMapUtils;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+
/**
* Fragment for call control buttons
*/
public class CallButtonFragment
extends BaseFragment<CallButtonPresenter, CallButtonPresenter.CallButtonUi>
implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener,
- View.OnClickListener, CompoundButton.OnCheckedChangeListener {
+ View.OnClickListener {
private static final int INVALID_INDEX = -1;
- private ImageButton mAudioButton;
+ private CompoundButton mAudioButton;
private ImageButton mChangeToVoiceButton;
- private ImageButton mMuteButton;
- private ImageButton mShowDialpadButton;
- private ImageButton mHoldButton;
+ private CompoundButton mMuteButton;
+ private CompoundButton mShowDialpadButton;
+ private CompoundButton mHoldButton;
private ImageButton mSwapButton;
private ImageButton mChangeToVideoButton;
- private ImageButton mSwitchCameraButton;
+ private CompoundButton mSwitchCameraButton;
private ImageButton mAddCallButton;
private ImageButton mMergeButton;
- private ImageButton mPauseVideoButton;
+ private CompoundButton mPauseVideoButton;
private ImageButton mOverflowButton;
private ImageButton mAddParticipantButton;
private ImageButton mManageVideoCallConferenceButton;
@@ -78,6 +85,7 @@ public class CallButtonFragment
private static final int VISIBLE = 255;
private boolean mIsEnabled;
+ private MaterialPalette mCurrentThemeColors;
@Override
CallButtonPresenter createPresenter() {
@@ -100,27 +108,27 @@ public class CallButtonFragment
Bundle savedInstanceState) {
final View parent = inflater.inflate(R.layout.call_button_fragment, container, false);
- mAudioButton = (ImageButton) parent.findViewById(R.id.audioButton);
+ mAudioButton = (CompoundButton) parent.findViewById(R.id.audioButton);
mAudioButton.setOnClickListener(this);
mChangeToVoiceButton = (ImageButton) parent.findViewById(R.id.changeToVoiceButton);
mChangeToVoiceButton. setOnClickListener(this);
- mMuteButton = (ImageButton) parent.findViewById(R.id.muteButton);
+ mMuteButton = (CompoundButton) parent.findViewById(R.id.muteButton);
mMuteButton.setOnClickListener(this);
- mShowDialpadButton = (ImageButton) parent.findViewById(R.id.dialpadButton);
+ mShowDialpadButton = (CompoundButton) parent.findViewById(R.id.dialpadButton);
mShowDialpadButton.setOnClickListener(this);
- mHoldButton = (ImageButton) parent.findViewById(R.id.holdButton);
+ mHoldButton = (CompoundButton) parent.findViewById(R.id.holdButton);
mHoldButton.setOnClickListener(this);
mSwapButton = (ImageButton) parent.findViewById(R.id.swapButton);
mSwapButton.setOnClickListener(this);
mChangeToVideoButton = (ImageButton) parent.findViewById(R.id.changeToVideoButton);
mChangeToVideoButton.setOnClickListener(this);
- mSwitchCameraButton = (ImageButton) parent.findViewById(R.id.switchCameraButton);
+ mSwitchCameraButton = (CompoundButton) parent.findViewById(R.id.switchCameraButton);
mSwitchCameraButton.setOnClickListener(this);
mAddCallButton = (ImageButton) parent.findViewById(R.id.addButton);
mAddCallButton.setOnClickListener(this);
mMergeButton = (ImageButton) parent.findViewById(R.id.mergeButton);
mMergeButton.setOnClickListener(this);
- mPauseVideoButton = (ImageButton) parent.findViewById(R.id.pauseVideoButton);
+ mPauseVideoButton = (CompoundButton) parent.findViewById(R.id.pauseVideoButton);
mPauseVideoButton.setOnClickListener(this);
mAddParticipantButton = (ImageButton) parent.findViewById(R.id.addParticipant);
mAddParticipantButton.setOnClickListener(this);
@@ -147,10 +155,8 @@ public class CallButtonFragment
getPresenter().refreshMuteState();
}
super.onResume();
- }
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ updateColors();
}
@Override
@@ -158,6 +164,7 @@ public class CallButtonFragment
int id = view.getId();
Log.d(this, "onClick(View " + view + ", id " + id + ")...");
+ boolean isClickHandled = true;
switch(id) {
case R.id.audioButton:
onAudioButtonClicked();
@@ -169,8 +176,7 @@ public class CallButtonFragment
getPresenter().displayModifyCallOptions();
break;
case R.id.muteButton: {
- final ImageButton button = (ImageButton) view;
- getPresenter().muteClicked(!button.isSelected());
+ getPresenter().muteClicked(!mMuteButton.isSelected());
break;
}
case R.id.mergeButton:
@@ -178,8 +184,7 @@ public class CallButtonFragment
mMergeButton.setEnabled(false);
break;
case R.id.holdButton: {
- final ImageButton button = (ImageButton) view;
- getPresenter().holdClicked(!button.isSelected());
+ getPresenter().holdClicked(!mHoldButton.isSelected());
break;
}
case R.id.swapButton:
@@ -209,9 +214,122 @@ public class CallButtonFragment
onManageVideoCallConferenceClicked();
break;
default:
+ isClickHandled = false;
Log.wtf(this, "onClick: unexpected");
break;
}
+
+ if (isClickHandled) {
+ view.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+
+ public void updateColors() {
+ MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
+
+ if (mCurrentThemeColors != null && mCurrentThemeColors.equals(themeColors)) {
+ return;
+ }
+
+ Resources res = getActivity().getResources();
+ View[] compoundButtons = {
+ mAudioButton,
+ mMuteButton,
+ mShowDialpadButton,
+ mHoldButton,
+ mSwitchCameraButton,
+ mPauseVideoButton
+ };
+
+ for (View button : compoundButtons) {
+ final LayerDrawable layers = (LayerDrawable) button.getBackground();
+ final RippleDrawable btnCompoundDrawable = compoundBackgroundDrawable(themeColors);
+ layers.setDrawableByLayerId(R.id.compoundBackgroundItem, btnCompoundDrawable);
+ }
+
+ ImageButton[] normalButtons = {
+ mChangeToVoiceButton,
+ mSwapButton,
+ mChangeToVideoButton,
+ mAddCallButton,
+ mMergeButton,
+ mOverflowButton
+ };
+
+ for (ImageButton button : normalButtons) {
+ final LayerDrawable layers = (LayerDrawable) button.getBackground();
+ final RippleDrawable btnDrawable = backgroundDrawable(themeColors);
+ layers.setDrawableByLayerId(R.id.backgroundItem, btnDrawable);
+ }
+
+ mCurrentThemeColors = themeColors;
+ }
+
+ /**
+ * Generate a RippleDrawable which will be the background for a compound button, i.e.
+ * a button with pressed and unpressed states. The unpressed state will be the same color
+ * as the rest of the call card, the pressed state will be the dark version of that color.
+ */
+ private RippleDrawable compoundBackgroundDrawable(MaterialPalette palette) {
+ Resources res = getResources();
+ ColorStateList rippleColor =
+ ColorStateList.valueOf(res.getColor(R.color.incall_accent_color));
+
+ StateListDrawable stateListDrawable = new StateListDrawable();
+ addSelectedAndFocused(res, stateListDrawable);
+ addFocused(res, stateListDrawable);
+ addSelected(res, stateListDrawable, palette);
+ addUnselected(res, stateListDrawable, palette);
+
+ return new RippleDrawable(rippleColor, stateListDrawable, null);
+ }
+
+ /**
+ * Generate a RippleDrawable which will be the background of a button to ensure it
+ * is the same color as the rest of the call card.
+ */
+ private RippleDrawable backgroundDrawable(MaterialPalette palette) {
+ Resources res = getResources();
+ ColorStateList rippleColor =
+ ColorStateList.valueOf(res.getColor(R.color.incall_accent_color));
+
+ StateListDrawable stateListDrawable = new StateListDrawable();
+ addFocused(res, stateListDrawable);
+ addUnselected(res, stateListDrawable, palette);
+
+ return new RippleDrawable(rippleColor, stateListDrawable, null);
+ }
+
+ // state_selected and state_focused
+ private void addSelectedAndFocused(Resources res, StateListDrawable drawable) {
+ int[] selectedAndFocused = {android.R.attr.state_selected, android.R.attr.state_focused};
+ Drawable selectedAndFocusedDrawable = res.getDrawable(R.drawable.btn_selected_focused);
+ drawable.addState(selectedAndFocused, selectedAndFocusedDrawable);
+ }
+
+ // state_focused
+ private void addFocused(Resources res, StateListDrawable drawable) {
+ int[] focused = {android.R.attr.state_focused};
+ Drawable focusedDrawable = res.getDrawable(R.drawable.btn_unselected_focused);
+ drawable.addState(focused, focusedDrawable);
+ }
+
+ // state_selected
+ private void addSelected(Resources res, StateListDrawable drawable, MaterialPalette palette) {
+ int[] selected = {android.R.attr.state_selected};
+ LayerDrawable selectedDrawable = (LayerDrawable) res.getDrawable(R.drawable.btn_selected);
+ ((GradientDrawable) selectedDrawable.getDrawable(0)).setColor(palette.mSecondaryColor);
+ drawable.addState(selected, selectedDrawable);
+ }
+
+ // default
+ private void addUnselected(Resources res, StateListDrawable drawable, MaterialPalette palette) {
+ LayerDrawable unselectedDrawable =
+ (LayerDrawable) res.getDrawable(R.drawable.btn_unselected);
+ ((GradientDrawable) unselectedDrawable.getDrawable(0)).setColor(palette.mPrimaryColor);
+ drawable.addState(new int[0], unselectedDrawable);
}
@Override
@@ -242,8 +360,6 @@ public class CallButtonFragment
public void setMute(boolean value) {
if (mMuteButton.isSelected() != value) {
mMuteButton.setSelected(value);
- maybeSendAccessibilityEvent(mMuteButton, value ? R.string.accessibility_call_muted
- : R.string.accessibility_call_unmuted);
}
}
@@ -271,9 +387,6 @@ public class CallButtonFragment
public void setHold(boolean value) {
if (mHoldButton.isSelected() != value) {
mHoldButton.setSelected(value);
- maybeSendAccessibilityEvent(mHoldButton,
- value ? R.string.accessibility_call_put_on_hold :
- R.string.accessibility_call_removed_from_hold);
}
}
@@ -298,6 +411,11 @@ public class CallButtonFragment
}
@Override
+ public void enableChangeToVideoButton(boolean enable) {
+ mChangeToVideoButton.setEnabled(enable);
+ }
+
+ @Override
public void showSwitchCameraButton(boolean show) {
mSwitchCameraButton.setVisibility(show ? View.VISIBLE : View.GONE);
}
@@ -499,26 +617,7 @@ public class CallButtonFragment
refreshAudioModePopup();
if (mPrevAudioMode != mode) {
- if (mPrevAudioMode != 0) {
- int stringId = 0;
- switch (mode) {
- case AudioState.ROUTE_EARPIECE:
- stringId = R.string.accessibility_earpiece_selected;
- break;
- case AudioState.ROUTE_BLUETOOTH:
- stringId = R.string.accessibility_bluetooth_headset_selected;
- break;
- case AudioState.ROUTE_WIRED_HEADSET:
- stringId = R.string.accessibility_wired_headset_selected;
- break;
- case AudioState.ROUTE_SPEAKER:
- stringId = R.string.accessibility_speakerphone_selected;
- break;
- }
- if (stringId != 0) {
- maybeSendAccessibilityEvent(mAudioButton, stringId);
- }
- }
+ updateAudioButtonContentDescription(mode);
mPrevAudioMode = mode;
}
}
@@ -572,6 +671,7 @@ public class CallButtonFragment
public void onDismiss(PopupMenu menu) {
Log.d(this, "- onDismiss: " + menu);
mAudioModePopupVisible = false;
+ updateAudioButtons(getPresenter().getSupportedAudio());
}
/**
@@ -593,7 +693,7 @@ public class CallButtonFragment
Log.d(this, "onManageVideoCallConferenceClicked");
final InCallActivity activity = (InCallActivity) getActivity();
if (activity != null) {
- activity.showConferenceCallManager();
+ activity.showConferenceCallManager(true);
}
}
@@ -637,10 +737,8 @@ public class CallButtonFragment
Log.d(this, "updateAudioButtons - popup menu mode");
audioButtonEnabled = true;
+ audioButtonChecked = true;
showMoreIndicator = true;
- // The audio button is NOT a toggle in this state. (And its
- // setChecked() state is irrelevant since we completely hide the
- // btn_compound_background layer anyway.)
// Update desired layers:
if (isAudio(AudioState.ROUTE_BLUETOOTH)) {
@@ -654,6 +752,9 @@ public class CallButtonFragment
// sort of "wired headset" icon here instead of the "handset
// earpiece" icon. (Still need an asset for that, though.)
}
+
+ // The audio button is NOT a toggle in this state, so set selected to false.
+ mAudioButton.setSelected(false);
} else if (speakerSupported) {
Log.d(this, "updateAudioButtons - speaker toggle mode");
@@ -662,6 +763,7 @@ public class CallButtonFragment
// The audio button *is* a toggle in this state, and indicated the
// current state of the speakerphone.
audioButtonChecked = isAudio(AudioState.ROUTE_SPEAKER);
+ mAudioButton.setSelected(audioButtonChecked);
// update desired layers:
showToggleIndicator = true;
@@ -673,6 +775,7 @@ public class CallButtonFragment
// irrelevant since it's always disabled and unchecked.
audioButtonEnabled = false;
audioButtonChecked = false;
+ mAudioButton.setSelected(false);
// update desired layers:
showToggleIndicator = true;
@@ -690,7 +793,7 @@ public class CallButtonFragment
// Only enable the audio button if the fragment is enabled.
mAudioButton.setEnabled(audioButtonEnabled && mIsEnabled);
- mAudioButton.setSelected(audioButtonChecked);
+ mAudioButton.setChecked(audioButtonChecked);
final LayerDrawable layers = (LayerDrawable) mAudioButton.getBackground();
Log.d(this, "'layers' drawable: " + layers);
@@ -712,6 +815,38 @@ public class CallButtonFragment
}
+ /**
+ * Update the content description of the audio button.
+ */
+ private void updateAudioButtonContentDescription(int mode) {
+ int stringId = 0;
+
+ // If bluetooth is not supported, the audio buttion will toggle, so use the label "speaker".
+ // Otherwise, use the label of the currently selected audio mode.
+ if (!isSupported(AudioState.ROUTE_BLUETOOTH)) {
+ stringId = R.string.audio_mode_speaker;
+ } else {
+ switch (mode) {
+ case AudioState.ROUTE_EARPIECE:
+ stringId = R.string.audio_mode_earpiece;
+ break;
+ case AudioState.ROUTE_BLUETOOTH:
+ stringId = R.string.audio_mode_bluetooth;
+ break;
+ case AudioState.ROUTE_WIRED_HEADSET:
+ stringId = R.string.audio_mode_wired_headset;
+ break;
+ case AudioState.ROUTE_SPEAKER:
+ stringId = R.string.audio_mode_speaker;
+ break;
+ }
+ }
+
+ if (stringId != 0) {
+ mAudioButton.setContentDescription(getResources().getString(stringId));
+ }
+ }
+
private void showAudioModePopup() {
Log.d(this, "showAudioPopup()...");
@@ -791,21 +926,6 @@ public class CallButtonFragment
return getActivity();
}
- private void maybeSendAccessibilityEvent(View view, int stringId) {
- final Context context = getActivity();
- AccessibilityManager manager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (manager != null && manager.isEnabled()) {
- AccessibilityEvent e = AccessibilityEvent.obtain();
- e.setSource(view);
- e.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT);
- e.setClassName(getClass().getName());
- e.setPackageName(context.getPackageName());
- e.getText().add(context.getResources().getString(stringId));
- manager.sendAccessibilityEvent(e);
- }
- }
-
private boolean isTtyModeEnabled() {
return (android.provider.Settings.Secure.getInt(
getContext().getContentResolver(),
diff --git a/src/com/android/incallui/CallButtonPresenter.java b/src/com/android/incallui/CallButtonPresenter.java
index 1beb7b26..bee02a6c 100644
--- a/src/com/android/incallui/CallButtonPresenter.java
+++ b/src/com/android/incallui/CallButtonPresenter.java
@@ -18,9 +18,9 @@ package com.android.incallui;
import android.app.AlertDialog;
import android.content.Context;
+import android.os.Bundle;
import android.telecom.AudioState;
import android.telecom.InCallService.VideoCall;
-import android.telecom.PhoneCapabilities;
import android.telecom.VideoProfile;
import com.android.incallui.AudioModeProvider.AudioModeListener;
@@ -45,6 +45,9 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
InCallDetailsListener, CallList.ActiveSubChangeListener, CanAddCallListener,
CameraSelectionListener {
+ private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
+ private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
+
private Call mCall;
private boolean mAutomaticallyMuted = false;
private boolean mPreviousMuteState = false;
@@ -95,9 +98,10 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
// OUTGOING. We may want to do that once we start showing "Voice mail" label on
// the dialpad too.)
if (ui != null) {
- if (oldState == InCallState.OUTGOING && mCall != null
- && PhoneNumberUtils.isVoiceMailNumber(mCall.getNumber())) {
- ui.displayDialpad(true /* show */, true /* animate */);
+ if (oldState == InCallState.OUTGOING && mCall != null) {
+ if (CallerInfoUtils.isVoiceMailNumber(ui.getContext(), mCall)) {
+ ui.displayDialpad(true /* show */, true /* animate */);
+ }
}
}
} else if (newState == InCallState.INCOMING) {
@@ -242,7 +246,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
mPreviousMuteState = AudioModeProvider.getInstance().getMute();
// Simulate a click on the mute button
muteClicked(true);
-
TelecomAdapter.getInstance().addCall();
}
@@ -350,7 +353,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
updateCallButtons(call, ui.getContext());
- ui.enableMute(call.can(PhoneCapabilities.MUTE));
+ ui.enableMute(call.can(android.telecom.Call.Details.CAPABILITY_MUTE));
}
private static int toInteger(boolean b) {
@@ -366,28 +369,36 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
private void updateCallButtons(Call call, Context context) {
if (CallUtils.isVideoCall(call)) {
updateVoiceCallButtons(call);
- updateVideoCallButtons();
+ updateVideoCallButtons(call);
} else {
updateVoiceCallButtons(call);
}
}
- private void updateVideoCallButtons() {
+ private void updateVideoCallButtons(Call call) {
Log.v(this, "Showing buttons for video call.");
final CallButtonUi ui = getUi();
// Show all video-call-related buttons.
ui.showSwitchCameraButton(true);
ui.showPauseVideoButton(false);
+
+ final boolean supportHold = call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD);
+ final boolean enableHoldOption = call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
+ ui.showHoldButton(supportHold);
+ ui.enableHold(enableHoldOption);
+ ui.setHold(call.getState() == Call.State.ONHOLD);
}
private boolean canShowMergeOption() {
CallList callList = CallList.getInstance();
Call activeCall = callList.getActiveCall(), backgroundCall = callList.getBackgroundCall();
boolean activeCallCanMerge =
- (activeCall != null) && activeCall.can(PhoneCapabilities.MERGE_CONFERENCE);
+ (activeCall != null) &&
+ activeCall.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
boolean backgroundCallCanMerge =
- (backgroundCall != null) && backgroundCall.can(PhoneCapabilities.MERGE_CONFERENCE);
+ (backgroundCall != null) &&
+ backgroundCall.can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
String acId = (activeCall != null) ? activeCall.getId() : "null";
String bcId = (backgroundCall != null) ? backgroundCall.getId() : "null";
Log.v(this, "canShowMergeOption: " + acId + " " + activeCallCanMerge +
@@ -408,37 +419,39 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
ui.showAudioButton(true);
ui.showDialpadButton(true);
- Log.v(this, "Show hold ", call.can(PhoneCapabilities.SUPPORT_HOLD));
- Log.v(this, "Enable hold", call.can(PhoneCapabilities.HOLD));
- // TODO: Every button here is calculated based on the provided call
- // Is it ok that we don't pay attention to the call argument?
- Log.v(this, "Show merge " + canShowMergeOption());
-
- Log.v(this, "Show swap ", call.can(PhoneCapabilities.SWAP_CONFERENCE));
+ Log.v(this, "Show hold ", call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD));
+ Log.v(this, "Enable hold", call.can(android.telecom.Call.Details.CAPABILITY_HOLD));
+ Log.v(this, "Show merge ", call.can(
+ android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE));
+ Log.v(this, "Show swap ", call.can(
+ android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE));
Log.v(this, "Show add call ", TelecomAdapter.getInstance().canAddCall());
- Log.v(this, "Show mute ", call.can(PhoneCapabilities.MUTE));
- Log.v(this, "Show video call local:", call.can(PhoneCapabilities.SUPPORTS_VT_LOCAL)
- + " remote: " + call.can(PhoneCapabilities.SUPPORTS_VT_REMOTE));
+ Log.v(this, "Show mute ", call.can(android.telecom.Call.Details.CAPABILITY_MUTE));
+ Log.v(this, "Show video call local:", call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL)
+ + " remote: " + call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE));
final boolean canAdd = TelecomAdapter.getInstance().canAddCall();
- final boolean enableHoldOption = call.can(PhoneCapabilities.HOLD);
- final boolean supportHold = call.can(PhoneCapabilities.SUPPORT_HOLD);
+ final boolean enableHoldOption = call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
+ final boolean supportHold = call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD);
+ final boolean isCallOnHold = call.getState() == Call.State.ONHOLD;
- boolean canVideoCall = call.can(PhoneCapabilities.SUPPORTS_VT_LOCAL)
- && call.can(PhoneCapabilities.SUPPORTS_VT_REMOTE)
- && call.can(PhoneCapabilities.CALL_TYPE_MODIFIABLE);
+ boolean canVideoCall = call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL)
+ && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE);
ui.showChangeToVideoButton(canVideoCall);
+ ui.enableChangeToVideoButton(!isCallOnHold);
- final boolean showMergeOption = canShowMergeOption();
+ final boolean showMergeOption = call.can(
+ android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
final boolean showAddCallOption = canAdd;
- final boolean showAddParticipantOption = call.can(PhoneCapabilities.ADD_PARTICIPANT);
+ final boolean showAddParticipantOption = call.can(android.telecom.Call.Details.ADD_PARTICIPANT);
final boolean showManageVideoCallConferenceOption = call.can(
- PhoneCapabilities.MANAGE_CONFERENCE) && CallUtils.isVideoCall(call);
+ android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE) && CallUtils.isVideoCall(call);
// Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
// (1) If the device normally can hold, show HOLD in a disabled state.
// (2) If the device doesn't have the concept of hold/swap, remove the button.
- final boolean showSwapOption = call.can(PhoneCapabilities.SWAP_CONFERENCE);
+ final boolean showSwapOption = call.can(
+ android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
final boolean showHoldOption = !showSwapOption && (enableHoldOption || supportHold);
ui.setHold(call.getState() == Call.State.ONHOLD);
@@ -451,18 +464,24 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
buttonCount += toInteger(showAddParticipantOption);
buttonCount += toInteger(showHoldOption);
buttonCount += toInteger(showSwapOption);
- buttonCount += toInteger(call.can(PhoneCapabilities.MUTE));
+ buttonCount += toInteger(call.can(android.telecom.Call.Details.CAPABILITY_MUTE));
buttonCount += toInteger(showManageVideoCallConferenceOption);
Log.v(this, "show AddParticipant: " + showAddParticipantOption +
" show ManageVideoCallConference: " + showManageVideoCallConferenceOption);
Log.v(this, "No of InCall buttons: " + buttonCount + " canVideoCall: " + canVideoCall);
+ ui.setHold(isCallOnHold);
+
// Show overflow menu if number of buttons is greater than 5.
final boolean showOverflowMenu =
buttonCount > BUTTON_THRESOLD_TO_DISPLAY_OVERFLOW_MENU;
- final boolean isVideoOverflowScenario = canVideoCall && showOverflowMenu;
- final boolean isOverflowScenario = !canVideoCall && showOverflowMenu;
+ // If we show video upgrade and add/merge and hold/swap, the overflow menu is needed.
+ final boolean isVideoOverflowScenario = canVideoCall
+ && (showAddCallOption || showMergeOption) && (showHoldOption || showSwapOption);
+ // If we show hold/swap, add, and merge simultaneously, the overflow menu is needed.
+ final boolean isOverflowScenario =
+ (showHoldOption || showSwapOption) && showMergeOption && showAddCallOption;
if (isVideoOverflowScenario) {
ui.showHoldButton(false);
@@ -520,6 +539,22 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
mAutomaticallyMuted = false;
}
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
+ outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
+ }
+
+ @Override
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ mAutomaticallyMuted =
+ savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
+ mPreviousMuteState =
+ savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
+ super.onRestoreInstanceState(savedInstanceState);
+ }
+
public interface CallButtonUi extends Ui {
void setEnabled(boolean on);
void setMute(boolean on);
@@ -532,6 +567,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
void enableHold(boolean enabled);
void showSwapButton(boolean show);
void showChangeToVideoButton(boolean show);
+ void enableChangeToVideoButton(boolean enable);
void showSwitchCameraButton(boolean show);
void setSwitchCameraButton(boolean isBackFacingCamera);
void showAddCallButton(boolean show);
diff --git a/src/com/android/incallui/CallCardFragment.java b/src/com/android/incallui/CallCardFragment.java
index a1d826db..d67ae11d 100644
--- a/src/com/android/incallui/CallCardFragment.java
+++ b/src/com/android/incallui/CallCardFragment.java
@@ -30,6 +30,7 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Bundle;
@@ -42,6 +43,7 @@ import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.text.format.DateUtils;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.view.ContextThemeWrapper;
import android.view.Display;
import android.text.format.DateUtils;
@@ -68,7 +70,9 @@ import android.widget.Toast;
import android.telecom.AudioState;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
import com.android.contacts.common.widget.FloatingActionButtonController;
+import com.android.incallui.service.PhoneNumberService;
import com.android.internal.telephony.util.BlacklistUtils;
import com.android.phone.common.animation.AnimUtils;
@@ -102,6 +106,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
private TextView mElapsedTime;
private ImageButton mMoreMenuButton;
private MorePopupMenu mMoreMenu;
+ private Drawable mPrimaryPhotoDrawable;
// Container view that houses the entire primary call card, including the call buttons
private View mPrimaryCallCardContainer;
@@ -125,7 +130,6 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
private TextView mSecondaryCallName;
private View mSecondaryCallProviderInfo;
private TextView mSecondaryCallProviderLabel;
- private ImageView mSecondaryCallProviderIcon;
private View mSecondaryCallConferenceCallIcon;
private View mProgressSpinner;
@@ -189,6 +193,8 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
private InCallActivity mInCallActivity;
+ private MaterialPalette mCurrentThemeColors;
+
@Override
CallCardPresenter.CallCardUi getUi() {
return this;
@@ -326,7 +332,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
@Override
public void onClick(View v) {
InCallActivity activity = (InCallActivity) getActivity();
- activity.showConferenceCallManager();
+ activity.showConferenceCallManager(true);
}
});
@@ -492,7 +498,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
if (TextUtils.isEmpty(name)) {
mPrimaryName.setText(null);
} else {
- mPrimaryName.setText(name);
+ mPrimaryName.setText(nameIsNumber
+ ? PhoneNumberUtils.ttsSpanAsPhoneNumber(name)
+ : name);
// Set direction of the name field
int nameDirection = View.TEXT_DIRECTION_INHERIT;
@@ -517,7 +525,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mPhoneNumber.setText(null);
mPhoneNumber.setVisibility(View.GONE);
} else {
- mPhoneNumber.setText(number);
+ mPhoneNumber.setText(PhoneNumberUtils.ttsSpanAsPhoneNumber(number));
mPhoneNumber.setVisibility(View.VISIBLE);
mPhoneNumber.setTextDirection(View.TEXT_DIRECTION_LTR);
}
@@ -545,8 +553,10 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
if (TextUtils.isEmpty(number) && TextUtils.isEmpty(label)) {
mCallNumberAndLabel.setVisibility(View.GONE);
+ mElapsedTime.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
} else {
mCallNumberAndLabel.setVisibility(View.VISIBLE);
+ mElapsedTime.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END);
}
setPrimaryPhoneNumber(number);
@@ -563,7 +573,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
@Override
public void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
- String providerLabel, Drawable providerIcon, boolean isConference) {
+ String providerLabel, boolean isConference) {
if (show != mSecondaryCallInfo.isShown()) {
updateFabPositionForSecondaryCallInfo();
@@ -575,10 +585,11 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mSecondaryCallConferenceCallIcon.setVisibility(isConference ? View.VISIBLE : View.GONE);
- mSecondaryCallName.setText(name);
+ mSecondaryCallName.setText(nameIsNumber
+ ? PhoneNumberUtils.ttsSpanAsPhoneNumber(name)
+ : name);
if (hasProvider) {
mSecondaryCallProviderLabel.setText(providerLabel);
- mSecondaryCallProviderIcon.setImageDrawable(providerIcon);
}
int nameDirection = View.TEXT_DIRECTION_INHERIT;
@@ -598,7 +609,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
int sessionModificationState,
DisconnectCause disconnectCause,
String connectionLabel,
- Drawable connectionIcon,
+ Drawable callStateIcon,
String gatewayNumber,
boolean isWaitingForRemoteSide) {
boolean isGatewayCall = !TextUtils.isEmpty(gatewayNumber);
@@ -624,7 +635,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mCallStateLabel.setAlpha(1);
mCallStateLabel.setVisibility(View.VISIBLE);
- if (connectionIcon == null) {
+ if (callStateIcon == null) {
mCallStateIcon.clearAnimation();
mCallStateIcon.setVisibility(View.GONE);
} else {
@@ -632,7 +643,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
// Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is
// needed because the pulse animation operates on the view alpha.
mCallStateIcon.setAlpha(1.0f);
- mCallStateIcon.setImageDrawable(connectionIcon);
+ mCallStateIcon.setImageDrawable(callStateIcon);
}
if (VideoProfile.VideoState.isVideo(videoState)
@@ -645,7 +656,6 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) {
mCallStateLabel.clearAnimation();
- mCallStateIcon.clearAnimation();
} else {
mCallStateLabel.startAnimation(mPulseAnimation);
if (mCallStateIcon.getVisibility() == View.VISIBLE) {
@@ -653,17 +663,60 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
}
}
} else {
- mCallStateLabel.clearAnimation();
+ Animation callStateLabelAnimation = mCallStateLabel.getAnimation();
+ if (callStateLabelAnimation != null) {
+ callStateLabelAnimation.cancel();
+ }
mCallStateLabel.setText(null);
mCallStateLabel.setAlpha(0);
mCallStateLabel.setVisibility(View.GONE);
+ }
+
+ if (callStateIcon != null) {
+ mCallStateIcon.setVisibility(View.VISIBLE);
+ // Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is
+ // needed because the pulse animation operates on the view alpha.
+ mCallStateIcon.setAlpha(1.0f);
+ mCallStateIcon.setImageDrawable(callStateIcon);
+
+ if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED
+ || TextUtils.isEmpty(callStateLabel)) {
+ mCallStateIcon.clearAnimation();
+ } else {
+ mCallStateIcon.startAnimation(mPulseAnimation);
+ }
+
+ if (callStateIcon instanceof AnimationDrawable) {
+ ((AnimationDrawable) callStateIcon).start();
+ }
+ } else {
+ Animation callStateIconAnimation = mCallStateIcon.getAnimation();
+ if (callStateIconAnimation != null) {
+ callStateIconAnimation.cancel();
+ }
+
// Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is
// needed because the pulse animation operates on the view alpha.
mCallStateIcon.setAlpha(0.0f);
mCallStateIcon.setVisibility(View.GONE);
+ }
+ if (VideoProfile.VideoState.isBidirectional(videoState)
+ || (state == Call.State.ACTIVE && sessionModificationState
+ == Call.SessionModificationState.WAITING_FOR_RESPONSE)) {
+ mCallStateVideoCallIcon.setVisibility(View.VISIBLE);
+ } else {
mCallStateVideoCallIcon.setVisibility(View.GONE);
}
+
+ if (state == Call.State.INCOMING) {
+ if (callStateLabel != null) {
+ getView().announceForAccessibility(callStateLabel);
+ }
+ if (mPrimaryName.getText() != null) {
+ getView().announceForAccessibility(mPrimaryName.getText());
+ }
+ }
}
@Override
@@ -702,12 +755,15 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
}
@Override
- public void setPrimaryCallElapsedTime(boolean show, String callTimeElapsed) {
+ public void setPrimaryCallElapsedTime(boolean show, long duration) {
if (show) {
if (mElapsedTime.getVisibility() != View.VISIBLE) {
AnimUtils.fadeIn(mElapsedTime, AnimUtils.DEFAULT_DURATION);
}
+ String callTimeElapsed = DateUtils.formatElapsedTime(duration / 1000);
+ String durationDescription = InCallDateUtils.formatDetailedDuration(duration);
mElapsedTime.setText(callTimeElapsed);
+ mElapsedTime.setContentDescription(durationDescription);
} else {
// hide() animation has no effect if it is already hidden.
AnimUtils.fadeOut(mElapsedTime, AnimUtils.DEFAULT_DURATION);
@@ -716,16 +772,24 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
private void setDrawableToImageView(ImageView view, Drawable photo) {
if (photo == null) {
- photo = view.getResources().getDrawable(R.drawable.img_no_image);
- photo.setAutoMirrored(true);
+ photo = ContactInfoCache.getInstance(
+ view.getContext()).getDefaultContactPhotoDrawable();
}
+ if (mPrimaryPhotoDrawable == photo) {
+ return;
+ }
+ mPrimaryPhotoDrawable = photo;
+
final Drawable current = view.getDrawable();
if (current == null) {
view.setImageDrawable(photo);
AnimUtils.fadeIn(mElapsedTime, AnimUtils.DEFAULT_DURATION);
} else {
- InCallAnimationUtils.startCrossFade(view, current, photo);
+ // Cross fading is buggy and not noticable due to the multiple calls to this method
+ // that switch drawables in the middle of the cross-fade animations. Just set the
+ // photo directly instead.
+ view.setImageDrawable(photo);
view.setVisibility(View.VISIBLE);
}
}
@@ -838,13 +902,12 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mSecondaryCallName = (TextView) getView().findViewById(R.id.secondaryCallName);
mSecondaryCallConferenceCallIcon =
getView().findViewById(R.id.secondaryCallConferenceCallIcon);
- if (hasProvider) {
- mSecondaryCallProviderInfo.setVisibility(View.VISIBLE);
- mSecondaryCallProviderLabel = (TextView) getView()
- .findViewById(R.id.secondaryCallProviderLabel);
- mSecondaryCallProviderIcon = (ImageView) getView()
- .findViewById(R.id.secondaryCallProviderIcon);
- }
+ }
+
+ if (mSecondaryCallProviderLabel == null && hasProvider) {
+ mSecondaryCallProviderInfo.setVisibility(View.VISIBLE);
+ mSecondaryCallProviderLabel = (TextView) getView()
+ .findViewById(R.id.secondaryCallProviderLabel);
}
}
@@ -918,6 +981,22 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
return mManageConferenceCallButton.getVisibility() == View.VISIBLE;
}
+ /**
+ * Get the overall InCallUI background colors and apply to call card.
+ */
+ public void updateColors() {
+ MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors();
+
+ if (mCurrentThemeColors != null && mCurrentThemeColors.equals(themeColors)) {
+ return;
+ }
+
+ mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor);
+ mCallButtonsContainer.setBackgroundColor(themeColors.mPrimaryColor);
+
+ mCurrentThemeColors = themeColors;
+ }
+
private void dispatchPopulateAccessibilityEvent(AccessibilityEvent event, View view) {
if (view == null) return;
final List<CharSequence> eventText = event.getText();
@@ -929,9 +1008,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
}
}
- public void animateForNewOutgoingCall(Point touchPoint) {
+ public void animateForNewOutgoingCall(final Point touchPoint,
+ final boolean showCircularReveal) {
final ViewGroup parent = (ViewGroup) mPrimaryCallCardContainer.getParent();
- final Point startPoint = touchPoint;
final ViewTreeObserver observer = getView().getViewTreeObserver();
@@ -962,19 +1041,16 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
mCallTypeLabel.setAlpha(0);
mCallNumberAndLabel.setAlpha(0);
- final Animator revealAnimator = getRevealAnimator(startPoint);
- final Animator shrinkAnimator =
- getShrinkAnimator(parent.getHeight(), originalHeight);
+ final Animator animator = getOutgoingCallAnimator(touchPoint,
+ parent.getHeight(), originalHeight, showCircularReveal);
- mAnimatorSet = new AnimatorSet();
- mAnimatorSet.playSequentially(revealAnimator, shrinkAnimator);
- mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setViewStatePostAnimation(listener);
}
});
- mAnimatorSet.start();
+ animator.start();
}
});
}
@@ -1030,6 +1106,8 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
updateFabPosition();
}
});
+
+ updateColors();
}
/**
@@ -1116,6 +1194,21 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
return valueAnimator;
}
+ private Animator getOutgoingCallAnimator(Point touchPoint, int startHeight, int endHeight,
+ boolean showCircularReveal) {
+
+ final Animator shrinkAnimator = getShrinkAnimator(startHeight, endHeight);
+
+ if (!showCircularReveal) {
+ return shrinkAnimator;
+ }
+
+ final Animator revealAnimator = getRevealAnimator(touchPoint);
+ final AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playSequentially(revealAnimator, shrinkAnimator);
+ return animatorSet;
+ }
+
private void assignTranslateAnimation(View view, int offset) {
view.setTranslationY(mTranslationOffset * offset);
view.animate().translationY(0).alpha(1).withLayer()
diff --git a/src/com/android/incallui/CallCardPresenter.java b/src/com/android/incallui/CallCardPresenter.java
index 39678611..07eca4ec 100644
--- a/src/com/android/incallui/CallCardPresenter.java
+++ b/src/com/android/incallui/CallCardPresenter.java
@@ -27,7 +27,6 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.telecom.DisconnectCause;
-import android.telecom.PhoneCapabilities;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.StatusHints;
@@ -37,7 +36,6 @@ import android.telephony.PhoneNumberUtils;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;
import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
@@ -76,7 +74,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
private ContactCacheEntry mSecondaryContactInfo;
private CallTimer mCallTimer;
private Context mContext;
- private TelecomManager mTelecomManager;
public static class ContactLookupCallback implements ContactInfoCacheCallback {
private final WeakReference<CallCardPresenter> mCallCardPresenter;
@@ -240,7 +237,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
} else {
Log.d(this, "Canceling the calltime timer");
mCallTimer.cancel();
- ui.setPrimaryCallElapsedTime(false, null);
+ ui.setPrimaryCallElapsedTime(false, 0);
}
// Set the call state
@@ -294,8 +291,10 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
updatePrimaryCallState();
- if (call.can(PhoneCapabilities.MANAGE_CONFERENCE) != PhoneCapabilities.can(
- details.getCallCapabilities(), PhoneCapabilities.MANAGE_CONFERENCE)) {
+ if (call.can(android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE) !=
+ android.telecom.Call.Details.can(
+ details.getCallCapabilities(),
+ android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE)) {
maybeShowManageConferenceCallButton();
}
}
@@ -306,8 +305,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
// number directly from the telephony layer).
PhoneAccountHandle accountHandle = mPrimary.getAccountHandle();
if (accountHandle != null) {
- TelecomManager mgr =
- (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+ TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
PhoneAccount account = mgr.getPhoneAccount(accountHandle);
if (account != null) {
return getNumberFromHandle(account.getSubscriptionAddress());
@@ -324,7 +322,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
mPrimary.getSessionModificationState(),
mPrimary.getDisconnectCause(),
getConnectionLabel(),
- getConnectionIcon(),
+ getCallStateIcon(),
getGatewayNumber(),
mPrimary.isWaitingForRemoteSide());
setCallbackNumber();
@@ -349,7 +347,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
return false;
}
- return mPrimary.can(PhoneCapabilities.MANAGE_CONFERENCE) && !mPrimary.isVideoCall(mContext);
+ return mPrimary.can(android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE) &&
+ !mPrimary.isVideoCall(mContext);
}
private void setCallbackNumber() {
@@ -369,9 +368,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
}
}
- TelephonyManager telephonyManager =
- (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
- String simNumber = telephonyManager.getLine1Number();
+ TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
+ String simNumber = mgr.getLine1Number(mPrimary.getAccountHandle());
if (PhoneNumberUtils.compare(callbackNumber, simNumber)) {
Log.d(this, "Numbers are the same; not showing the callback number");
callbackNumber = null;
@@ -385,13 +383,13 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
if (ui == null || mPrimary == null || mPrimary.getState() != Call.State.ACTIVE) {
if (ui != null) {
- ui.setPrimaryCallElapsedTime(false, null);
+ ui.setPrimaryCallElapsedTime(false, 0);
}
mCallTimer.cancel();
} else {
final long callStart = mPrimary.getConnectTimeMillis();
final long duration = System.currentTimeMillis() - callStart;
- ui.setPrimaryCallElapsedTime(true, DateUtils.formatElapsedTime(duration / 1000));
+ ui.setPrimaryCallElapsedTime(true, duration);
}
}
@@ -598,7 +596,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
if (mSecondary == null) {
// Clear the secondary display info.
- ui.setSecondary(false, null, false, null, null, null, false /* isConference */);
+ ui.setSecondary(false, null, false, null, null, false /* isConference */);
return;
}
@@ -609,7 +607,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
false /* nameIsNumber */,
null /* label */,
getCallProviderLabel(mSecondary),
- getCallProviderIcon(mSecondary),
true /* isConference */);
} else if (mSecondaryContactInfo != null) {
Log.d(TAG, "updateSecondaryDisplayInfo() " + mSecondaryContactInfo);
@@ -621,11 +618,10 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
nameIsNumber,
mSecondaryContactInfo.label,
getCallProviderLabel(mSecondary),
- getCallProviderIcon(mSecondary),
false /* isConference */);
} else {
// Clear the secondary display info.
- ui.setSecondary(false, null, false, null, null, null, false /* isConference */);
+ ui.setSecondary(false, null, false, null, null, false /* isConference */);
}
}
@@ -638,7 +634,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
if (accountHandle == null) {
return null;
}
- return getTelecomManager().getPhoneAccount(accountHandle);
+ return InCallPresenter.getInstance().getTelecomManager().getPhoneAccount(accountHandle);
}
/**
@@ -652,30 +648,13 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
}
/**
- * Return the Drawable object of the icon to display to the left of the connection label.
- */
- private Drawable getCallProviderIcon(Call call) {
- PhoneAccount account = getAccountForCall(call);
-
- // on MSIM devices irrespective of number of enabled phone
- // accounts pick icon from phone account and display on UI
- if (account != null && (getTelecomManager().hasMultipleCallCapableAccounts()
- || (CallList.PHONE_COUNT > 1))) {
- return account.createIconDrawable(mContext);
- }
- return null;
- }
-
- /**
* Return the string label to represent the call provider
*/
private String getCallProviderLabel(Call call) {
PhoneAccount account = getAccountForCall(call);
-
- // on MSIM devices irrespective of number of
- // enabled phone accounts display label info on UI
- if (account != null && (getTelecomManager().hasMultipleCallCapableAccounts()
- || (CallList.PHONE_COUNT > 1))) {
+ TelecomManager mgr = InCallPresenter.getInstance().getTelecomManager();
+ if (account != null && !TextUtils.isEmpty(account.getLabel())
+ && mgr.hasMultipleCallCapableAccounts()) {
return account.getLabel().toString();
}
return null;
@@ -706,7 +685,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
return getCallProviderLabel(mPrimary);
}
- private Drawable getConnectionIcon() {
+ private Drawable getCallStateIcon() {
+ // Return connection icon if one exists.
StatusHints statusHints = mPrimary.getTelecommCall().getDetails().getStatusHints();
if (statusHints != null && statusHints.getIconResId() != 0) {
Drawable icon = statusHints.getIcon(mContext);
@@ -714,7 +694,15 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
return icon;
}
}
- return getCallProviderIcon(mPrimary);
+
+ // Return high definition audio icon if the capability is indicated.
+ if (mPrimary.getTelecommCall().getDetails().can(
+ android.telecom.Call.Details.CAPABILITY_HIGH_DEF_AUDIO)
+ && mPrimary.getState() == Call.State.ACTIVE) {
+ return mContext.getResources().getDrawable(R.drawable.ic_hd_audio);
+ }
+
+ return null;
}
private boolean hasOutgoingGatewayCall() {
@@ -754,7 +742,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
public void secondaryInfoClicked() {
if (mSecondary == null) {
- Log.wtf(this, "Secondary info clicked but no secondary call.");
+ Log.w(this, "Secondary info clicked but no secondary call.");
return;
}
@@ -791,16 +779,9 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
ui.setCallCardVisible(!isFullScreenVideo);
}
- private TelecomManager getTelecomManager() {
- if (mTelecomManager == null) {
- mTelecomManager =
- (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
- }
- return mTelecomManager;
- }
-
private String getConferenceString(Call call) {
- boolean isGenericConference = call.can(PhoneCapabilities.GENERIC_CONFERENCE);
+ boolean isGenericConference = call.can(
+ android.telecom.Call.Details.CAPABILITY_GENERIC_CONFERENCE);
Log.v(this, "getConferenceString: " + isGenericConference);
final int resId = isGenericConference
@@ -809,7 +790,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
}
private Drawable getConferencePhoto(Call call) {
- boolean isGenericConference = call.can(PhoneCapabilities.GENERIC_CONFERENCE);
+ boolean isGenericConference = call.can(
+ android.telecom.Call.Details.CAPABILITY_GENERIC_CONFERENCE);
Log.v(this, "getConferencePhoto: " + isGenericConference);
final int resId = isGenericConference
@@ -850,11 +832,11 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
String label, Drawable photo, boolean isSipCall,
String nickName, String organization, String position, String city);
void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
- String providerLabel, Drawable providerIcon, boolean isConference);
+ String providerLabel, boolean isConference);
void setCallState(int state, int videoState, int sessionModificationState,
DisconnectCause disconnectCause, String connectionLabel,
Drawable connectionIcon, String gatewayNumber, boolean isWaitingForRemoteSide);
- void setPrimaryCallElapsedTime(boolean show, String duration);
+ void setPrimaryCallElapsedTime(boolean show, long duration);
void setPrimaryName(String name, boolean nameIsNumber);
void setPrimaryImage(Drawable image);
void setPrimaryPhoneNumber(String phoneNumber);
diff --git a/src/com/android/incallui/CallList.java b/src/com/android/incallui/CallList.java
index cee69f52..50313035 100644
--- a/src/com/android/incallui/CallList.java
+++ b/src/com/android/incallui/CallList.java
@@ -99,8 +99,6 @@ public class CallList implements InCallPhoneListener {
public void onCallRemoved(Phone phone, android.telecom.Call telecommCall) {
if (mCallByTelecommCall.containsKey(telecommCall)) {
Call call = mCallByTelecommCall.get(telecommCall);
- call.setState(Call.State.DISCONNECTED);
- call.setDisconnectCause(new DisconnectCause(DisconnectCause.UNKNOWN));
if (updateCallInMap(call)) {
Log.w(this, "Removing call not previously disconnected " + call.getId());
}
@@ -485,7 +483,6 @@ public class CallList implements InCallPhoneListener {
if (call.getState() == Call.State.DISCONNECTED) {
// update existing (but do not add!!) disconnected calls
if (mCallById.containsKey(call.getId())) {
-
// 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.
diff --git a/src/com/android/incallui/CallerInfo.java b/src/com/android/incallui/CallerInfo.java
index c7940691..50dc7770 100644
--- a/src/com/android/incallui/CallerInfo.java
+++ b/src/com/android/incallui/CallerInfo.java
@@ -328,16 +328,10 @@ public class CallerInfo {
* @param context To lookup the localized 'Emergency Number' string.
* @return this instance.
*/
- // TODO: Note we're setting the phone number here (refer to
- // javadoc comments at the top of CallerInfo class) to a localized
- // string 'Emergency Number'. This is pretty bad because we are
- // making UI work here instead of just packaging the data. We
- // should set the phone number to the dialed number and name to
- // 'Emergency Number' and let the UI make the decision about what
- // should be displayed.
- /* package */ CallerInfo markAsEmergency(Context context, String number) {
- phoneNumber = context.getString(R.string.emergency_call_dialog_number_for_display) + " "
- + number;
+ /* package */ CallerInfo markAsEmergency(Context context) {
+ name = context.getString(R.string.emergency_call_dialog_number_for_display);
+ phoneNumber = null;
+
photoResource = R.drawable.img_phone;
mIsEmergency = true;
return this;
@@ -351,8 +345,6 @@ public class CallerInfo {
* set to null.
* @return this instance.
*/
- // TODO: As in the emergency number handling, we end up writing a
- // string in the phone number field.
/* package */ CallerInfo markAsVoiceMail(Context context) {
mIsVoiceMail = true;
@@ -360,7 +352,8 @@ public class CallerInfo {
// For voicemail calls, we display the voice mail tag
// instead of the real phone number in the "number"
// field.
- phoneNumber = TelephonyManagerUtils.getVoiceMailAlphaTag(context);
+ name = TelephonyManagerUtils.getVoiceMailAlphaTag(context);
+ phoneNumber = null;
} catch (SecurityException se) {
// Should never happen: if this process does not have
// permission to retrieve VM tag, it should not have
diff --git a/src/com/android/incallui/CallerInfoAsyncQuery.java b/src/com/android/incallui/CallerInfoAsyncQuery.java
index 8c5abe05..87174d71 100644
--- a/src/com/android/incallui/CallerInfoAsyncQuery.java
+++ b/src/com/android/incallui/CallerInfoAsyncQuery.java
@@ -250,7 +250,7 @@ public class CallerInfoAsyncQuery {
if (cw.event == EVENT_EMERGENCY_NUMBER) {
// Note we're setting the phone number here (refer to javadoc
// comments at the top of CallerInfo class).
- mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext, cw.number);
+ mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext);
} else if (cw.event == EVENT_VOICEMAIL_NUMBER) {
mCallerInfo = new CallerInfo().markAsVoiceMail(mQueryContext);
} else {
@@ -371,8 +371,7 @@ public class CallerInfoAsyncQuery {
// check to see if these are recognized numbers, and use shortcuts if we can.
if (PhoneNumberUtils.isLocalEmergencyNumber(context, info.phoneNumber)) {
cw.event = EVENT_EMERGENCY_NUMBER;
- } else if (info.isVoiceMailNumber()
- || PhoneNumberUtils.isVoiceMailNumber(subId, info.phoneNumber)) {
+ } else if (info.isVoiceMailNumber()) {
cw.event = EVENT_VOICEMAIL_NUMBER;
} else {
cw.event = EVENT_NEW_QUERY;
diff --git a/src/com/android/incallui/CallerInfoUtils.java b/src/com/android/incallui/CallerInfoUtils.java
index 9fd45e41..5693db09 100644
--- a/src/com/android/incallui/CallerInfoUtils.java
+++ b/src/com/android/incallui/CallerInfoUtils.java
@@ -6,6 +6,7 @@ import android.content.Loader.OnLoadCompleteListener;
import android.net.Uri;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.util.Log;
@@ -74,14 +75,22 @@ public class CallerInfoUtils {
// Because the InCallUI is immediately launched before the call is connected, occasionally
// a voicemail call will be passed to InCallUI as a "voicemail:" URI without a number.
// This call should still be handled as a voicemail call.
- if (call.getHandle() != null &&
- PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())) {
+ if ((call.getHandle() != null &&
+ PhoneAccount.SCHEME_VOICEMAIL.equals(call.getHandle().getScheme())) ||
+ isVoiceMailNumber(context, call)) {
info.markAsVoiceMail(context);
}
return info;
}
+ public static boolean isVoiceMailNumber(Context context, Call call) {
+ TelecomManager telecomManager =
+ (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+ return telecomManager.isVoiceMailNumber(
+ call.getTelecommCall().getDetails().getAccountHandle(), call.getNumber());
+ }
+
/**
* Handles certain "corner cases" for CNAP. When we receive weird phone numbers
* from the network to indicate different number presentations, convert them to
diff --git a/src/com/android/incallui/CircularRevealActivity.java b/src/com/android/incallui/CircularRevealActivity.java
new file mode 100644
index 00000000..7a9b7ccb
--- /dev/null
+++ b/src/com/android/incallui/CircularRevealActivity.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.android.incallui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Outline;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.support.v4.content.LocalBroadcastManager;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewOutlineProvider;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+
+import com.android.contacts.common.interactions.TouchPointManager;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+
+/**
+ * Lightweight activity used to display a circular reveal while InCallActivity is starting up.
+ * A BroadcastReceiver is used to listen to broadcasts from a LocalBroadcastManager to finish
+ * the activity at suitable times.
+ */
+public class CircularRevealActivity extends Activity {
+ private static final int REVEAL_DURATION = 333;
+ public static final String EXTRA_THEME_COLORS = "extra_theme_colors";
+ public static final String ACTION_CLEAR_DISPLAY = "action_clear_display";
+
+ final BroadcastReceiver mClearDisplayReceiver = new BroadcastReceiver( ) {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ clearDisplay();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ overridePendingTransition(0, 0);
+ setContentView(R.layout.outgoing_call_animation);
+ final Point touchPoint = getIntent().getParcelableExtra(TouchPointManager.TOUCH_POINT);
+ final MaterialPalette palette = getIntent().getParcelableExtra(EXTRA_THEME_COLORS);
+ setupDecorView(touchPoint, palette);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (!InCallPresenter.getInstance().isServiceBound()) {
+ clearDisplay();
+ }
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_CLEAR_DISPLAY);
+ LocalBroadcastManager.getInstance(this).registerReceiver(mClearDisplayReceiver, filter);
+ }
+
+ @Override
+ protected void onStop() {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mClearDisplayReceiver);
+ super.onStop();
+ }
+
+ private void setupDecorView(final Point touchPoint, MaterialPalette palette) {
+ final View view = getWindow().getDecorView();
+
+ // The circle starts from an initial size of 0 so clip it such that it is invisible. When
+ // the animation later starts, this clip will be clobbered by the circular reveal clip.
+ // See ViewAnimationUtils.createCircularReveal.
+ view.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ // Using (0, 0, 0, 0) will not work since the outline will simply be treated as
+ // an empty outline.
+ outline.setOval(-1, -1, 0, 0);
+ }
+ });
+ view.setClipToOutline(true);
+
+ if (palette != null) {
+ view.findViewById(R.id.outgoing_call_animation_circle).setBackgroundColor(
+ palette.mPrimaryColor);
+ getWindow().setStatusBarColor(palette.mSecondaryColor);
+ }
+
+ view.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ final ViewTreeObserver vto = view.getViewTreeObserver();
+ if (vto.isAlive()) {
+ vto.removeOnPreDrawListener(this);
+ }
+ final Animator animator = getRevealAnimator(touchPoint);
+ // Since this animator is a RenderNodeAnimator (native animator), add an arbitary
+ // start delay to force the onAnimationStart callback to happen later on the UI
+ // thread. Otherwise it would happen right away inside animator.start()
+ animator.setStartDelay(5);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ InCallPresenter.getInstance().onCircularRevealStarted(
+ CircularRevealActivity.this);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipToOutline(false);
+ super.onAnimationEnd(animation);
+ }
+ });
+ animator.start();
+ return false;
+ }
+ });
+ }
+
+ private void clearDisplay() {
+ getWindow().getDecorView().setVisibility(View.INVISIBLE);
+ finish();
+ }
+
+ @Override
+ public void onBackPressed() {
+ return;
+ }
+
+ public static void sendClearDisplayBroadcast(Context context) {
+ LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(ACTION_CLEAR_DISPLAY));
+ }
+
+ private Animator getRevealAnimator(Point touchPoint) {
+ final View view = getWindow().getDecorView();
+ final Display display = getWindowManager().getDefaultDisplay();
+ final Point size = new Point();
+ display.getSize(size);
+
+ int startX = size.x / 2;
+ int startY = size.y / 2;
+ if (touchPoint != null) {
+ startX = touchPoint.x;
+ startY = touchPoint.y;
+ }
+
+ final Animator valueAnimator = ViewAnimationUtils.createCircularReveal(view,
+ startX, startY, 0, Math.max(size.x, size.y));
+ valueAnimator.setDuration(REVEAL_DURATION);
+ return valueAnimator;
+ }
+}
diff --git a/src/com/android/incallui/ConferenceManagerFragment.java b/src/com/android/incallui/ConferenceManagerFragment.java
index efdea28b..72182867 100644
--- a/src/com/android/incallui/ConferenceManagerFragment.java
+++ b/src/com/android/incallui/ConferenceManagerFragment.java
@@ -94,6 +94,9 @@ public class ConferenceManagerFragment
final CallList calls = CallList.getInstance();
getPresenter().init(getActivity(), calls);
getView().setVisibility(View.VISIBLE);
+ // Request focus on the list of participants for accessibility purposes. This ensures
+ // that once the list of participants is shown, the first participant is announced.
+ mConferenceParticipantList.requestFocus();
} else {
getView().setVisibility(View.GONE);
diff --git a/src/com/android/incallui/ConferenceManagerPresenter.java b/src/com/android/incallui/ConferenceManagerPresenter.java
index 7acd94e3..8c41424a 100644
--- a/src/com/android/incallui/ConferenceManagerPresenter.java
+++ b/src/com/android/incallui/ConferenceManagerPresenter.java
@@ -17,14 +17,12 @@
package com.android.incallui;
import android.content.Context;
-import android.net.Uri;
-import android.telecom.PhoneCapabilities;
-import android.text.TextUtils;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;
import com.android.incallui.InCallPresenter.InCallDetailsListener;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.InCallPresenter.IncomingCallListener;
import com.google.common.base.Preconditions;
@@ -36,7 +34,7 @@ import java.util.List;
*/
public class ConferenceManagerPresenter
extends Presenter<ConferenceManagerPresenter.ConferenceManagerUi>
- implements InCallStateListener, InCallDetailsListener {
+ implements InCallStateListener, InCallDetailsListener, IncomingCallListener {
private Context mContext;
@@ -46,6 +44,7 @@ public class ConferenceManagerPresenter
// register for call state changes last
InCallPresenter.getInstance().addListener(this);
+ InCallPresenter.getInstance().addIncomingCallListener(this);
}
@Override
@@ -53,6 +52,7 @@ public class ConferenceManagerPresenter
super.onUiUnready(ui);
InCallPresenter.getInstance().removeListener(this);
+ InCallPresenter.getInstance().removeIncomingCallListener(this);
}
@Override
@@ -66,29 +66,40 @@ public class ConferenceManagerPresenter
String.valueOf(call.getChildCallIds().size()));
update(callList);
} else {
- getUi().setVisible(false);
+ InCallPresenter.getInstance().showConferenceCallManager(false);
}
} else {
- getUi().setVisible(false);
+ InCallPresenter.getInstance().showConferenceCallManager(false);
}
}
}
@Override
public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
- boolean canDisconnect = PhoneCapabilities.can(
- details.getCallCapabilities(), PhoneCapabilities.DISCONNECT_FROM_CONFERENCE);
- boolean canSeparate = PhoneCapabilities.can(
- details.getCallCapabilities(), PhoneCapabilities.SEPARATE_FROM_CONFERENCE);
-
- if (call.can(PhoneCapabilities.DISCONNECT_FROM_CONFERENCE) != canDisconnect
- || call.can(PhoneCapabilities.SEPARATE_FROM_CONFERENCE) != canSeparate) {
+ boolean canDisconnect = details.can(
+ android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
+ boolean canSeparate = details.can(
+ android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
+
+ if (call.can(android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE)
+ != canDisconnect
+ || call.can(android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE)
+ != canSeparate) {
getUi().refreshCall(call);
}
- if (!PhoneCapabilities.can(
- details.getCallCapabilities(), PhoneCapabilities.MANAGE_CONFERENCE)) {
- getUi().setVisible(false);
+ if (!details.can(
+ android.telecom.Call.Details.CAPABILITY_MANAGE_CONFERENCE)) {
+ InCallPresenter.getInstance().showConferenceCallManager(false);
+ }
+ }
+
+ @Override
+ public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
+ // When incoming call exists, set conference ui invisible.
+ if (getUi().isFragmentVisible()) {
+ Log.d(this, "onIncomingCall()... Conference ui is showing, hide it.");
+ InCallPresenter.getInstance().showConferenceCallManager(false);
}
}
@@ -117,9 +128,8 @@ public class ConferenceManagerPresenter
Log.d(this, "Number of calls is " + String.valueOf(calls.size()));
- // Users can split out a call from the conference call if there either the active call
- // or the holding call is empty. If both are filled at the moment, users can not split out
- // another call.
+ // Users can split out a call from the conference call if either the active call or the
+ // holding call is empty. If both are filled, users can not split out another call.
final boolean hasActiveCall = (callList.getActiveCall() != null);
final boolean hasHoldingCall = (callList.getBackgroundCall() != null);
boolean canSeparate = !(hasActiveCall && hasHoldingCall);
diff --git a/src/com/android/incallui/ConferenceParticipantListAdapter.java b/src/com/android/incallui/ConferenceParticipantListAdapter.java
index 641261e0..f03b9f05 100644
--- a/src/com/android/incallui/ConferenceParticipantListAdapter.java
+++ b/src/com/android/incallui/ConferenceParticipantListAdapter.java
@@ -19,6 +19,7 @@ package com.android.incallui;
import android.content.Context;
import android.net.Uri;
import android.telecom.PhoneCapabilities;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -336,11 +337,10 @@ public class ConferenceParticipantListAdapter extends BaseAdapter {
new ContactLookupCallback(this));
}
- int callCapabilities = call.getTelecommCall().getDetails().getCallCapabilities();
- boolean thisRowCanSeparate = mParentCanSeparate && PhoneCapabilities.can(
- callCapabilities, PhoneCapabilities.SEPARATE_FROM_CONFERENCE);
- boolean thisRowCanDisconnect = PhoneCapabilities.can(
- callCapabilities, PhoneCapabilities.DISCONNECT_FROM_CONFERENCE);
+ boolean thisRowCanSeparate = mParentCanSeparate && call.getTelecommCall().getDetails().can(
+ android.telecom.Call.Details.CAPABILITY_SEPARATE_FROM_CONFERENCE);
+ boolean thisRowCanDisconnect = call.getTelecommCall().getDetails().can(
+ android.telecom.Call.Details.CAPABILITY_DISCONNECT_FROM_CONFERENCE);
setCallerInfoForRow(result, contactCache.name, contactCache.number, contactCache.label,
contactCache.lookupKey, contactCache.displayPhotoUri, thisRowCanSeparate,
@@ -420,7 +420,7 @@ public class ConferenceParticipantListAdapter extends BaseAdapter {
numberTypeTextView.setVisibility(View.GONE);
} else {
numberTextView.setVisibility(View.VISIBLE);
- numberTextView.setText(callerNumber);
+ numberTextView.setText(PhoneNumberUtils.ttsSpanAsPhoneNumber(callerNumber));
numberTypeTextView.setVisibility(View.VISIBLE);
numberTypeTextView.setText(callerNumberType);
}
diff --git a/src/com/android/incallui/ContactInfoCache.java b/src/com/android/incallui/ContactInfoCache.java
index 0570ee2d..b953099b 100644
--- a/src/com/android/incallui/ContactInfoCache.java
+++ b/src/com/android/incallui/ContactInfoCache.java
@@ -70,6 +70,9 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
private static ContactInfoCache sCache = null;
+ private Drawable mDefaultContactPhotoDrawable;
+ private Drawable mConferencePhotoDrawable;
+
public static synchronized ContactInfoCache getInstance(Context mContext) {
if (sCache == null) {
sCache = new ContactInfoCache(mContext.getApplicationContext());
@@ -170,12 +173,10 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
}
ContactCacheEntry cacheEntry = mInfoMap.get(callId);
- // Rebuild the entry from the new data if:
- // 1) This is NOT the asynchronous local lookup (IOW, this is the first pass)
- // 2) The local lookup was done and the contact exists
- // 3) The existing cached entry is empty (no name).
- if (!didLocalLookup || callerInfo.contactExists ||
- (cacheEntry != null && TextUtils.isEmpty(cacheEntry.name))) {
+ // Ensure we always have a cacheEntry. Replace the existing entry if
+ // it has no name or if we found a local contact.
+ if (cacheEntry == null || TextUtils.isEmpty(cacheEntry.name) ||
+ callerInfo.contactExists) {
cacheEntry = buildEntry(mContext, callId, callerInfo, presentationMode, isIncoming);
mInfoMap.put(callId, cacheEntry);
}
@@ -183,8 +184,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
sendInfoNotifications(callId, cacheEntry);
if (didLocalLookup) {
-
- // Before issuing a request for more data from other services, We only check that the
+ // Before issuing a request for more data from other services, we only check that the
// contact wasn't found in the local DB. We don't check the if the cache entry already
// has a name because we allow overriding cnap data with data from other services.
if (!callerInfo.contactExists && cacheEntry.name == null) {
@@ -334,12 +334,10 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
if (info.cachedPhoto != null) {
photo = info.cachedPhoto;
} else {
- photo = context.getResources().getDrawable(R.drawable.img_no_image);
- photo.setAutoMirrored(true);
+ photo = getDefaultContactPhotoDrawable();
}
} else if (info.contactDisplayPhotoUri == null) {
- photo = context.getResources().getDrawable(R.drawable.img_no_image);
- photo.setAutoMirrored(true);
+ photo = getDefaultContactPhotoDrawable();
} else {
cce.displayPhotoUri = info.contactDisplayPhotoUri;
}
@@ -513,6 +511,22 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
return name;
}
+ public Drawable getDefaultContactPhotoDrawable() {
+ if (mDefaultContactPhotoDrawable == null) {
+ mDefaultContactPhotoDrawable =
+ mContext.getResources().getDrawable(R.drawable.img_no_image_automirrored);
+ }
+ return mDefaultContactPhotoDrawable;
+ }
+
+ public Drawable getConferenceDrawable() {
+ if (mConferencePhotoDrawable == null) {
+ mConferencePhotoDrawable =
+ mContext.getResources().getDrawable(R.drawable.img_conference_automirrored);
+ }
+ return mConferencePhotoDrawable;
+ }
+
/**
* Callback interface for the contact query.
*/
diff --git a/src/com/android/incallui/DialpadFragment.java b/src/com/android/incallui/DialpadFragment.java
index 6cb5d1f8..da3f0cb7 100644
--- a/src/com/android/incallui/DialpadFragment.java
+++ b/src/com/android/incallui/DialpadFragment.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.telephony.PhoneNumberUtils;
import android.text.Editable;
import android.text.method.DialerKeyListener;
import android.util.AttributeSet;
@@ -34,6 +35,7 @@ import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.dialpad.DialpadView;
@@ -48,6 +50,10 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
private static final int ACCESSIBILITY_DTMF_STOP_DELAY_MILLIS = 50;
+ private final int[] mButtonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three,
+ R.id.four, R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star,
+ R.id.pound};
+
/**
* LinearLayout with getter and setter methods for the translationY property using floats,
* for animation purposes.
@@ -132,6 +138,8 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
private DialpadView mDialpadView;
+ private int mCurrentTextColor;
+
/**
* Our own key listener, specialized for dealing with DTMF codes.
* 1. Ignore the backspace since it is irrelevant.
@@ -457,13 +465,35 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
// the edit (copy / paste / select) functions.
mDtmfDialerField.setLongClickable(false);
mDtmfDialerField.setElegantTextHeight(false);
- configureKeypadListeners(mDialpadView);
+ configureKeypadListeners();
}
return parent;
}
@Override
+ public void onResume() {
+ super.onResume();
+ updateColors();
+ }
+
+ public void updateColors() {
+ int textColor = InCallPresenter.getInstance().getThemeColors().mPrimaryColor;
+
+ if (mCurrentTextColor == textColor) {
+ return;
+ }
+
+ DialpadKeyButton dialpadKey;
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
+ ((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);
+ }
+
+ mCurrentTextColor = textColor;
+ }
+
+ @Override
public void onDestroyView() {
mDialerKeyListener = null;
super.onDestroyView();
@@ -484,7 +514,7 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
* @param text Text to set Dialpad EditText to.
*/
public void setDtmfText(String text) {
- mDtmfDialerField.setText(text);
+ mDtmfDialerField.setText(PhoneNumberUtils.ttsSpanAsPhoneNumber(text));
}
@Override
@@ -544,12 +574,10 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
}
}
- private void configureKeypadListeners(View fragmentView) {
- final int[] buttonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
- R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star, R.id.pound};
+ private void configureKeypadListeners() {
DialpadKeyButton dialpadKey;
- for (int i = 0; i < buttonIds.length; i++) {
- dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
dialpadKey.setOnTouchListener(this);
dialpadKey.setOnKeyListener(this);
dialpadKey.setOnHoverListener(this);
diff --git a/src/com/android/incallui/InCallActivity.java b/src/com/android/incallui/InCallActivity.java
index c37d8080..fc70f6c5 100644
--- a/src/com/android/incallui/InCallActivity.java
+++ b/src/com/android/incallui/InCallActivity.java
@@ -34,12 +34,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.TypedArray;
+import android.content.res.Resources;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
import android.telecom.DisconnectCause;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -57,6 +58,10 @@ import android.widget.TextView;
import com.android.phone.common.animation.AnimUtils;
import com.android.phone.common.animation.AnimationListenerAdapter;
import com.android.contacts.common.interactions.TouchPointManager;
+import com.android.contacts.common.util.MaterialColorMapUtils;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
import com.android.incallui.Call.State;
import java.util.ArrayList;
@@ -70,11 +75,12 @@ public class InCallActivity extends Activity {
public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad";
public static final String DIALPAD_TEXT_EXTRA = "InCallActivity.dialpad_text";
- public static final String NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
private static final String ACTION_SUPP_SERVICE_FAILURE =
"org.codeaurora.ACTION_SUPP_SERVICE_FAILURE";
private static final String EXTRA_RESULT = "result";
+ public static final String NEW_OUTGOING_CALL_EXTRA = "InCallActivity.new_outgoing_call";
+ public static final String SHOW_CIRCULAR_REVEAL_EXTRA = "InCallActivity.show_circular_reveal";
private CallButtonFragment mCallButtonFragment;
private CallCardFragment mCallCardFragment;
@@ -110,6 +116,7 @@ public class InCallActivity extends Activity {
private Tab[] mDsdaTab = new Tab[TAB_COUNT_TWO];
private boolean[] mDsdaTabAdd = {false, false};
+ private boolean mDismissKeyguard = false;
AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() {
@Override
@@ -216,6 +223,7 @@ public class InCallActivity extends Activity {
if (mDialpadFragment != null) {
out.putString(DIALPAD_TEXT_EXTRA, mDialpadFragment.getDtmfText());
}
+ super.onSaveInstanceState(out);
}
@Override
@@ -238,6 +246,8 @@ public class InCallActivity extends Activity {
super.onResume();
mIsForegroundActivity = true;
+
+ InCallPresenter.getInstance().setThemeColors();
InCallPresenter.getInstance().onUiShowing(true);
if (mShowDialpadRequested) {
@@ -271,6 +281,9 @@ public class InCallActivity extends Activity {
}
InCallPresenter.getInstance().onUiShowing(false);
+ if (isFinishing()) {
+ InCallPresenter.getInstance().unsetActivity(this);
+ }
}
@Override
@@ -285,10 +298,7 @@ public class InCallActivity extends Activity {
@Override
protected void onDestroy() {
Log.d(this, "onDestroy()... this = " + this);
-
- InCallPresenter.getInstance().updateIsChangingConfigurations();
- InCallPresenter.getInstance().setActivity(null);
- unregisterReceiver(mReceiver);
+ InCallPresenter.getInstance().unsetActivity(this);
super.onDestroy();
}
@@ -351,12 +361,12 @@ public class InCallActivity extends Activity {
@Override
public void onBackPressed() {
- Log.d(this, "onBackPressed()...");
+ Log.i(this, "onBackPressed");
// BACK is also used to exit out of any "special modes" of the
// in-call UI:
- if (!mCallCardFragment.isVisible()) {
+ if (!mConferenceManagerFragment.isVisible() && !mCallCardFragment.isVisible()) {
return;
}
@@ -365,7 +375,7 @@ public class InCallActivity extends Activity {
mCallButtonFragment.getPresenter().showDialpadClicked(false);
return;
} else if (mConferenceManagerFragment.isVisible()) {
- mConferenceManagerFragment.setVisible(false);
+ showConferenceCallManager(false);
return;
}
@@ -531,8 +541,8 @@ public class InCallActivity extends Activity {
relaunchedFromDialer(showDialpad);
}
- if (intent.getBooleanExtra(NEW_OUTGOING_CALL, false)) {
- intent.removeExtra(NEW_OUTGOING_CALL);
+ if (intent.getBooleanExtra(NEW_OUTGOING_CALL_EXTRA, false)) {
+ intent.removeExtra(NEW_OUTGOING_CALL_EXTRA);
Call call = CallList.getInstance().getOutgoingCall();
if (call == null) {
call = CallList.getInstance().getPendingOutgoingCall();
@@ -547,7 +557,6 @@ public class InCallActivity extends Activity {
extras = new Bundle();
}
-
Point touchPoint = null;
if (TouchPointManager.getInstance().hasValidPoint()) {
// Use the most immediate touch point in the InCallUi if available
@@ -558,23 +567,21 @@ public class InCallActivity extends Activity {
touchPoint = (Point) extras.getParcelable(TouchPointManager.TOUCH_POINT);
}
}
- mCallCardFragment.animateForNewOutgoingCall(touchPoint);
-
- /*
- * If both a phone account handle and a list of phone accounts to choose from are
- * missing, then disconnect the call because there is no way to place an outgoing
- * call.
- * The exception is emergency calls, which may be waiting for the ConnectionService
- * to set the PhoneAccount during the PENDING_OUTGOING state.
- */
- if (call != null && !isEmergencyCall(call)) {
- final List<PhoneAccountHandle> phoneAccountHandles = extras
- .getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
- if (call.getAccountHandle() == null &&
- (phoneAccountHandles == null || phoneAccountHandles.isEmpty())) {
- TelecomAdapter.getInstance().disconnectCall(call.getId());
- }
+
+ // This is only true in the case where an outgoing call is initiated by tapping
+ // on the "Select account dialog", in which case we skip the initial animation. In
+ // most other cases the circular reveal is done by OutgoingCallAnimationActivity.
+ final boolean showCircularReveal =
+ intent.getBooleanExtra(SHOW_CIRCULAR_REVEAL_EXTRA, false);
+ mCallCardFragment.animateForNewOutgoingCall(touchPoint, showCircularReveal);
+
+ // InCallActivity is responsible for disconnecting a new outgoing call if there
+ // is no way of making it (i.e. no valid call capable accounts)
+ if (InCallPresenter.isCallWithNoValidAccounts(call)) {
+ TelecomAdapter.getInstance().disconnectCall(call.getId());
}
+
+ dismissKeyguard(true);
}
Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall();
@@ -591,8 +598,22 @@ public class InCallActivity extends Activity {
phoneAccountHandles = new ArrayList<>();
}
+ SelectPhoneAccountListener listener = new SelectPhoneAccountListener() {
+ @Override
+ public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle,
+ boolean setDefault) {
+ InCallPresenter.getInstance().handleAccountSelection(selectedAccountHandle,
+ setDefault);
+ }
+ @Override
+ public void onDialogDismissed() {
+ InCallPresenter.getInstance().cancelAccountSelection();
+ }
+ };
+
SelectPhoneAccountDialogFragment.showAccountDialog(getFragmentManager(),
- phoneAccountHandles);
+ R.string.select_phone_account_for_calls, true, phoneAccountHandles,
+ listener);
} else {
mCallCardFragment.setVisible(true);
}
@@ -601,14 +622,6 @@ public class InCallActivity extends Activity {
}
}
- private boolean isEmergencyCall(Call call) {
- final Uri handle = call.getHandle();
- if (handle == null) {
- return false;
- }
- return PhoneNumberUtils.isEmergencyNumber(handle.getSchemeSpecificPart());
- }
-
private void relaunchedFromDialer(boolean showDialpad) {
mShowDialpadRequested = showDialpad;
mAnimateDialpadOnShow = true;
@@ -662,6 +675,10 @@ public class InCallActivity extends Activity {
}
public void dismissKeyguard(boolean dismiss) {
+ if (mDismissKeyguard == dismiss) {
+ return;
+ }
+ mDismissKeyguard = dismiss;
if (dismiss) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
} else {
@@ -716,8 +733,18 @@ public class InCallActivity extends Activity {
return mDialpadFragment != null && mDialpadFragment.isVisible();
}
- public void showConferenceCallManager() {
- mConferenceManagerFragment.setVisible(true);
+ /**
+ * Hides or shows the conference manager fragment.
+ *
+ * @param show {@code true} if the conference manager should be shown, {@code false} if it
+ * should be hidden.
+ */
+ public void showConferenceCallManager(boolean show) {
+ mConferenceManagerFragment.setVisible(show);
+
+ // Need to hide the call card fragment to ensure that accessibility service does not try to
+ // give focus to the call card when the conference manager is visible.
+ mCallCardFragment.getView().setVisibility(show ? View.GONE : View.VISIBLE);
}
public void showPostCharWaitDialog(String callId, String chars) {
@@ -838,7 +865,7 @@ public class InCallActivity extends Activity {
mDialog = new AlertDialog.Builder(this)
.setMessage(msg)
- .setPositiveButton(R.string.ok, new OnClickListener() {
+ .setPositiveButton(android.R.string.ok, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onDialogDismissed();
diff --git a/src/com/android/incallui/InCallDateUtils.java b/src/com/android/incallui/InCallDateUtils.java
new file mode 100644
index 00000000..88c5354c
--- /dev/null
+++ b/src/com/android/incallui/InCallDateUtils.java
@@ -0,0 +1,56 @@
+package com.android.incallui;
+
+import android.content.res.Resources;
+
+/**
+ * Methods to parse time and date information in the InCallUi
+ */
+public class InCallDateUtils {
+ public InCallDateUtils() {
+
+ }
+
+ /**
+ * Return given duration in a human-friendly format. For example, "4
+ * minutes 3 seconds" or "3 hours 1 second". Returns the hours, minutes and seconds in that
+ * order if they exist.
+ */
+ public static String formatDetailedDuration(long millis) {
+ int hours = 0;
+ int minutes = 0;
+ int seconds = 0;
+ int elapsedSeconds = (int) (millis / 1000);
+ if (elapsedSeconds >= 3600) {
+ hours = elapsedSeconds / 3600;
+ elapsedSeconds -= hours * 3600;
+ }
+ if (elapsedSeconds >= 60) {
+ minutes = elapsedSeconds / 60;
+ elapsedSeconds -= minutes * 60;
+ }
+ seconds = elapsedSeconds;
+
+ final Resources res = Resources.getSystem();
+ StringBuilder duration = new StringBuilder();
+ if (hours > 0) {
+ duration.append(res.getQuantityString(
+ com.android.internal.R.plurals.duration_hours, hours, hours));
+ }
+ if (minutes > 0) {
+ if (hours > 0) {
+ duration.append(' ');
+ }
+ duration.append(res.getQuantityString(
+ com.android.internal.R.plurals.duration_minutes, minutes, minutes));
+ }
+ if (seconds > 0) {
+ if (hours > 0 || minutes > 0) {
+ duration.append(' ');
+ }
+ duration.append(res.getQuantityString(
+ com.android.internal.R.plurals.duration_seconds, seconds, seconds));
+ }
+ return duration.toString();
+ }
+
+}
diff --git a/src/com/android/incallui/InCallPresenter.java b/src/com/android/incallui/InCallPresenter.java
index d26e48aa..743f667d 100644
--- a/src/com/android/incallui/InCallPresenter.java
+++ b/src/com/android/incallui/InCallPresenter.java
@@ -16,19 +16,22 @@
package com.android.incallui;
-import android.Manifest;
-import android.app.PendingIntent;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.content.pm.ActivityInfo;
+import android.graphics.Point;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccount;
-import android.telecom.PhoneCapabilities;
import android.telecom.Phone;
import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
+import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.Surface;
@@ -37,6 +40,9 @@ import android.view.Window;
import android.view.WindowManager;
import com.google.common.base.Preconditions;
+
+import com.android.contacts.common.interactions.TouchPointManager;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
import com.android.incalluibind.ObjectFactory;
import java.util.Collections;
@@ -60,6 +66,8 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
private static final String EXTRA_FIRST_TIME_SHOWN =
"com.android.incallui.intent.extra.FIRST_TIME_SHOWN";
+ private static final Bundle EMPTY_EXTRAS = new Bundle();
+
private static InCallPresenter sInCallPresenter;
/**
@@ -74,6 +82,8 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
new ConcurrentHashMap<InCallDetailsListener, Boolean>(8, 0.9f, 1));
private final Set<CanAddCallListener> mCanAddCallListeners = Collections.newSetFromMap(
new ConcurrentHashMap<CanAddCallListener, Boolean>(8, 0.9f, 1));
+ private final Set<InCallUiListener> mInCallUiListeners = Collections.newSetFromMap(
+ new ConcurrentHashMap<InCallUiListener, Boolean>(8, 0.9f, 1));
private final Set<InCallOrientationListener> mOrientationListeners = Collections.newSetFromMap(
new ConcurrentHashMap<InCallOrientationListener, Boolean>(8, 0.9f, 1));
private final Set<InCallEventListener> mInCallEventListeners = Collections.newSetFromMap(
@@ -159,9 +169,34 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
*/
private boolean mIsChangingConfigurations = false;
+ /**
+ * Whether or not to wait for the circular reveal animation to be started, to avoid stopping
+ * the circular reveal animation activity before the animation is initiated.
+ */
+ private boolean mWaitForRevealAnimationStart = false;
+
+ /**
+ * Whether or not the CircularRevealAnimationActivity has started.
+ */
+ private boolean mCircularRevealActivityStarted = false;
+
+ private boolean mShowDialpadOnStart = false;
+
+ /**
+ * Whether or not InCallService is bound to Telecom.
+ */
+ private boolean mServiceBound = false;
+
private Phone mPhone;
private int mLastDisconnectCause = DisconnectCause.ERROR;
+ private Handler mHandler = new Handler();
+
+ /** Display colors for the UI. Consists of a primary color and secondary (darker) color */
+ private MaterialPalette mThemeColors;
+
+ private TelecomManager mTelecomManager;
+
public static synchronized InCallPresenter getInstance() {
if (sInCallPresenter == null) {
sInCallPresenter = new InCallPresenter();
@@ -246,6 +281,13 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
private void attemptFinishActivity() {
+ mWaitForRevealAnimationStart = false;
+
+ Context context = mContext != null ? mContext : mInCallActivity;
+ if (context != null) {
+ CircularRevealActivity.sendClearDisplayBroadcast(context);
+ }
+
final boolean doFinish = (mInCallActivity != null && isActivityStarted());
Log.i(this, "Hide in call UI: " + doFinish);
@@ -270,11 +312,44 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
/**
- * 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
- * the tear-down process.
+ * Called when the UI begins, and starts the callstate callbacks if necessary.
*/
public void setActivity(InCallActivity inCallActivity) {
+ if (inCallActivity == null) {
+ throw new IllegalArgumentException("registerActivity cannot be called with null");
+ }
+ if (mInCallActivity != null && mInCallActivity != inCallActivity) {
+ Log.wtf(this, "Setting a second activity before destroying the first.");
+ }
+ updateActivity(inCallActivity);
+ }
+
+ /**
+ * Called when the UI ends. Attempts to tear down everything if necessary. See
+ * {@link #tearDown()} for more insight on the tear-down process.
+ */
+ public void unsetActivity(InCallActivity inCallActivity) {
+ if (inCallActivity == null) {
+ throw new IllegalArgumentException("unregisterActivity cannot be called with null");
+ }
+ if (mInCallActivity == null) {
+ Log.i(this, "No InCallActivity currently set, no need to unset.");
+ return;
+ }
+ if (mInCallActivity != inCallActivity) {
+ Log.w(this, "Second instance of InCallActivity is trying to unregister when another"
+ + " instance is active. Ignoring.");
+ return;
+ }
+ updateActivity(null);
+ }
+
+ /**
+ * Updates the current instance of {@link InCallActivity} with the provided one. If a
+ * {@code null} activity is provided, it means that the activity was finished and we should
+ * attempt to cleanup.
+ */
+ private void updateActivity(InCallActivity inCallActivity) {
boolean updateListeners = false;
boolean doAttemptCleanup = false;
@@ -282,8 +357,6 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
if (mInCallActivity == null) {
updateListeners = true;
Log.i(this, "UI Initialized");
- } else if (mInCallActivity != inCallActivity) {
- Log.wtf(this, "Setting a second activity before destroying the first.");
} else {
// since setActivity is called onStart(), it can be called multiple times.
// This is fine and ignorable, but we do not want to update the world every time
@@ -305,18 +378,18 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
// 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.");
+ Log.i(this, "UI Initialized, but no calls left. shut down.");
attemptFinishActivity();
return;
}
} else {
- Log.i(this, "UI Destroyed)");
+ Log.i(this, "UI Destroyed");
updateListeners = true;
mInCallActivity = null;
// We attempt cleanup for the destroy case but only after we recalculate the state
- // to see if we need to come back up or stay shut down. This is why we do the cleanup
- // after the call to onCallListChange() instead of directly here.
+ // to see if we need to come back up or stay shut down. This is why we do the
+ // cleanup after the call to onCallListChange() instead of directly here.
doAttemptCleanup = true;
}
@@ -705,7 +778,11 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
if (showing) {
mIsActivityPreviouslyStarted = true;
} else {
- updateIsChangingConfigurations();
+ CircularRevealActivity.sendClearDisplayBroadcast(mContext);
+ }
+
+ for (InCallUiListener listener : mInCallUiListeners) {
+ listener.onUiShowing(showing);
}
}
@@ -729,6 +806,14 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
}
+ public void addInCallUiListener(InCallUiListener listener) {
+ mInCallUiListeners.add(listener);
+ }
+
+ public boolean removeInCallUiListener(InCallUiListener listener) {
+ return mInCallUiListeners.remove(listener);
+ }
+
/**
* Brings the app into the foreground if possible.
*/
@@ -783,8 +868,10 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
if (activeCall != null) {
// TODO: This logic is repeated from CallButtonPresenter.java. We should
// consolidate this logic.
- final boolean canMerge = activeCall.can(PhoneCapabilities.MERGE_CONFERENCE);
- final boolean canSwap = activeCall.can(PhoneCapabilities.SWAP_CONFERENCE);
+ final boolean canMerge = activeCall.can(
+ android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
+ final boolean canSwap = activeCall.can(
+ android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
Log.v(this, "activeCall: " + activeCall + ", canMerge: " + canMerge +
", canSwap: " + canSwap);
@@ -806,7 +893,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
if (heldCall != null) {
// We have a hold call so presumeable it will always support HOLD...but
// there is no harm in double checking.
- final boolean canHold = heldCall.can(PhoneCapabilities.HOLD);
+ final boolean canHold = heldCall.can(android.telecom.Call.Details.CAPABILITY_HOLD);
Log.v(this, "heldCall: " + heldCall + ", canHold: " + canHold);
@@ -939,12 +1026,23 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
// This is different from the incoming call sequence because we do not need to shock the
// user with a top-level notification. Just show the call UI normally.
final boolean mainUiNotVisible = !isShowingInCallUi() || !getCallCardFragmentVisible();
- final boolean showCallUi = ((InCallState.PENDING_OUTGOING == newState ||
- InCallState.OUTGOING == newState) && mainUiNotVisible);
-
- // TODO: Can we be suddenly in a call without it having been in the outgoing or incoming
- // state? I havent seen that but if it can happen, the code below should be enabled.
- // showCallUi |= (InCallState.INCALL && !isActivityStarted());
+ boolean showCallUi = InCallState.OUTGOING == newState && mainUiNotVisible;
+
+ // Direct transition from PENDING_OUTGOING -> INCALL means that there was an error in the
+ // outgoing call process, so the UI should be brought up to show an error dialog.
+ showCallUi |= (InCallState.PENDING_OUTGOING == mInCallState
+ && InCallState.INCALL == newState && !isActivityStarted());
+
+ // Another exception - InCallActivity is in charge of disconnecting a call with no
+ // valid accounts set. Bring the UI up if this is true for the current pending outgoing
+ // call so that:
+ // 1) The call can be disconnected correctly
+ // 2) The UI comes up and correctly displays the error dialog.
+ // TODO: Remove these special case conditions by making InCallPresenter a true state
+ // machine. Telecom should also be the component responsible for disconnecting a call
+ // with no valid accounts.
+ showCallUi |= InCallState.PENDING_OUTGOING == newState && mainUiNotVisible
+ && isCallWithNoValidAccounts(CallList.getInstance().getPendingOutgoingCall());
// The only time that we have an instance of mInCallActivity and it isn't started is
// when it is being destroyed. In that case, lets avoid bringing up another instance of
@@ -983,6 +1081,43 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
/**
+ * Determines whether or not a call has no valid phone accounts that can be used to make the
+ * call with. Emergency calls do not require a phone account.
+ *
+ * @param call to check accounts for.
+ * @return {@code true} if the call has no call capable phone accounts set, {@code false} if
+ * the call contains a phone account that could be used to initiate it with, or is an emergency
+ * call.
+ */
+ public static boolean isCallWithNoValidAccounts(Call call) {
+ if (call != null && !isEmergencyCall(call)) {
+ Bundle extras = call.getTelecommCall().getDetails().getExtras();
+
+ if (extras == null) {
+ extras = EMPTY_EXTRAS;
+ }
+
+ final List<PhoneAccountHandle> phoneAccountHandles = extras
+ .getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
+
+ if ((call.getAccountHandle() == null &&
+ (phoneAccountHandles == null || phoneAccountHandles.isEmpty()))) {
+ Log.i(InCallPresenter.getInstance(), "No valid accounts for call " + call);
+ return true;
+ }
+ }
+ 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.
* @param call
@@ -1030,6 +1165,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
Log.i(this, "Start UI " + " anyOtherSubActive:" + anyOtherSubActive);
if (isCallWaiting || anyOtherSubActive) {
if (mProximitySensor.isScreenReallyOff() && isActivityStarted()) {
+ Log.i(this, "Restarting InCallActivity to turn screen on for call waiting");
mInCallActivity.finish();
// When the activity actually finishes, we will start it again if there are
// any active calls, so we do not need to start it explicitly here. Note, we
@@ -1103,21 +1239,104 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
}
- private void showInCall(boolean showDialpad, boolean newOutgoingCall) {
- mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall));
+ public void showInCall(final boolean showDialpad, final boolean newOutgoingCall) {
+ if (mCircularRevealActivityStarted) {
+ mWaitForRevealAnimationStart = true;
+ mShowDialpadOnStart = showDialpad;
+ Log.i(this, "Waiting for circular reveal completion to show InCallActivity");
+ } else {
+ Log.i(this, "Showing InCallActivity immediately");
+ mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall,
+ newOutgoingCall /* showCircularReveal */));
+ }
+ }
+
+ public void onCircularRevealStarted(final Activity activity) {
+ mCircularRevealActivityStarted = false;
+ if (mWaitForRevealAnimationStart) {
+ mWaitForRevealAnimationStart = false;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.i(this, "Showing InCallActivity after circular reveal");
+ final Intent intent =
+ getInCallIntent(mShowDialpadOnStart, true, false, false);
+ activity.startActivity(intent);
+ mShowDialpadOnStart = false;
+ }
+ });
+ } else if (!mServiceBound) {
+ CircularRevealActivity.sendClearDisplayBroadcast(mContext);
+ return;
+ }
}
- public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall) {
- final Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ public void onServiceBind() {
+ mServiceBound = true;
+ }
+
+ public void onServiceUnbind() {
+ mServiceBound = false;
+ }
+
+ public boolean isServiceBound() {
+ return mServiceBound;
+ }
+
+ public void maybeStartRevealAnimation(Intent intent) {
+ if (intent == null || mInCallActivity != null) {
+ return;
+ }
+ final Bundle extras = intent.getBundleExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
+ if (extras == null) {
+ // Incoming call, just show the in-call UI directly.
+ return;
+ }
+
+ if (extras.containsKey(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS)) {
+ // Account selection dialog will show up so don't show the animation.
+ return;
+ }
+
+ final PhoneAccountHandle accountHandle =
+ intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+ final MaterialPalette colors = getColorsFromPhoneAccountHandle(accountHandle);
+ final Point touchPoint = extras.getParcelable(TouchPointManager.TOUCH_POINT);
+
+ mCircularRevealActivityStarted = true;
+ mContext.startActivity(getAnimationIntent(touchPoint, colors));
+ }
+
+ private Intent getAnimationIntent(Point touchPoint, MaterialPalette palette) {
+ final Intent intent = new Intent(mContext, CircularRevealActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ intent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint);
+ intent.putExtra(CircularRevealActivity.EXTRA_THEME_COLORS, palette);
+ return intent;
+ }
+
+ public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall,
+ boolean showCircularReveal) {
+ return getInCallIntent(showDialpad, newOutgoingCall, showCircularReveal, true);
+ }
+
+ public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall,
+ boolean showCircularReveal, boolean newTask) {
+ final Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ if (newTask) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
intent.setClass(mContext, InCallActivity.class);
if (showDialpad) {
intent.putExtra(InCallActivity.SHOW_DIALPAD_EXTRA, true);
}
-
- intent.putExtra(InCallActivity.NEW_OUTGOING_CALL, newOutgoingCall);
+ intent.putExtra(InCallActivity.NEW_OUTGOING_CALL_EXTRA, newOutgoingCall);
+ intent.putExtra(InCallActivity.SHOW_CIRCULAR_REVEAL_EXTRA, showCircularReveal);
return intent;
}
@@ -1262,6 +1481,20 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
/**
+ * Hides or shows the conference manager fragment.
+ *
+ * @param show {@code true} if the conference manager should be shown, {@code false} if it
+ * should be hidden.
+ */
+ public void showConferenceCallManager(boolean show) {
+ if (mInCallActivity == null) {
+ return;
+ }
+
+ mInCallActivity.showConferenceCallManager(show);
+ }
+
+ /**
* @return True if the application is currently running in a right-to-left locale.
*/
public static boolean isRtl() {
@@ -1270,6 +1503,61 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
}
/**
+ * Extract background color from call object. The theme colors will include a primary color
+ * and a secondary color.
+ */
+ public void setThemeColors() {
+ // This method will set the background to default if the color is PhoneAccount.NO_COLOR.
+ mThemeColors = getColorsFromCall(CallList.getInstance().getFirstCall());
+
+ if (mInCallActivity == null) {
+ return;
+ }
+
+ mInCallActivity.getWindow().setStatusBarColor(mThemeColors.mSecondaryColor);
+ }
+
+ /**
+ * @return A palette for colors to display in the UI.
+ */
+ public MaterialPalette getThemeColors() {
+ return mThemeColors;
+ }
+
+ private MaterialPalette getColorsFromCall(Call call) {
+ return getColorsFromPhoneAccountHandle(call == null ? null : call.getAccountHandle());
+ }
+
+ private MaterialPalette getColorsFromPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ int highlightColor = PhoneAccount.NO_HIGHLIGHT_COLOR;
+ if (phoneAccountHandle != null) {
+ final TelecomManager tm = getTelecomManager();
+
+ if (tm != null) {
+ final PhoneAccount account = tm.getPhoneAccount(phoneAccountHandle);
+ // For single-sim devices, there will be no selected highlight color, so the phone
+ // account will default to NO_HIGHLIGHT_COLOR.
+ if (account != null) {
+ highlightColor = account.getHighlightColor();
+ }
+ }
+ }
+ return new InCallUIMaterialColorMapUtils(
+ mContext.getResources()).calculatePrimaryAndSecondaryColor(highlightColor);
+ }
+
+ /**
+ * @return An instance of TelecomManager.
+ */
+ public TelecomManager getTelecomManager() {
+ if (mTelecomManager == null) {
+ mTelecomManager = (TelecomManager)
+ mContext.getSystemService(Context.TELECOM_SERVICE);
+ }
+ return mTelecomManager;
+ }
+
+ /**
* Private constructor. Must use getInstance() to get this singleton.
*/
private InCallPresenter() {
@@ -1340,4 +1628,8 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener {
public interface InCallEventListener {
public void onFullScreenVideoStateChanged(boolean isFullScreenVideo);
}
+
+ public interface InCallUiListener {
+ void onUiShowing(boolean showing);
+ }
}
diff --git a/src/com/android/incallui/InCallServiceImpl.java b/src/com/android/incallui/InCallServiceImpl.java
index dfec2622..39aaf931 100644
--- a/src/com/android/incallui/InCallServiceImpl.java
+++ b/src/com/android/incallui/InCallServiceImpl.java
@@ -16,6 +16,8 @@
package com.android.incallui;
+import android.content.Intent;
+import android.os.IBinder;
import android.telecom.InCallService;
import android.telecom.Phone;
@@ -34,10 +36,6 @@ public class InCallServiceImpl extends InCallService {
AudioModeProvider.getInstance().setPhone(phone);
TelecomAdapter.getInstance().setPhone(phone);
InCallPresenter.getInstance().setPhone(phone);
- InCallPresenter.getInstance().setUp(
- getApplicationContext(),
- CallList.getInstance(),
- AudioModeProvider.getInstance());
CallRecorder.getInstance().setUp(getApplicationContext());
TelecomAdapter.getInstance().setContext(InCallServiceImpl.this);
}
@@ -53,4 +51,21 @@ public class InCallServiceImpl extends InCallService {
CallList.getInstance().clearOnDisconnect();
InCallPresenter.getInstance().tearDown();
}
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ InCallPresenter.getInstance().setUp(
+ getApplicationContext(),
+ CallList.getInstance(),
+ AudioModeProvider.getInstance());
+ InCallPresenter.getInstance().onServiceBind();
+ InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
+ return super.onBind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ InCallPresenter.getInstance().onServiceUnbind();
+ return super.onUnbind(intent);
+ }
}
diff --git a/src/com/android/incallui/InCallUIMaterialColorMapUtils.java b/src/com/android/incallui/InCallUIMaterialColorMapUtils.java
new file mode 100644
index 00000000..1f61070e
--- /dev/null
+++ b/src/com/android/incallui/InCallUIMaterialColorMapUtils.java
@@ -0,0 +1,52 @@
+package com.android.incallui;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.telecom.PhoneAccount;
+
+import com.android.contacts.common.util.MaterialColorMapUtils;
+import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+
+public class InCallUIMaterialColorMapUtils extends MaterialColorMapUtils {
+ private final TypedArray sPrimaryColors;
+ private final TypedArray sSecondaryColors;
+ private final Resources mResources;
+
+ public InCallUIMaterialColorMapUtils(Resources resources) {
+ super(resources);
+ sPrimaryColors = resources.obtainTypedArray(
+ com.android.incallui.R.array.background_colors);
+ sSecondaryColors = resources.obtainTypedArray(
+ com.android.incallui.R.array.background_colors_dark);
+ mResources = resources;
+ }
+
+ /**
+ * Currently the InCallUI color will only vary by SIM color which is a list of colors
+ * defined in the background_colors array, so first search the list for the matching color and
+ * fall back to the closest matching color if an exact match does not exist.
+ */
+ @Override
+ public MaterialPalette calculatePrimaryAndSecondaryColor(int color) {
+ if (color == PhoneAccount.NO_HIGHLIGHT_COLOR) {
+ return getDefaultPrimaryAndSecondaryColors(mResources);
+ }
+
+ for (int i = 0; i < sPrimaryColors.length(); i++) {
+ if (sPrimaryColors.getColor(i, 0) == color) {
+ return new MaterialPalette(
+ sPrimaryColors.getColor(i, 0),
+ sSecondaryColors.getColor(i, 0));
+ }
+ }
+
+ // The color isn't in the list, so use the superclass to find an approximate color.
+ return super.calculatePrimaryAndSecondaryColor(color);
+ }
+
+ public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) {
+ final int primaryColor = resources.getColor(R.color.dialer_theme_color);
+ final int secondaryColor = resources.getColor(R.color.dialer_theme_color_dark);
+ return new MaterialPalette(primaryColor, secondaryColor);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/incallui/Log.java b/src/com/android/incallui/Log.java
index 5e13c790..07a0e61c 100644
--- a/src/com/android/incallui/Log.java
+++ b/src/com/android/incallui/Log.java
@@ -31,9 +31,11 @@ public class Log {
// Generic tag for all In Call logging
public static final String TAG = "InCall";
- public static final boolean DEBUG = android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
- public static final boolean VERBOSE = android.util.Log.isLoggable(TAG,
- android.util.Log.VERBOSE);
+ public static final boolean FORCE_DEBUG = false; /* STOPSHIP if true */
+ public static final boolean DEBUG = FORCE_DEBUG ||
+ android.util.Log.isLoggable(TAG, android.util.Log.DEBUG);
+ public static final boolean VERBOSE = FORCE_DEBUG ||
+ android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE);
public static final String TAG_DELIMETER = " - ";
public static void d(String tag, String msg) {
diff --git a/src/com/android/incallui/Presenter.java b/src/com/android/incallui/Presenter.java
index d2f2d36f..4e1fa978 100644
--- a/src/com/android/incallui/Presenter.java
+++ b/src/com/android/incallui/Presenter.java
@@ -16,6 +16,8 @@
package com.android.incallui;
+import android.os.Bundle;
+
/**
* Base class for Presenters.
*/
@@ -47,6 +49,10 @@ public abstract class Presenter<U extends Ui> {
public void onUiUnready(U ui) {
}
+ public void onSaveInstanceState(Bundle outState) {}
+
+ public void onRestoreInstanceState(Bundle savedInstanceState) {}
+
public U getUi() {
return mUi;
}
diff --git a/src/com/android/incallui/SelectPhoneAccountDialogFragment.java b/src/com/android/incallui/SelectPhoneAccountDialogFragment.java
deleted file mode 100644
index e6c78c6d..00000000
--- a/src/com/android/incallui/SelectPhoneAccountDialogFragment.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright (C) 2014 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
- */
-
-package com.android.incallui;
-
-import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccountHandle;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.FragmentManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.telecom.TelecomManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.contacts.common.R;
-
-import java.util.List;
-
-/**
- * Dialog that allows the user to switch between default SIM cards
- */
-public class SelectPhoneAccountDialogFragment extends DialogFragment {
- private List<PhoneAccountHandle> mAccountHandles;
- private boolean mIsSelected;
- private TelecomManager mTelecomManager;
-
- /**
- * Shows the account selection dialog.
- * This is the preferred way to show this dialog.
- *
- * @param fragmentManager The fragment manager.
- * @param accountHandles The {@code PhoneAccountHandle}s available to select from.
- */
- public static void showAccountDialog(FragmentManager fragmentManager,
- List<PhoneAccountHandle> accountHandles) {
- SelectPhoneAccountDialogFragment fragment =
- new SelectPhoneAccountDialogFragment(accountHandles);
- fragment.show(fragmentManager, "selectAccount");
- }
-
- public SelectPhoneAccountDialogFragment(List<PhoneAccountHandle> accountHandles) {
- super();
- mAccountHandles = accountHandles;
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- mIsSelected = false;
- mTelecomManager =
- (TelecomManager) getActivity().getSystemService(Context.TELECOM_SERVICE);
-
- final DialogInterface.OnClickListener selectionListener =
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mIsSelected = true;
- PhoneAccountHandle selectedAccountHandle = mAccountHandles.get(which);
- InCallPresenter.getInstance().handleAccountSelection(selectedAccountHandle,
- mIsSelected);
- }
- };
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-
- ListAdapter selectAccountListAdapter = new SelectAccountListAdapter(
- builder.getContext(),
- R.layout.select_account_list_item,
- mAccountHandles);
-
- return builder.setTitle(R.string.select_account_dialog_title)
- .setAdapter(selectAccountListAdapter, selectionListener)
- .create();
- }
-
- private class SelectAccountListAdapter extends ArrayAdapter<PhoneAccountHandle> {
- private Context mContext;
- private int mResId;
-
- public SelectAccountListAdapter(
- Context context, int resource, List<PhoneAccountHandle> accountHandles) {
- super(context, resource, accountHandles);
- mContext = context;
- mResId = resource;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- LayoutInflater inflater = (LayoutInflater)
- mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- View rowView;
- final ViewHolder holder;
-
- if (convertView == null) {
- // Cache views for faster scrolling
- rowView = inflater.inflate(mResId, null);
- holder = new ViewHolder();
- holder.textView = (TextView) rowView.findViewById(R.id.text);
- holder.imageView = (ImageView) rowView.findViewById(R.id.icon);
- rowView.setTag(holder);
- }
- else {
- rowView = convertView;
- holder = (ViewHolder) rowView.getTag();
- }
-
- PhoneAccountHandle accountHandle = getItem(position);
- PhoneAccount account = mTelecomManager.getPhoneAccount(accountHandle);
- holder.textView.setText(account.getLabel());
- holder.imageView.setImageDrawable(account.createIconDrawable(mContext));
- return rowView;
- }
-
- private class ViewHolder {
- TextView textView;
- ImageView imageView;
- }
- }
-
- @Override
- public void onPause() {
- if (!mIsSelected) {
- InCallPresenter.getInstance().cancelAccountSelection();
- }
- super.onPause();
- }
-}
diff --git a/src/com/android/incallui/StatusBarNotifier.java b/src/com/android/incallui/StatusBarNotifier.java
index ca76d235..8e0afcb3 100644
--- a/src/com/android/incallui/StatusBarNotifier.java
+++ b/src/com/android/incallui/StatusBarNotifier.java
@@ -17,6 +17,7 @@
package com.android.incallui;
import android.net.Uri;
+
import com.google.common.base.Preconditions;
import android.app.Notification;
@@ -34,8 +35,11 @@ import android.telecom.PhoneCapabilities;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
+import android.text.BidiFormatter;
+import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
+import com.android.contacts.common.util.BitmapUtil;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;
import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
import com.android.incallui.InCallApp.NotificationBroadcastReceiver;
@@ -150,8 +154,10 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
* @see #updateInCallNotification(InCallState,CallList)
*/
private void cancelInCall() {
- Log.d(this, "cancelInCall()...");
- mNotificationManager.cancel(IN_CALL_NOTIFICATION);
+ if (mIsShowingNotification) {
+ Log.d(this, "cancelInCall()...");
+ mNotificationManager.cancel(IN_CALL_NOTIFICATION);
+ }
mIsShowingNotification = false;
}
@@ -270,15 +276,12 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
}
final int state = call.getState();
- final boolean isConference = call.isConferenceCall();
- final boolean isVideoUpgradeRequest = call.getSessionModificationState()
- == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
// Check if data has changed; if nothing is different, don't issue another notification.
final int iconResId = getIconToDisplay(call);
- final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, isConference);
+ final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
final int contentResId = getContentString(call);
- final String contentTitle = getContentTitle(contactInfo, isConference);
+ final String contentTitle = getContentTitle(contactInfo, call);
if (!checkForChangeAndSaveData(iconResId, contentResId, largeIcon, contentTitle, state)) {
return;
@@ -297,6 +300,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
if ((state == Call.State.INCOMING || state == Call.State.CALL_WAITING) &&
!InCallPresenter.getInstance().isShowingInCallUi()) {
configureFullScreenIntent(builder, inCallPendingIntent, call);
+ // Set the notification category for incoming calls
+ builder.setCategory(Notification.CATEGORY_CALL);
}
// Set the content
@@ -306,14 +311,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
builder.setLargeIcon(largeIcon);
builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
- if (TelephonyManager.getDefault().isMultiSimEnabled()) {
- SubscriptionManager mgr = SubscriptionManager.from(mContext);
- SubscriptionInfo subInfoRecord = mgr.getActiveSubscriptionInfo(call.getSubId());
- if (subInfoRecord != null) {
- builder.setSubText(subInfoRecord.getDisplayName());
- }
- }
-
+ final boolean isVideoUpgradeRequest = call.getSessionModificationState()
+ == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
if (isVideoUpgradeRequest) {
builder.setUsesChronometer(false);
addDismissUpgradeRequestAction(builder);
@@ -400,15 +399,15 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
/**
* Returns the main string to use in the notification.
*/
- private String getContentTitle(ContactCacheEntry contactInfo, boolean isConference) {
- if (isConference) {
+ private String getContentTitle(ContactCacheEntry contactInfo, Call call) {
+ if (call.isConferenceCall()
+ && !call.can(android.telecom.Call.Details.CAPABILITY_GENERIC_CONFERENCE)) {
return mContext.getResources().getString(R.string.card_title_conf_call);
}
if (TextUtils.isEmpty(contactInfo.name)) {
- if (!TextUtils.isEmpty(contactInfo.location)){
- return contactInfo.number + " " + contactInfo.location;
- }
- return contactInfo.number;
+ return TextUtils.isEmpty(contactInfo.number) ? null
+ : BidiFormatter.getInstance().unicodeWrap(
+ contactInfo.number.toString(), TextDirectionHeuristics.LTR);
}
return contactInfo.name;
}
@@ -426,9 +425,10 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
/**
* Gets a large icon from the contact info object to display in the notification.
*/
- private Bitmap getLargeIconToDisplay(ContactCacheEntry contactInfo, boolean isConference) {
+ private Bitmap getLargeIconToDisplay(ContactCacheEntry contactInfo, Call call) {
Bitmap largeIcon = null;
- if (isConference) {
+ if (call.isConferenceCall()
+ && !call.can(android.telecom.Call.Details.CAPABILITY_GENERIC_CONFERENCE)) {
largeIcon = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.img_conference);
}
@@ -441,9 +441,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
android.R.dimen.notification_large_icon_height);
final int width = (int) mContext.getResources().getDimension(
android.R.dimen.notification_large_icon_width);
- largeIcon = Bitmap.createScaledBitmap(largeIcon, width, height, false);
+ largeIcon = BitmapUtil.getRoundedBitmap(largeIcon, width, height);
}
-
return largeIcon;
}
@@ -526,7 +525,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
PendingIntent answerVoicePendingIntent = createNotificationPendingIntent(
mContext, InCallApp.ACTION_ANSWER_VOICE_INCOMING_CALL);
builder.addAction(R.drawable.ic_call_white_24dp,
- mContext.getText(R.string.description_target_answer),
+ mContext.getText(R.string.notification_action_answer),
answerVoicePendingIntent);
}
@@ -657,7 +656,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
private PendingIntent createLaunchPendingIntent() {
final Intent intent = InCallPresenter.getInstance().getInCallIntent(
- false /* showDialpad */, false /* newOutgoingCall */);
+ false /* showDialpad */, false /* newOutgoingCall */,
+ false /* showCircularReveal */);
// PendingIntent that can be used to launch the InCallActivity. The
// system fires off this intent if the user pulls down the windowshade
diff --git a/src/com/android/incallui/TelecomAdapter.java b/src/com/android/incallui/TelecomAdapter.java
index 6acb6404..b0fcc128 100644
--- a/src/com/android/incallui/TelecomAdapter.java
+++ b/src/com/android/incallui/TelecomAdapter.java
@@ -24,8 +24,6 @@ import android.telecom.InCallAdapter;
import android.telecom.Phone;
import android.telecom.PhoneAccountHandle;
-import android.telecom.PhoneCapabilities;
-
import com.google.common.base.Preconditions;
import java.util.List;
@@ -181,8 +179,8 @@ final class TelecomAdapter implements InCallPhoneListener {
if (!conferenceable.isEmpty()) {
call.conference(conferenceable.get(0));
} else {
- int capabilities = call.getDetails().getCallCapabilities();
- if (0 != (capabilities & PhoneCapabilities.MERGE_CONFERENCE)) {
+ if (call.getDetails().can(
+ android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
call.mergeConference();
}
}
@@ -194,8 +192,8 @@ final class TelecomAdapter implements InCallPhoneListener {
void swap(String callId) {
if (mPhone != null) {
android.telecom.Call call = getTelecommCallById(callId);
- int capabilities = call.getDetails().getCallCapabilities();
- if (0 != (capabilities & PhoneCapabilities.SWAP_CONFERENCE)) {
+ if (call.getDetails().can(
+ android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE)) {
call.swapConference();
}
} else {
@@ -248,8 +246,7 @@ final class TelecomAdapter implements InCallPhoneListener {
}
}
- void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle,
- boolean setDefault) {
+ void phoneAccountSelected(String callId, PhoneAccountHandle accountHandle, boolean setDefault) {
if (mPhone != null) {
getTelecommCallById(callId).phoneAccountSelected(accountHandle, setDefault);
} else {
diff --git a/src/com/android/incallui/widget/multiwaveview/GlowPadView.java b/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
index 745b1870..7c115b99 100644
--- a/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
+++ b/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
@@ -402,7 +402,6 @@ public class GlowPadView extends View {
case STATE_TRACKING:
mHandleDrawable.setAlpha(0.0f);
- showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null);
break;
case STATE_SNAP: