diff options
Diffstat (limited to 'InCallUI/src/com/android/incallui')
30 files changed, 808 insertions, 775 deletions
diff --git a/InCallUI/src/com/android/incallui/AccelerometerListener.java b/InCallUI/src/com/android/incallui/AccelerometerListener.java index 1a7077866..ca8e7d0a4 100644 --- a/InCallUI/src/com/android/incallui/AccelerometerListener.java +++ b/InCallUI/src/com/android/incallui/AccelerometerListener.java @@ -120,7 +120,7 @@ public final class AccelerometerListener { if (x == 0.0 || y == 0.0 || z == 0.0) return; // magnitude of the acceleration vector projected onto XY plane - final double xy = Math.sqrt(x*x + y*y); + final double xy = Math.hypot(x, y); // compute the vertical angle double angle = Math.atan2(xy, z); // convert to degrees @@ -131,16 +131,19 @@ public final class AccelerometerListener { } SensorEventListener mSensorListener = new SensorEventListener() { + @Override public void onSensorChanged(SensorEvent event) { onSensorEvent(event.values[0], event.values[1], event.values[2]); } + @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignore } }; Handler mHandler = new Handler() { + @Override public void handleMessage(Message msg) { switch (msg.what) { case ORIENTATION_CHANGED: diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java index a066396c9..d1636371c 100644 --- a/InCallUI/src/com/android/incallui/AnswerFragment.java +++ b/InCallUI/src/com/android/incallui/AnswerFragment.java @@ -79,7 +79,7 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente @Override public AnswerPresenter createPresenter() { - return new AnswerPresenter(); + return InCallPresenter.getInstance().getAnswerPresenter(); } @Override @@ -111,11 +111,9 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente } @Override - public void showAnswerUi(boolean show) { - getView().setVisibility(show ? View.VISIBLE : View.GONE); - - Log.d(this, "Show answer UI: " + show); - if (show) { + public void onShowAnswerUi(boolean shown) { + Log.d(this, "Show answer UI: " + shown); + if (shown) { mGlowpad.startPing(); } else { mGlowpad.stopPing(); @@ -277,7 +275,7 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente } } - public void dismissPendingDialogues() { + public void dismissPendingDialogs() { if (isCannedResponsePopupShowing()) { dismissCannedResponsePopup(); } diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java index 208532572..32c7b9a18 100644 --- a/InCallUI/src/com/android/incallui/AnswerPresenter.java +++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java @@ -17,16 +17,25 @@ package com.android.incallui; import android.content.Context; + +import com.android.incallui.InCallPresenter.InCallState; import android.telecom.TelecomManager; import android.telecom.VideoProfile; import java.util.List; /** - * Presenter for the Incoming call widget. + * Presenter for the Incoming call widget. The {@link AnswerPresenter} handles the logic during + * incoming calls. It is also in charge of responding to incoming calls, so there needs to be + * an instance alive so that it can receive onIncomingCall callbacks. + * + * An instance of {@link AnswerPresenter} is created by InCallPresenter at startup, registers + * for callbacks via InCallPresenter, and shows/hides the {@link AnswerFragment} via IncallActivity. + * */ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> - implements CallList.CallUpdateListener, CallList.Listener { + implements CallList.CallUpdateListener, InCallPresenter.InCallUiListener, + InCallPresenter.IncomingCallListener { private static final String TAG = AnswerPresenter.class.getSimpleName(); @@ -35,48 +44,38 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> private boolean mHasTextMessages = false; @Override - public void onUiReady(AnswerUi ui) { - Log.d(this, "onUiReady ui=" + ui); - super.onUiReady(ui); - - final CallList calls = CallList.getInstance(); - Call call; - call = calls.getIncomingCall(); - if (call != null) { - processIncomingCall(call); - } - - Call videoCall = calls.getVideoUpgradeRequestCall(); - Log.d(this, "getVideoUpgradeRequestCall call =" + call); - - if (videoCall != null && call == null) { - processVideoUpgradeRequestCall(videoCall); + public void onUiShowing(boolean showing) { + if (showing) { + final CallList calls = CallList.getInstance(); + Call call; + call = calls.getIncomingCall(); + if (call != null) { + processIncomingCall(call); + } + call = calls.getVideoUpgradeRequestCall(); + Log.d(this, "getVideoUpgradeRequestCall call =" + call); + if (videoCall != null && call == null) { + processVideoUpgradeRequestCall(call); + } + } else { + // This is necessary because the activity can be destroyed while an incoming call exists. + // This happens when back button is pressed while incoming call is still being shown. + if (mCallId != null) { + CallList.getInstance().removeCallUpdateListener(mCallId, this); + } } - - // Listen for incoming calls. - calls.addListener(this); } @Override - public void onUiUnready(AnswerUi ui) { - super.onUiUnready(ui); - - CallList.getInstance().removeListener(this); - - // This is necessary because the activity can be destroyed while an incoming call exists. - // This happens when back button is pressed while incoming call is still being shown. - if (mCallId != null) { - CallList.getInstance().removeCallUpdateListener(mCallId, this); + public void onIncomingCall(InCallState oldState, InCallState newState, Call call) { + Log.d(this, "onIncomingCall: " + this); + if (!call.getId().equals(mCallId)) { + // A new call is coming in. + processIncomingCall(call); } } @Override - public void onCallListChange(CallList callList) { - Log.d(this, "onCallListChange callList=" + callList); - // no-op - } - - @Override public void onDisconnect(Call call) { // no-op } @@ -98,6 +97,7 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> // A new call is coming in. processIncomingCall(call); } +>>>>>>> 8bef461 } } @@ -134,9 +134,20 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> CallList.getInstance().addCallUpdateListener(mCallId, this); Log.d(TAG, "Showing incoming for call id: " + mCallId + " " + this); - final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId()); - getUi().showAnswerUi(true); - configureAnswerTargetsForSms(call, textMsgs); + if (showAnswerUi(true)) { + final List<String> textMsgs = CallList.getInstance().getTextResponses(call.getId()); + configureAnswerTargetsForSms(call, textMsgs); + } + } + + private boolean showAnswerUi(boolean show) { + final InCallActivity activity = InCallPresenter.getInstance().getActivity(); + if (activity != null) { + activity.showAnswerFragment(show); + return true; + } else { + return false; + } } private void processVideoUpgradeRequestCall(Call call) { @@ -155,14 +166,8 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> return; } - AnswerUi ui = getUi(); - - if (ui == null) { - Log.e(this, "Ui is null. Can't process upgrade request"); - return; - } - ui.showAnswerUi(true); - ui.showTargets(getUiTarget(currentVideoState, modifyToVideoState)); + showAnswerUi(true); + getUi().showTargets(getUiTarget(currentVideoState, modifyToVideoState)); } @@ -200,9 +205,9 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> final Call incall = CallList.getInstance().getIncomingCall(); if (incall != null || isUpgradePending) { - getUi().showAnswerUi(true); + showAnswerUi(true); } else { - getUi().showAnswerUi(false); + showAnswerUi(false); } mHasTextMessages = false; @@ -261,6 +266,10 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> } private void configureAnswerTargetsForSms(Call call, List<String> textMsgs) { + if (getUi() == null) { + return; + } + final Context context = getUi().getContext(); mHasTextMessages = textMsgs != null; @@ -285,7 +294,7 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> } interface AnswerUi extends Ui { - public void showAnswerUi(boolean show); + public void onShowAnswerUi(boolean shown); public void showTargets(int targetSet); public void showMessageDialog(); public void configureMessageDialog(List<String> textResponses); diff --git a/InCallUI/src/com/android/incallui/AudioModeProvider.java b/InCallUI/src/com/android/incallui/AudioModeProvider.java index c823fda3c..a26766126 100644 --- a/InCallUI/src/com/android/incallui/AudioModeProvider.java +++ b/InCallUI/src/com/android/incallui/AudioModeProvider.java @@ -26,7 +26,7 @@ import java.util.List; /** * Proxy class for getting and setting the audio mode. */ -/* package */ class AudioModeProvider implements InCallPhoneListener { +public class AudioModeProvider implements InCallPhoneListener { static final int AUDIO_MODE_INVALID = 0; diff --git a/InCallUI/src/com/android/incallui/BaseFragment.java b/InCallUI/src/com/android/incallui/BaseFragment.java index 1ef3b1513..037c2455f 100644 --- a/InCallUI/src/com/android/incallui/BaseFragment.java +++ b/InCallUI/src/com/android/incallui/BaseFragment.java @@ -16,6 +16,7 @@ package com.android.incallui; +import android.app.Activity; import android.app.Fragment; import android.os.Bundle; @@ -24,6 +25,8 @@ import android.os.Bundle; */ public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends Fragment { + private static final String KEY_FRAGMENT_HIDDEN = "key_fragment_hidden"; + private T mPresenter; abstract T createPresenter(); @@ -54,6 +57,9 @@ public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends super.onCreate(savedInstanceState); if (savedInstanceState != null) { mPresenter.onRestoreInstanceState(savedInstanceState); + if (savedInstanceState.getBoolean(KEY_FRAGMENT_HIDDEN)) { + getFragmentManager().beginTransaction().hide(this).commit(); + } } } @@ -67,5 +73,12 @@ public abstract class BaseFragment<T extends Presenter<U>, U extends Ui> extends public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mPresenter.onSaveInstanceState(outState); + outState.putBoolean(KEY_FRAGMENT_HIDDEN, isHidden()); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + ((FragmentDisplayManager) activity).onFragmentAttached(this); } } diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java index 3d6f2032e..4ed7a49cf 100644 --- a/InCallUI/src/com/android/incallui/Call.java +++ b/InCallUI/src/com/android/incallui/Call.java @@ -17,11 +17,14 @@ package com.android.incallui; import com.android.contacts.common.CallUtil; +import com.android.contacts.common.testing.NeededForTesting; import com.android.incallui.CallList.Listener; import android.content.Context; import android.hardware.camera2.CameraCharacteristics; import android.net.Uri; +import android.os.Bundle; +import android.os.Trace; import android.telecom.CallProperties; import android.telecom.DisconnectCause; import android.telecom.GatewayInfo; @@ -36,7 +39,8 @@ import java.util.Locale; /** * Describes a single call and its state. */ -public final class Call { +@NeededForTesting +public class Call { /* Defines different states of this call */ public static class State { public static final int INVALID = 0; @@ -236,7 +240,7 @@ public final class Call { } }; - private final android.telecom.Call mTelecommCall; + private android.telecom.Call mTelecommCall; private final String mId; private int mState = State.INVALID; private DisconnectCause mDisconnectCause; @@ -250,6 +254,16 @@ public final class Call { private InCallVideoCallListener mVideoCallListener; + /** + * Used only to create mock calls for testing + */ + @NeededForTesting + Call(int state) { + mTelecommCall = null; + mId = ID_PREFIX + Integer.toString(sIdCounter++); + setState(state); + } + public Call(android.telecom.Call telecommCall) { mTelecommCall = telecommCall; mId = ID_PREFIX + Integer.toString(sIdCounter++); @@ -270,6 +284,7 @@ public final class Call { } private void update() { + Trace.beginSection("Update"); int oldState = getState(); updateFromTelecommCall(); if (oldState != getState() && getState() == Call.State.DISCONNECTED) { @@ -277,6 +292,7 @@ public final class Call { } else { CallList.getInstance().onUpdate(this); } + Trace.endSection(); } private void updateFromTelecommCall() { @@ -329,6 +345,9 @@ public final class Call { } public String getNumber() { + if (mTelecommCall == null) { + return null; + } if (mTelecommCall.getDetails().getGatewayInfo() != null) { return mTelecommCall.getDetails().getGatewayInfo() .getOriginalAddress().getSchemeSpecificPart(); @@ -337,11 +356,11 @@ public final class Call { } public Uri getHandle() { - return mTelecommCall.getDetails().getHandle(); + return mTelecommCall == null ? null : mTelecommCall.getDetails().getHandle(); } public int getState() { - if (mTelecommCall.getParent() != null) { + if (mTelecommCall != null && mTelecommCall.getParent() != null) { return State.CONFERENCED; } else { return mState; @@ -353,15 +372,21 @@ public final class Call { } public int getNumberPresentation() { - return getTelecommCall().getDetails().getHandlePresentation(); + return mTelecommCall == null ? null : mTelecommCall.getDetails().getHandlePresentation(); } public int getCnapNamePresentation() { - return getTelecommCall().getDetails().getCallerDisplayNamePresentation(); + return mTelecommCall == null ? null + : mTelecommCall.getDetails().getCallerDisplayNamePresentation(); } public String getCnapName() { - return getTelecommCall().getDetails().getCallerDisplayName(); + return mTelecommCall == null ? null + : getTelecommCall().getDetails().getCallerDisplayName(); + } + + public Bundle getExtras() { + return mTelecommCall == null ? null : mTelecommCall.getDetails().getExtras(); } /** Returns call disconnect cause, defined by {@link DisconnectCause}. */ @@ -414,15 +439,15 @@ public final class Call { } public GatewayInfo getGatewayInfo() { - return mTelecommCall.getDetails().getGatewayInfo(); + return mTelecommCall == null ? null : mTelecommCall.getDetails().getGatewayInfo(); } public PhoneAccountHandle getAccountHandle() { - return mTelecommCall.getDetails().getAccountHandle(); + return mTelecommCall == null ? null : mTelecommCall.getDetails().getAccountHandle(); } public VideoCall getVideoCall() { - return mTelecommCall.getVideoCall(); + return mTelecommCall == null ? null : mTelecommCall.getVideoCall(); } public List<String> getChildCallIds() { @@ -521,6 +546,12 @@ public final class Call { @Override public String toString() { + if (mTelecommCall == null) { + // This should happen only in testing since otherwise we would never have a null + // Telecom call. + return String.valueOf(mId); + } + return String.format(Locale.US, "[%s, %s, %s, children:%s, parent:%s, conferenceable:%s, " + "videoState:%d, callSubState:%d, mSessionModificationState:%d, VideoSettings:%s]", mId, diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java index 8682d65e5..a9fafae18 100644 --- a/InCallUI/src/com/android/incallui/CallButtonFragment.java +++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java @@ -327,10 +327,6 @@ public class CallButtonFragment @Override public void setEnabled(boolean isEnabled) { mIsEnabled = isEnabled; - View view = getView(); - if (view.getVisibility() != View.VISIBLE) { - view.setVisibility(View.VISIBLE); - } mAudioButton.setEnabled(isEnabled); mChangeToVoiceButton.setEnabled(isEnabled); @@ -877,7 +873,7 @@ public class CallButtonFragment public void displayDialpad(boolean value, boolean animate) { mShowDialpadButton.setSelected(value); if (getActivity() != null && getActivity() instanceof InCallActivity) { - ((InCallActivity) getActivity()).displayDialpad(value, animate); + ((InCallActivity) getActivity()).showDialpadFragment(value, animate); } } diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 9f43502e0..7420ba90e 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -21,23 +21,23 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.animation.ValueAnimator.AnimatorUpdateListener; import android.app.Activity; import android.content.Context; import android.content.res.Configuration; -import android.graphics.Point; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Trace; import android.telecom.DisconnectCause; import android.telecom.VideoProfile; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.text.format.DateUtils; -import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnLayoutChangeListener; -import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; @@ -51,7 +51,6 @@ import android.widget.TextView; import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; import com.android.contacts.common.widget.FloatingActionButtonController; -import com.android.incallui.service.PhoneNumberService; import com.android.phone.common.animation.AnimUtils; import java.util.List; @@ -61,9 +60,9 @@ import java.util.List; */ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPresenter.CallCardUi> implements CallCardPresenter.CallCardUi { + private static final String TAG = "CallCardFragment"; private AnimatorSet mAnimatorSet; - private int mRevealAnimationDuration; private int mShrinkAnimationDuration; private int mFabNormalDiameter; private int mFabSmallDiameter; @@ -109,13 +108,12 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr private ImageButton mFloatingActionButton; private int mFloatingActionButtonVerticalOffset; - // Cached DisplayMetrics density. - private float mDensity; - private float mTranslationOffset; private Animation mPulseAnimation; private int mVideoAnimationDuration; + // Whether or not the call card is currently in the process of an animation + private boolean mIsAnimating; private MaterialPalette mCurrentThemeColors; @@ -133,7 +131,6 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mRevealAnimationDuration = getResources().getInteger(R.integer.reveal_animation_duration); mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration); mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration); mFloatingActionButtonVerticalOffset = getResources().getDimensionPixelOffset( @@ -144,7 +141,6 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr R.dimen.end_call_floating_action_button_small_diameter); } - @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -157,13 +153,12 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - - mDensity = getResources().getDisplayMetrics().density; + Trace.beginSection(TAG + " onCreate"); mTranslationOffset = getResources().getDimensionPixelSize(R.dimen.call_card_anim_translate_y_offset); - - return inflater.inflate(R.layout.call_card_content, container, false); + final View view = inflater.inflate(R.layout.call_card_fragment, container, false); + Trace.endSection(); + return view; } @Override @@ -226,12 +221,15 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr @Override public void onClick(View v) { InCallActivity activity = (InCallActivity) getActivity(); - activity.showConferenceCallManager(true); + activity.showConferenceFragment(true); } }); mPrimaryName.setElegantTextHeight(false); mCallStateLabel.setElegantTextHeight(false); + + final LayoutTransition transition = mPrimaryCallInfo.getLayoutTransition(); + transition.enableTransitionType(LayoutTransition.CHANGING); } @Override @@ -370,7 +368,17 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr if (mIsLandscape) { return getView().getWidth() - mPrimaryCallCardContainer.getWidth(); } else { - return getView().getHeight() - mPrimaryCallCardContainer.getHeight(); + final int callCardHeight; + // Retrieve the actual height of the call card, independent of whether or not the + // outgoing call animation is in progress. The animation does not run in landscape mode + // so this only needs to be done for portrait. + if (mPrimaryCallCardContainer.getTag(R.id.view_tag_callcard_actual_height) != null) { + callCardHeight = (int) mPrimaryCallCardContainer.getTag( + R.id.view_tag_callcard_actual_height); + } else { + callCardHeight = mPrimaryCallCardContainer.getHeight(); + } + return getView().getHeight() - callCardHeight; } } @@ -380,7 +388,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mPrimaryName.setText(null); } else { mPrimaryName.setText(nameIsNumber - ? PhoneNumberUtils.ttsSpanAsPhoneNumber(name) + ? PhoneNumberUtils.getPhoneTtsSpannable(name) : name); // Set direction of the name field @@ -406,7 +414,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mPhoneNumber.setText(null); mPhoneNumber.setVisibility(View.GONE); } else { - mPhoneNumber.setText(PhoneNumberUtils.ttsSpanAsPhoneNumber(number)); + mPhoneNumber.setText(PhoneNumberUtils.getPhoneTtsSpannable(number)); mPhoneNumber.setVisibility(View.VISIBLE); mPhoneNumber.setTextDirection(View.TEXT_DIRECTION_LTR); } @@ -427,7 +435,6 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr public void setPrimary(String number, String name, boolean nameIsNumber, String label, Drawable photo, boolean isSipCall) { Log.d(this, "Setting primary call"); - // set the name field. setPrimaryName(name, nameIsNumber); @@ -464,7 +471,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mSecondaryCallConferenceCallIcon.setVisibility(isConference ? View.VISIBLE : View.GONE); mSecondaryCallName.setText(nameIsNumber - ? PhoneNumberUtils.ttsSpanAsPhoneNumber(name) + ? PhoneNumberUtils.getPhoneTtsSpannable(name) : name); if (hasProvider) { mSecondaryCallProviderLabel.setText(providerLabel); @@ -598,6 +605,10 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mInCallMessageLabel.setVisibility(View.VISIBLE); } + public boolean isAnimating() { + return mIsAnimating; + } + private void showInternetCallLabel(boolean show) { if (show) { final String label = getView().getContext().getString( @@ -855,13 +866,16 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr } } - public void animateForNewOutgoingCall(final Point touchPoint, - final boolean showCircularReveal) { + @Override + public void animateForNewOutgoingCall() { final ViewGroup parent = (ViewGroup) mPrimaryCallCardContainer.getParent(); final ViewTreeObserver observer = getView().getViewTreeObserver(); - mPrimaryCallInfo.getLayoutTransition().disableTransitionType(LayoutTransition.CHANGING); + final LayoutTransition transition = mPrimaryCallInfo.getLayoutTransition(); + transition.disableTransitionType(LayoutTransition.CHANGING); + + mIsAnimating = true; observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override @@ -875,26 +889,39 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr final LayoutIgnoringListener listener = new LayoutIgnoringListener(); mPrimaryCallCardContainer.addOnLayoutChangeListener(listener); - // Prepare the state of views before the circular reveal animation + // Prepare the state of views before the slide animation final int originalHeight = mPrimaryCallCardContainer.getHeight(); + mPrimaryCallCardContainer.setTag(R.id.view_tag_callcard_actual_height, + originalHeight); mPrimaryCallCardContainer.setBottom(parent.getHeight()); // Set up FAB. mFloatingActionButtonContainer.setVisibility(View.GONE); mFloatingActionButtonController.setScreenWidth(parent.getWidth()); + mCallButtonsContainer.setAlpha(0); mCallStateLabel.setAlpha(0); mPrimaryName.setAlpha(0); mCallTypeLabel.setAlpha(0); mCallNumberAndLabel.setAlpha(0); - final Animator animator = getOutgoingCallAnimator(touchPoint, - parent.getHeight(), originalHeight, showCircularReveal); + assignTranslateAnimation(mCallStateLabel, 1); + assignTranslateAnimation(mCallStateIcon, 1); + assignTranslateAnimation(mPrimaryName, 2); + assignTranslateAnimation(mCallNumberAndLabel, 3); + assignTranslateAnimation(mCallTypeLabel, 4); + assignTranslateAnimation(mCallButtonsContainer, 5); + + final Animator animator = getShrinkAnimator(parent.getHeight(), originalHeight); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + mPrimaryCallCardContainer.setTag(R.id.view_tag_callcard_actual_height, + null); setViewStatePostAnimation(listener); + mIsAnimating = false; + InCallPresenter.getInstance().onShrinkAnimationComplete(); } }); animator.start(); @@ -902,7 +929,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr }); } - public void onDialpadVisiblityChange(boolean isShown) { + public void onDialpadVisibilityChange(boolean isShown) { mIsDialpadShowing = isShown; updateFabPosition(); } @@ -972,7 +999,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr } observer.removeOnGlobalLayoutListener(this); - onDialpadVisiblityChange(mIsDialpadShowing); + onDialpadVisibilityChange(mIsDialpadShowing); } }); } @@ -983,19 +1010,12 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr * and then translated upwards together with the scrim. */ private Animator getShrinkAnimator(int startHeight, int endHeight) { - final Animator shrinkAnimator = + final ObjectAnimator shrinkAnimator = ObjectAnimator.ofInt(mPrimaryCallCardContainer, "bottom", startHeight, endHeight); shrinkAnimator.setDuration(mShrinkAnimationDuration); shrinkAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - assignTranslateAnimation(mCallStateLabel, 1); - assignTranslateAnimation(mCallStateIcon, 1); - assignTranslateAnimation(mPrimaryName, 2); - assignTranslateAnimation(mCallNumberAndLabel, 3); - assignTranslateAnimation(mCallTypeLabel, 4); - assignTranslateAnimation(mCallButtonsContainer, 5); - mFloatingActionButton.setEnabled(true); } }); @@ -1003,42 +1023,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr return shrinkAnimator; } - private Animator getRevealAnimator(Point touchPoint) { - final Activity activity = getActivity(); - final View view = activity.getWindow().getDecorView(); - final Display display = activity.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(mRevealAnimationDuration); - 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.setLayerType(View.LAYER_TYPE_HARDWARE, null); + view.buildLayer(); view.setTranslationY(mTranslationOffset * offset); view.animate().translationY(0).alpha(1).withLayer() .setDuration(mShrinkAnimationDuration).setInterpolator(AnimUtils.EASE_IN); @@ -1058,7 +1045,10 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr setViewStatePostAnimation(mCallStateIcon); mPrimaryCallCardContainer.removeOnLayoutChangeListener(layoutChangeListener); - mPrimaryCallInfo.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); + + final LayoutTransition transition = mPrimaryCallInfo.getLayoutTransition(); + transition.enableTransitionType(LayoutTransition.CHANGING); + mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY); } diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index 7eae5c602..bd3694339 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -17,6 +17,8 @@ package com.android.incallui; import android.Manifest; +import android.app.Activity; +import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -35,6 +37,7 @@ import android.telephony.PhoneNumberUtils; import android.telephony.TelephonyManager; import android.text.TextUtils; +import com.android.incallui.CircularRevealFragment.OnCircularRevealCompleteListener; import com.android.incallui.ContactInfoCache.ContactCacheEntry; import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback; import com.android.incallui.InCallPresenter.InCallDetailsListener; @@ -119,6 +122,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> updateContactEntry(null, true); } } + + onStateChange(null, InCallPresenter.getInstance().getInCallState(), CallList.getInstance()); } @Override @@ -250,7 +255,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> maybeShowManageConferenceCallButton(); - final boolean enableEndCallButton = Call.State.isConnectingOrConnected(callState) && + final boolean enableEndCallButton = (Call.State.isConnectingOrConnected(callState) + || callState == Call.State.DISCONNECTING) && callState != Call.State.INCOMING && mPrimary != null; // Hide the end call button instantly if we're receiving an incoming call. getUi().setEndCallButtonEnabled( @@ -678,9 +684,10 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> } Log.i(this, "Disconnecting call: " + mPrimary); + final String callId = mPrimary.getId(); mPrimary.setState(Call.State.DISCONNECTING); CallList.getInstance().onUpdate(mPrimary); - TelecomAdapter.getInstance().disconnectCall(mPrimary.getId()); + TelecomAdapter.getInstance().disconnectCall(callId); } private String getNumberFromHandle(Uri handle) { @@ -750,5 +757,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> void showHdAudioIndicator(boolean visible); void showManageConferenceCallButton(boolean visible); boolean isManageConferenceVisible(); + void animateForNewOutgoingCall(); } } diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index f2d04cf11..9868aef50 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -16,11 +16,13 @@ package com.android.incallui; +import com.android.contacts.common.testing.NeededForTesting; import com.google.common.collect.Maps; import com.google.common.base.Preconditions; import android.os.Handler; import android.os.Message; +import android.os.Trace; import android.telecom.DisconnectCause; import android.telecom.Phone; @@ -71,6 +73,7 @@ public class CallList implements InCallPhoneListener { private Phone.Listener mPhoneListener = new Phone.Listener() { @Override public void onCallAdded(Phone phone, android.telecom.Call telecommCall) { + Trace.beginSection("onCallAdded"); Call call = new Call(telecommCall); Log.d(this, "onCallAdded: callState=" + call.getState()); if (call.getState() == Call.State.INCOMING || @@ -79,6 +82,7 @@ public class CallList implements InCallPhoneListener { } else { onUpdate(call); } + Trace.endSection(); } @Override public void onCallRemoved(Phone phone, android.telecom.Call telecommCall) { @@ -93,9 +97,11 @@ public class CallList implements InCallPhoneListener { }; /** - * Private constructor. Instance should only be acquired through getInstance(). + * USED ONLY FOR TESTING + * Testing-only constructor. Instance should only be acquired through getInstance(). */ - private CallList() { + @NeededForTesting + CallList() { } @Override @@ -147,8 +153,10 @@ public class CallList implements InCallPhoneListener { * Called when a single call has changed. */ public void onUpdate(Call call) { + Trace.beginSection("onUpdate"); onUpdateCall(call); notifyGenericListeners(); + Trace.endSection(); } public void notifyCallUpdateListeners(Call call) { diff --git a/InCallUI/src/com/android/incallui/CircularRevealActivity.java b/InCallUI/src/com/android/incallui/CircularRevealActivity.java deleted file mode 100644 index 7a9b7ccbf..000000000 --- a/InCallUI/src/com/android/incallui/CircularRevealActivity.java +++ /dev/null @@ -1,169 +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.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/InCallUI/src/com/android/incallui/CircularRevealFragment.java b/InCallUI/src/com/android/incallui/CircularRevealFragment.java new file mode 100644 index 000000000..f8cc424fe --- /dev/null +++ b/InCallUI/src/com/android/incallui/CircularRevealFragment.java @@ -0,0 +1,164 @@ +/* + * 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.app.Fragment; +import android.app.FragmentManager; +import android.graphics.Outline; +import android.graphics.Point; +import android.os.Bundle; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; + +import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; + +public class CircularRevealFragment extends Fragment { + static final String TAG = "CircularRevealFragment"; + + private Point mTouchPoint; + private OnCircularRevealCompleteListener mListener; + private boolean mAnimationStarted; + + interface OnCircularRevealCompleteListener { + public void onCircularRevealComplete(FragmentManager fm); + } + + public static void startCircularReveal(FragmentManager fm, Point touchPoint, + OnCircularRevealCompleteListener listener) { + if (fm.findFragmentByTag(TAG) == null) { + fm.beginTransaction().add(R.id.main, + new CircularRevealFragment(touchPoint, listener), TAG) + .commitAllowingStateLoss(); + } else { + Log.w(TAG, "An instance of CircularRevealFragment already exists"); + } + } + + public static void endCircularReveal(FragmentManager fm) { + final Fragment fragment = fm.findFragmentByTag(TAG); + if (fragment != null) { + fm.beginTransaction().remove(fragment).commitAllowingStateLoss(); + } + } + + /** + * Empty constructor used only by the {@link FragmentManager}. + */ + public CircularRevealFragment() {} + + public CircularRevealFragment(Point touchPoint, OnCircularRevealCompleteListener listener) { + mTouchPoint = touchPoint; + mListener = listener; + } + + @Override + public void onResume() { + super.onResume(); + if (!mAnimationStarted) { + // Only run the animation once for each instance of the fragment + startOutgoingAnimation(InCallPresenter.getInstance().getThemeColors()); + } + mAnimationStarted = true; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.outgoing_call_animation, container, false); + } + + public void startOutgoingAnimation(MaterialPalette palette) { + final Activity activity = getActivity(); + if (activity == null) { + Log.w(this, "Asked to do outgoing call animation when not attached"); + return; + } + + final View view = activity.getWindow().getDecorView(); + + // The circle starts from an initial size of 0 so clip it such that it is invisible. + // Otherwise the first frame is drawn with a fully opaque screen which causes jank. 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); + activity.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(mTouchPoint); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + view.setClipToOutline(false); + if (mListener != null) { + mListener.onCircularRevealComplete(getFragmentManager()); + } + } + }); + animator.start(); + return false; + } + }); + } + + private Animator getRevealAnimator(Point touchPoint) { + final Activity activity = getActivity(); + final View view = activity.getWindow().getDecorView(); + final Display display = activity.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(getResources().getInteger(R.integer.reveal_animation_duration)); + return valueAnimator; + } +} diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java index 163954b2d..6b232c934 100644 --- a/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java +++ b/InCallUI/src/com/android/incallui/ConferenceManagerFragment.java @@ -29,22 +29,25 @@ import com.android.contacts.common.ContactPhotoManager; import java.util.List; /** - * Fragment for call control buttons + * Fragment that allows the user to manage a conference call. */ public class ConferenceManagerFragment extends BaseFragment<ConferenceManagerPresenter, ConferenceManagerPresenter.ConferenceManagerUi> implements ConferenceManagerPresenter.ConferenceManagerUi { + private static final String KEY_IS_VISIBLE = "key_conference_is_visible"; + private ListView mConferenceParticipantList; private int mActionBarElevation; private ContactPhotoManager mContactPhotoManager; private LayoutInflater mInflater; private ConferenceParticipantListAdapter mConferenceParticipantListAdapter; + private boolean mIsVisible; + private boolean mIsRecreating; @Override ConferenceManagerPresenter createPresenter() { - // having a singleton instance. return new ConferenceManagerPresenter(); } @@ -56,6 +59,10 @@ public class ConferenceManagerFragment @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + mIsRecreating = true; + mIsVisible = savedInstanceState.getBoolean(KEY_IS_VISIBLE); + } } @Override @@ -75,15 +82,23 @@ public class ConferenceManagerFragment } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onResume() { + super.onResume(); + if (mIsRecreating) { + onVisibilityChanged(mIsVisible); + } } @Override - public void setVisible(boolean on) { - ActionBar actionBar = getActivity().getActionBar(); + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(KEY_IS_VISIBLE, mIsVisible); + super.onSaveInstanceState(outState); + } - if (on) { + public void onVisibilityChanged(boolean isVisible) { + mIsVisible = isVisible; + ActionBar actionBar = getActivity().getActionBar(); + if (isVisible) { actionBar.setTitle(R.string.manageConferenceLabel); actionBar.setElevation(mActionBarElevation); actionBar.setHideOffset(0); @@ -91,13 +106,10 @@ 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); - actionBar.setElevation(0); actionBar.setHideOffset(actionBar.getHeight()); } diff --git a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java index 86fc18ff3..6fb6e5dda 100644 --- a/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java +++ b/InCallUI/src/com/android/incallui/ConferenceManagerPresenter.java @@ -137,7 +137,6 @@ public class ConferenceManagerPresenter } public interface ConferenceManagerUi extends Ui { - void setVisible(boolean on); boolean isFragmentVisible(); void update(Context context, List<Call> participants, boolean parentCanSeparate); void refreshCall(Call call); diff --git a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java index 7825dc1ad..62c1214c7 100644 --- a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java +++ b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java @@ -418,7 +418,7 @@ public class ConferenceParticipantListAdapter extends BaseAdapter { numberTypeTextView.setVisibility(View.GONE); } else { numberTextView.setVisibility(View.VISIBLE); - numberTextView.setText(PhoneNumberUtils.ttsSpanAsPhoneNumber(callerNumber)); + numberTextView.setText(PhoneNumberUtils.getPhoneTtsSpannable(callerNumber)); numberTypeTextView.setVisibility(View.VISIBLE); numberTypeTextView.setText(callerNumberType); } diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java index da3f0cb74..5a02b8b0e 100644 --- a/InCallUI/src/com/android/incallui/DialpadFragment.java +++ b/InCallUI/src/com/android/incallui/DialpadFragment.java @@ -29,13 +29,11 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityManager; 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; @@ -83,30 +81,6 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese } } - /** - * LinearLayout that always returns true for onHoverEvent callbacks, to fix - * problems with accessibility due to the dialpad overlaying other fragments. - */ - public static class HoverIgnoringLinearLayout extends LinearLayout { - - public HoverIgnoringLinearLayout(Context context) { - super(context); - } - - public HoverIgnoringLinearLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public HoverIgnoringLinearLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public boolean onHoverEvent(MotionEvent event) { - return true; - } - } - private EditText mDtmfDialerField; /** Hash Map to map a view id to a character*/ @@ -445,11 +419,6 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese } @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View parent = inflater.inflate( @@ -514,7 +483,7 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese * @param text Text to set Dialpad EditText to. */ public void setDtmfText(String text) { - mDtmfDialerField.setText(PhoneNumberUtils.ttsSpanAsPhoneNumber(text)); + mDtmfDialerField.setText(PhoneNumberUtils.getPhoneTtsSpannable(text)); } @Override diff --git a/InCallUI/src/com/android/incallui/FragmentDisplayManager.java b/InCallUI/src/com/android/incallui/FragmentDisplayManager.java new file mode 100644 index 000000000..045d999a0 --- /dev/null +++ b/InCallUI/src/com/android/incallui/FragmentDisplayManager.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.incallui; + +import android.app.Fragment; + +interface FragmentDisplayManager { + public void onFragmentAttached(Fragment fragment); +} diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java index 873c288a4..1283177d6 100644 --- a/InCallUI/src/com/android/incallui/InCallActivity.java +++ b/InCallUI/src/com/android/incallui/InCallActivity.java @@ -18,7 +18,10 @@ package com.android.incallui; import android.app.ActionBar; import android.app.Activity; +import android.app.ActivityManager; import android.app.AlertDialog; +import android.app.DialogFragment; +import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; @@ -27,14 +30,11 @@ import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.res.Configuration; -import android.content.res.Resources; import android.graphics.Point; -import android.net.Uri; import android.os.Bundle; +import android.os.Trace; import android.telecom.DisconnectCause; import android.telecom.PhoneAccountHandle; -import android.telecom.TelecomManager; -import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.view.MenuItem; import android.view.animation.Animation; @@ -48,8 +48,6 @@ import android.view.accessibility.AccessibilityEvent; 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; @@ -59,14 +57,21 @@ import java.util.List; import java.util.Locale; /** - * Phone app "in call" screen. + * Main activity that the user interacts with while in a live call. */ -public class InCallActivity extends Activity { +public class InCallActivity extends Activity implements FragmentDisplayManager { + + public static final String TAG = InCallActivity.class.getSimpleName(); 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_EXTRA = "InCallActivity.new_outgoing_call"; - public static final String SHOW_CIRCULAR_REVEAL_EXTRA = "InCallActivity.show_circular_reveal"; + + private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment"; + private static final String TAG_CONFERENCE_FRAGMENT = "tag_conference_manager_fragment"; + private static final String TAG_CALLCARD_FRAGMENT = "tag_callcard_fragment"; + private static final String TAG_ANSWER_FRAGMENT = "tag_answer_fragment"; + private static final String TAG_SELECT_ACCT_FRAGMENT = "tag_select_acct_fragment"; private CallButtonFragment mCallButtonFragment; private CallCardFragment mCallCardFragment; @@ -100,7 +105,20 @@ public class InCallActivity extends Activity { AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() { @Override public void onAnimationEnd(Animation animation) { - showDialpad(false); + showFragment(TAG_DIALPAD_FRAGMENT, false, true); + } + }; + + private SelectPhoneAccountListener mSelectAcctListener = new SelectPhoneAccountListener() { + @Override + public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle, + boolean setDefault) { + InCallPresenter.getInstance().handleAccountSelection(selectedAccountHandle, + setDefault); + } + @Override + public void onDialogDismissed() { + InCallPresenter.getInstance().cancelAccountSelection(); } }; @@ -135,11 +153,8 @@ public class InCallActivity extends Activity { // TODO(klp): Do we need to add this back when prox sensor is not available? // lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; - // Inflate everything in incall_screen.xml and add it to the screen. setContentView(R.layout.incall_screen); - initializeInCall(); - internalResolveIntent(getIntent()); mIsLandscape = getResources().getConfiguration().orientation == @@ -171,13 +186,21 @@ public class InCallActivity extends Activity { mShowDialpadRequested = icicle.getBoolean(SHOW_DIALPAD_EXTRA); mAnimateDialpadOnShow = false; mDtmfText = icicle.getString(DIALPAD_TEXT_EXTRA); + + SelectPhoneAccountDialogFragment dialogFragment = (SelectPhoneAccountDialogFragment) + getFragmentManager().findFragmentByTag(TAG_SELECT_ACCT_FRAGMENT); + if (dialogFragment != null) { + dialogFragment.setListener(mSelectAcctListener); + } } Log.d(this, "onCreate(): exit"); } @Override protected void onSaveInstanceState(Bundle out) { - out.putBoolean(SHOW_DIALPAD_EXTRA, mCallButtonFragment.isDialpadVisible()); + // TODO: The dialpad fragment should handle this as part of its own state + out.putBoolean(SHOW_DIALPAD_EXTRA, + mCallButtonFragment != null && mCallButtonFragment.isDialpadVisible()); if (mDialpadFragment != null) { out.putString(DIALPAD_TEXT_EXTRA, mDialpadFragment.getDtmfText()); } @@ -262,37 +285,45 @@ public class InCallActivity extends Activity { } /** + * When fragments have a parent fragment, onAttachFragment is not called on the parent + * activity. To fix this, register our own callback instead that is always called for + * all fragments. + * + * @see {@link BaseFragment#onAttach(Activity)} + */ + @Override + public void onFragmentAttached(Fragment fragment) { + if (fragment instanceof DialpadFragment) { + mDialpadFragment = (DialpadFragment) fragment; + } else if (fragment instanceof AnswerFragment) { + mAnswerFragment = (AnswerFragment) fragment; + } else if (fragment instanceof CallCardFragment) { + mCallCardFragment = (CallCardFragment) fragment; + mChildFragmentManager = mCallCardFragment.getChildFragmentManager(); + } else if (fragment instanceof ConferenceManagerFragment) { + mConferenceManagerFragment = (ConferenceManagerFragment) fragment; + } else if (fragment instanceof CallButtonFragment) { + mCallButtonFragment = (CallButtonFragment) fragment; + } + } + + /** * Returns true when theActivity is in foreground (between onResume and onPause). */ /* package */ boolean isForegroundActivity() { return mIsForegroundActivity; } - private boolean hasPendingErrorDialog() { - return mDialog != null; + private boolean hasPendingDialogs() { + return mDialog != null || (mAnswerFragment != null && mAnswerFragment.hasPendingDialogs()); } - /** - * Dismisses the in-call screen. - * - * We never *really* finish() the InCallActivity, since we don't want to get destroyed and then - * have to be re-created from scratch for the next call. Instead, we just move ourselves to the - * back of the activity stack. - * - * This also means that we'll no longer be reachable via the BACK button (since moveTaskToBack() - * puts us behind the Home app, but the home app doesn't allow the BACK key to move you any - * farther down in the history stack.) - * - * (Since the Phone app itself is never killed, this basically means that we'll keep a single - * InCallActivity instance around for the entire uptime of the device. This noticeably improves - * the UI responsiveness for incoming calls.) - */ @Override public void finish() { Log.i(this, "finish(). Dialog showing: " + (mDialog != null)); // skip finish if we are still showing a dialog. - if (!hasPendingErrorDialog() && !mAnswerFragment.hasPendingDialogs()) { + if (!hasPendingDialogs()) { super.finish(); } } @@ -325,22 +356,23 @@ public class InCallActivity extends Activity { // BACK is also used to exit out of any "special modes" of the // in-call UI: - if (!mConferenceManagerFragment.isVisible() && !mCallCardFragment.isVisible()) { + if ((mConferenceManagerFragment == null || !mConferenceManagerFragment.isVisible()) + && !mCallCardFragment.isVisible()) { return; } if (mDialpadFragment != null && mDialpadFragment.isVisible()) { mCallButtonFragment.displayDialpad(false /* show */, true /* animate */); return; - } else if (mConferenceManagerFragment.isVisible()) { - showConferenceCallManager(false); + } else if (mConferenceManagerFragment != null && mConferenceManagerFragment.isVisible()) { + showConferenceFragment(false); return; } // Always disable the Back key while an incoming call is ringing final Call call = CallList.getInstance().getIncomingCall(); if (call != null) { - Log.d(this, "Consume Back press for an incoming call"); + Log.i(this, "Consume Back press for an incoming call"); return; } @@ -436,13 +468,6 @@ public class InCallActivity extends Activity { // key events to the DTMFDialer's onDialerKeyDown. if (mDialpadFragment != null && mDialpadFragment.isVisible()) { return mDialpadFragment.onDialerKeyDown(event); - - // TODO: If the dialpad isn't currently visible, maybe - // consider automatically bringing it up right now? - // (Just to make sure the user sees the digits widget...) - // But this probably isn't too critical since it's awkward to - // use the hard keyboard while in-call in the first place, - // especially now that the in-call UI is portrait-only... } return false; @@ -479,10 +504,13 @@ public class InCallActivity extends Activity { return mCallCardFragment; } + public AnswerFragment getAnswerFragment() { + return mAnswerFragment; + } + private void internalResolveIntent(Intent intent) { final String action = intent.getAction(); - - if (action.equals(intent.ACTION_MAIN)) { + if (action.equals(Intent.ACTION_MAIN)) { // This action is the normal way to bring up the in-call UI. // // But we do check here for one extra that can come along with the @@ -499,6 +527,7 @@ public class InCallActivity extends Activity { relaunchedFromDialer(showDialpad); } + boolean newOutgoingCall = false; if (intent.getBooleanExtra(NEW_OUTGOING_CALL_EXTRA, false)) { intent.removeExtra(NEW_OUTGOING_CALL_EXTRA); Call call = CallList.getInstance().getOutgoingCall(); @@ -526,12 +555,9 @@ public class InCallActivity extends Activity { } } - // 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); + // Start animation for new outgoing call + CircularRevealFragment.startCircularReveal(getFragmentManager(), touchPoint, + InCallPresenter.getInstance()); // InCallActivity is responsible for disconnecting a new outgoing call if there // is no way of making it (i.e. no valid call capable accounts) @@ -540,11 +566,12 @@ public class InCallActivity extends Activity { } dismissKeyguard(true); + newOutgoingCall = true; } Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall(); if (pendingAccountSelectionCall != null) { - mCallCardFragment.setVisible(false); + showCallCardFragment(false); Bundle extras = pendingAccountSelectionCall .getTelecommCall().getDetails().getExtras(); @@ -556,24 +583,12 @@ 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(), + DialogFragment dialogFragment = SelectPhoneAccountDialogFragment.newInstance( R.string.select_phone_account_for_calls, true, phoneAccountHandles, - listener); - } else { - mCallCardFragment.setVisible(true); + mSelectAcctListener); + dialogFragment.show(getFragmentManager(), TAG_SELECT_ACCT_FRAGMENT); + } else if (!newOutgoingCall) { + showCallCardFragment(true); } return; @@ -594,40 +609,6 @@ public class InCallActivity extends Activity { } } - private void initializeInCall() { - if (mCallCardFragment == null) { - mCallCardFragment = (CallCardFragment) getFragmentManager() - .findFragmentById(R.id.callCardFragment); - } - - mChildFragmentManager = mCallCardFragment.getChildFragmentManager(); - - if (mCallButtonFragment == null) { - mCallButtonFragment = (CallButtonFragment) mChildFragmentManager - .findFragmentById(R.id.callButtonFragment); - mCallButtonFragment.getView().setVisibility(View.INVISIBLE); - } - - if (mAnswerFragment == null) { - mAnswerFragment = (AnswerFragment) mChildFragmentManager - .findFragmentById(R.id.answerFragment); - } - - if (mConferenceManagerFragment == null) { - mConferenceManagerFragment = (ConferenceManagerFragment) getFragmentManager() - .findFragmentById(R.id.conferenceManagerFragment); - mConferenceManagerFragment.getView().setVisibility(View.INVISIBLE); - } - } - - /** - * Simulates a user click to hide the dialpad. This will update the UI to show the call card, - * update the checked state of the dialpad button, and update the proximity sensor state. - */ - public void hideDialpadForDisconnect() { - mCallButtonFragment.displayDialpad(false /* show */, true /* animate */); - } - public void dismissKeyguard(boolean dismiss) { if (mDismissKeyguard == dismiss) { return; @@ -640,67 +621,134 @@ public class InCallActivity extends Activity { } } - private void showDialpad(boolean showDialpad) { - // If the dialpad is being shown and it has not already been loaded, replace the dialpad - // placeholder with the actual fragment before continuing. - if (mDialpadFragment == null && showDialpad) { - final FragmentTransaction loadTransaction = mChildFragmentManager.beginTransaction(); - View fragmentContainer = findViewById(R.id.dialpadFragmentContainer); - mDialpadFragment = new DialpadFragment(); - loadTransaction.replace(fragmentContainer.getId(), mDialpadFragment, - DialpadFragment.class.getName()); - loadTransaction.commitAllowingStateLoss(); - mChildFragmentManager.executePendingTransactions(); + private void showFragment(String tag, boolean show, boolean executeImmediately) { + Trace.beginSection("showFragment - " + tag); + final FragmentManager fm = getFragmentManagerForTag(tag); + + if (fm == null) { + Log.w(TAG, "Fragment manager is null for : " + tag); + return; + } + + Fragment fragment = fm.findFragmentByTag(tag); + if (!show && fragment == null) { + // Nothing to show, so bail early. + return; } - final FragmentTransaction ft = mChildFragmentManager.beginTransaction(); - if (showDialpad) { - ft.show(mDialpadFragment); + final FragmentTransaction transaction = fm.beginTransaction(); + if (show) { + if (fragment == null) { + fragment = createNewFragmentForTag(tag); + transaction.add(getContainerIdForFragment(tag), fragment, tag); + } else { + transaction.show(fragment); + } } else { - ft.hide(mDialpadFragment); + transaction.hide(fragment); + } + + transaction.commitAllowingStateLoss(); + if (executeImmediately) { + fm.executePendingTransactions(); } - ft.commitAllowingStateLoss(); + Trace.endSection(); } - public void displayDialpad(boolean showDialpad, boolean animate) { + private Fragment createNewFragmentForTag(String tag) { + if (TAG_DIALPAD_FRAGMENT.equals(tag)) { + mDialpadFragment = new DialpadFragment(); + return mDialpadFragment; + } else if (TAG_ANSWER_FRAGMENT.equals(tag)) { + mAnswerFragment = new AnswerFragment(); + return mAnswerFragment; + } else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) { + mConferenceManagerFragment = new ConferenceManagerFragment(); + return mConferenceManagerFragment; + } else if (TAG_CALLCARD_FRAGMENT.equals(tag)) { + mCallCardFragment = new CallCardFragment(); + return mCallCardFragment; + } + throw new IllegalStateException("Unexpected fragment: " + tag); + } + + private FragmentManager getFragmentManagerForTag(String tag) { + if (TAG_DIALPAD_FRAGMENT.equals(tag)) { + return mChildFragmentManager; + } else if (TAG_ANSWER_FRAGMENT.equals(tag)) { + return mChildFragmentManager; + } else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) { + return getFragmentManager(); + } else if (TAG_CALLCARD_FRAGMENT.equals(tag)) { + return getFragmentManager(); + } + throw new IllegalStateException("Unexpected fragment: " + tag); + } + + private int getContainerIdForFragment(String tag) { + if (TAG_DIALPAD_FRAGMENT.equals(tag)) { + return R.id.answer_and_dialpad_container; + } else if (TAG_ANSWER_FRAGMENT.equals(tag)) { + return R.id.answer_and_dialpad_container; + } else if (TAG_CONFERENCE_FRAGMENT.equals(tag)) { + return R.id.main; + } else if (TAG_CALLCARD_FRAGMENT.equals(tag)) { + return R.id.main; + } + throw new IllegalStateException("Unexpected fragment: " + tag); + } + + public void showDialpadFragment(boolean show, boolean animate) { // If the dialpad is already visible, don't animate in. If it's gone, don't animate out. - if ((showDialpad && isDialpadVisible()) || (!showDialpad && !isDialpadVisible())) { + if ((show && isDialpadVisible()) || (!show && !isDialpadVisible())) { return; } // We don't do a FragmentTransaction on the hide case because it will be dealt with when // the listener is fired after an animation finishes. if (!animate) { - showDialpad(showDialpad); + showFragment(TAG_DIALPAD_FRAGMENT, show, true); } else { - if (showDialpad) { - showDialpad(true); + if (show) { + showFragment(TAG_DIALPAD_FRAGMENT, true, true); mDialpadFragment.animateShowDialpad(); } - mCallCardFragment.onDialpadVisiblityChange(showDialpad); - mDialpadFragment.getView().startAnimation(showDialpad ? mSlideIn : mSlideOut); + mCallCardFragment.onDialpadVisibilityChange(show); + mDialpadFragment.getView().startAnimation(show ? mSlideIn : mSlideOut); } - InCallPresenter.getInstance().getProximitySensor().onDialpadVisible(showDialpad); + final ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor(); + if (sensor != null) { + sensor.onDialpadVisible(show); + } } public boolean isDialpadVisible() { return mDialpadFragment != null && mDialpadFragment.isVisible(); } + public void showCallCardFragment(boolean show) { + showFragment(TAG_CALLCARD_FRAGMENT, show, 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); + public void showConferenceFragment(boolean show) { + showFragment(TAG_CONFERENCE_FRAGMENT, show, true); + mConferenceManagerFragment.onVisibilityChanged(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 showAnswerFragment(boolean show) { + showFragment(TAG_ANSWER_FRAGMENT, show, true); + } + public void showPostCharWaitDialog(String callId, String chars) { if (isForegroundActivity()) { final PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars); @@ -739,7 +787,9 @@ public class InCallActivity extends Activity { mDialog.dismiss(); mDialog = null; } - mAnswerFragment.dismissPendingDialogues(); + if (mAnswerFragment != null) { + mAnswerFragment.dismissPendingDialogs(); + } } /** diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index 74c85db8a..fdf193318 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -16,14 +16,13 @@ package com.android.incallui; -import android.app.Activity; +import android.app.FragmentManager; import android.content.Context; import android.content.Intent; 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.Phone; @@ -40,6 +39,7 @@ import android.view.WindowManager; import com.google.common.base.Preconditions; import com.android.contacts.common.interactions.TouchPointManager; +import com.android.contacts.common.testing.NeededForTesting; import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; import com.android.incalluibind.ObjectFactory; @@ -59,7 +59,8 @@ import java.util.concurrent.CopyOnWriteArrayList; * that want to listen in on the in-call state changes. * TODO: This class has become more of a state machine at this point. Consider renaming. */ -public class InCallPresenter implements CallList.Listener, InCallPhoneListener { +public class InCallPresenter implements CallList.Listener, InCallPhoneListener, + CircularRevealFragment.OnCircularRevealCompleteListener { private static final String EXTRA_FIRST_TIME_SHOWN = "com.android.incallui.intent.extra.FIRST_TIME_SHOWN"; @@ -98,6 +99,17 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { private boolean mServiceConnected = false; private boolean mAccountSelectionCancelled = false; private InCallCameraManager mInCallCameraManager = null; + private AnswerPresenter mAnswerPresenter = new AnswerPresenter(); + + /** + * Whether or not we are currently bound and waiting for Telecom to send us a new call. + */ + private boolean mBoundAndWaitingForOutgoingCall; + /** + * If there is no actual call currently in the call list, this will be used as a fallback + * to determine the theme color for InCallUI. + */ + private PhoneAccountHandle mPendingPhoneAccountHandle; private final Phone.Listener mPhoneListener = new Phone.Listener() { @Override @@ -107,6 +119,9 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } @Override public void onCallAdded(Phone phone, android.telecom.Call call) { + // Since a call has been added we are no longer waiting for Telecom to send us a + // call. + setBoundAndWaitingForOutgoingCall(false, null); call.addListener(mCallListener); } @Override @@ -125,8 +140,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { new android.telecom.Call.Listener() { @Override public void onPostDialWait(android.telecom.Call call, String remainingPostDialSequence) { - onPostDialCharWait( - CallList.getInstance().getCallByTelecommCall(call).getId(), + onPostDialCharWait(mCallList.getCallByTelecommCall(call).getId(), remainingPostDialSequence); } @@ -134,7 +148,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { public void onDetailsChanged(android.telecom.Call call, android.telecom.Call.Details details) { for (InCallDetailsListener listener : mDetailsListeners) { - listener.onDetailsChanged(CallList.getInstance().getCallByTelecommCall(call), + listener.onDetailsChanged(mCallList.getCallByTelecommCall(call), details); } } @@ -144,7 +158,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { android.telecom.Call call, List<android.telecom.Call> conferenceableCalls) { Log.i(this, "onConferenceableCallsChanged: " + call); for (InCallDetailsListener listener : mDetailsListeners) { - listener.onDetailsChanged(CallList.getInstance().getCallByTelecommCall(call), + listener.onDetailsChanged(mCallList.getCallByTelecommCall(call), call.getDetails()); } } @@ -159,19 +173,6 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { private boolean mIsActivityPreviouslyStarted = 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; @@ -186,8 +187,6 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { private Phone mPhone; - private Handler mHandler = new Handler(); - /** Display colors for the UI. Consists of a primary color and secondary (darker) color */ private MaterialPalette mThemeColors; @@ -200,6 +199,11 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { return sInCallPresenter; } + @NeededForTesting + static synchronized void setInstance(InCallPresenter inCallPresenter) { + sInCallPresenter = inCallPresenter; + } + @Override public void setPhone(Phone phone) { mPhone = phone; @@ -220,7 +224,12 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { return mCallList; } - public void setUp(Context context, CallList callList, AudioModeProvider audioModeProvider) { + public void setUp(Context context, + CallList callList, + AudioModeProvider audioModeProvider, + StatusBarNotifier statusBarNotifier, + ContactInfoCache contactInfoCache, + ProximitySensor proximitySensor) { if (mServiceConnected) { Log.i(this, "New service connection replacing existing one."); // retain the current resources, no need to create new ones. @@ -233,14 +242,14 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { Preconditions.checkNotNull(context); mContext = context; - mContactInfoCache = ContactInfoCache.getInstance(context); + mContactInfoCache = contactInfoCache; - mStatusBarNotifier = new StatusBarNotifier(context, mContactInfoCache); + mStatusBarNotifier = statusBarNotifier; addListener(mStatusBarNotifier); mAudioModeProvider = audioModeProvider; - mProximitySensor = new ProximitySensor(context, mAudioModeProvider); + mProximitySensor = proximitySensor; addListener(mProximitySensor); mCallList = callList; @@ -274,13 +283,6 @@ 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); if (doFinish) { @@ -400,6 +402,8 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } } + private boolean mAwaitingCallListUpdate = false; + /** * Called when there is a change to the call list. * Sets the In-Call state for the entire in-call app based on the information it gets from @@ -408,10 +412,17 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { */ @Override public void onCallListChange(CallList callList) { + if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null && + mInCallActivity.getCallCardFragment().isAnimating()) { + mAwaitingCallListUpdate = true; + return; + } if (callList == null) { return; } - Log.d(this, "onCallListChange callList=" + callList.toString() ); + + mAwaitingCallListUpdate = false; + InCallState newState = getPotentialStateFromCallList(callList); InCallState oldState = mInCallState; Log.d(this, "onCallListChange oldState= " + oldState + " newState=" + newState); @@ -463,11 +474,10 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { */ @Override public void onDisconnect(Call call) { - hideDialpadForDisconnect(); maybeShowErrorDialogOnDisconnect(call); // We need to do the run the same code as onCallListChange. - onCallListChange(CallList.getInstance()); + onCallListChange(mCallList); if (isActivityStarted()) { mInCallActivity.dismissKeyguard(false); @@ -477,7 +487,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { /** * Given the call list, return the state in which the in-call screen should be. */ - public static InCallState getPotentialStateFromCallList(CallList callList) { + public InCallState getPotentialStateFromCallList(CallList callList) { InCallState newState = InCallState.NO_CALLS; @@ -499,9 +509,47 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { newState = InCallState.INCALL; } + if (newState == InCallState.NO_CALLS) { + if (mBoundAndWaitingForOutgoingCall) { + return InCallState.OUTGOING; + } + } + return newState; } + public boolean isBoundAndWaitingForOutgoingCall() { + return mBoundAndWaitingForOutgoingCall; + } + + public void setBoundAndWaitingForOutgoingCall(boolean isBound, PhoneAccountHandle handle) { + // NOTE: It is possible for there to be a race and have handle become null before + // the circular reveal starts. This should not cause any problems because CallCardFragment + // should fallback to the actual call in the CallList at that point in time to determine + // the theme color. + Log.i(this, "setBoundAndWaitingForOutgoingCall: " + isBound); + mBoundAndWaitingForOutgoingCall = isBound; + mPendingPhoneAccountHandle = handle; + if (isBound && mInCallState == InCallState.NO_CALLS) { + mInCallState = InCallState.OUTGOING; + } + } + + @Override + public void onCircularRevealComplete(FragmentManager fm) { + if (mInCallActivity != null) { + mInCallActivity.showCallCardFragment(true); + mInCallActivity.getCallCardFragment().animateForNewOutgoingCall(); + CircularRevealFragment.endCircularReveal(mInCallActivity.getFragmentManager()); + } + } + + public void onShrinkAnimationComplete() { + if (mAwaitingCallListUpdate) { + onCallListChange(mCallList); + } + } + public void addIncomingCallListener(IncomingCallListener listener) { Preconditions.checkNotNull(listener); mIncomingCallListeners.add(listener); @@ -573,19 +621,23 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } public void handleAccountSelection(PhoneAccountHandle accountHandle, boolean setDefault) { - Call call = mCallList.getWaitingForAccountCall(); - if (call != null) { - String callId = call.getId(); - TelecomAdapter.getInstance().phoneAccountSelected(callId, accountHandle, setDefault); + if (mCallList != null) { + Call call = mCallList.getWaitingForAccountCall(); + if (call != null) { + String callId = call.getId(); + TelecomAdapter.getInstance().phoneAccountSelected(callId, accountHandle, setDefault); + } } } public void cancelAccountSelection() { mAccountSelectionCancelled = true; - Call call = mCallList.getWaitingForAccountCall(); - if (call != null) { - String callId = call.getId(); - TelecomAdapter.getInstance().disconnectCall(callId); + if (mCallList != null) { + Call call = mCallList.getWaitingForAccountCall(); + if (call != null) { + String callId = call.getId(); + TelecomAdapter.getInstance().disconnectCall(callId); + } } } @@ -752,7 +804,6 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { if (showing) { mIsActivityPreviouslyStarted = true; } else { - CircularRevealActivity.sendClearDisplayBroadcast(mContext); updateIsChangingConfigurations(); } @@ -825,7 +876,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { /** * INCOMING CALL */ - final CallList calls = CallList.getInstance(); + final CallList calls = mCallList; final Call incomingCall = calls.getIncomingCall(); Log.v(this, "incomingCall: " + incomingCall); @@ -922,15 +973,6 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } /** - * Hides the dialpad. Called when a call is disconnected (Requires hiding dialpad). - */ - private void hideDialpadForDisconnect() { - if (isActivityStarted()) { - mInCallActivity.hideDialpadForDisconnect(); - } - } - - /** * When the state of in-call changes, this is the first method to get called. It determines if * the UI needs to be started or finished depending on the new state and does it. */ @@ -968,8 +1010,9 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { // [ AND NOW YOU'RE IN THE CALL. voila! ] // // Our app is started using a fullScreen notification. We need to do this whenever - // we get an incoming call. - final boolean startStartupSequence = (InCallState.INCOMING == newState); + // we get an incoming call. Depending on the current context of the device, either a + // incoming call HUN or the actual InCallActivity will be shown. + final boolean startIncomingCallSequence = (InCallState.INCOMING == newState); // A dialog to show on top of the InCallUI to select a PhoneAccount final boolean showAccountPicker = (InCallState.WAITING_FOR_ACCOUNT == newState); @@ -1002,7 +1045,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { // 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()); + && isCallWithNoValidAccounts(mCallList.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 @@ -1017,7 +1060,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { if (showCallUi || showAccountPicker) { Log.i(this, "Start in call UI"); showInCall(false /* showDialpad */, !showAccountPicker /* newOutgoingCall */); - } else if (startStartupSequence) { + } else if (startIncomingCallSequence) { Log.i(this, "Start Full Screen in call UI"); // We're about the bring up the in-call UI for an incoming call. If we still have @@ -1051,7 +1094,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { */ public static boolean isCallWithNoValidAccounts(Call call) { if (call != null && !isEmergencyCall(call)) { - Bundle extras = call.getTelecommCall().getDetails().getExtras(); + Bundle extras = call.getExtras(); if (extras == null) { extras = EMPTY_EXTRAS; @@ -1188,41 +1231,8 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } 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() { - if (mContext == null) { - Log.e(this, "Setup did not occur before showing the InCallActivity."); - activity.finish(); - return; - } - - 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; - } + Log.i(this, "Showing InCallActivity"); + mContext.startActivity(getInCallIntent(showDialpad, newOutgoingCall)); } public void onServiceBind() { @@ -1230,6 +1240,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } public void onServiceUnbind() { + InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(false, null); mServiceBound = false; } @@ -1254,43 +1265,26 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { 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)); - } + InCallPresenter.getInstance().setBoundAndWaitingForOutgoingCall(true, accountHandle); - 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; + final Intent incallIntent = getInCallIntent(false, true); + incallIntent.putExtra(TouchPointManager.TOUCH_POINT, touchPoint); + mContext.startActivity(incallIntent); } - 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) { + public Intent getInCallIntent(boolean showDialpad, boolean newOutgoingCall) { 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.FLAG_ACTIVITY_NO_USER_ACTION + | 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_EXTRA, newOutgoingCall); - intent.putExtra(InCallActivity.SHOW_CIRCULAR_REVEAL_EXTRA, showCircularReveal); return intent; } @@ -1400,7 +1394,10 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { * @return The space beside the call card. */ public float getSpaceBesideCallCard() { - return mInCallActivity.getCallCardFragment().getSpaceBesideCallCard(); + if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null) { + return mInCallActivity.getCallCardFragment().getSpaceBesideCallCard(); + } + return 0; } /** @@ -1409,7 +1406,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { * @return True if the call card fragment is visible. */ public boolean getCallCardFragmentVisible() { - if (mInCallActivity != null) { + if (mInCallActivity != null && mInCallActivity.getCallCardFragment() != null) { return mInCallActivity.getCallCardFragment().isVisible(); } return false; @@ -1426,7 +1423,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { return; } - mInCallActivity.showConferenceCallManager(show); + mInCallActivity.showConferenceFragment(show); } /** @@ -1443,7 +1440,7 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { */ public void setThemeColors() { // This method will set the background to default if the color is PhoneAccount.NO_COLOR. - mThemeColors = getColorsFromCall(CallList.getInstance().getFirstCall()); + mThemeColors = getColorsFromCall(mCallList.getFirstCall()); if (mInCallActivity == null) { return; @@ -1460,7 +1457,11 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } private MaterialPalette getColorsFromCall(Call call) { - return getColorsFromPhoneAccountHandle(call == null ? null : call.getAccountHandle()); + if (call == null) { + return getColorsFromPhoneAccountHandle(mPendingPhoneAccountHandle); + } else { + return getColorsFromPhoneAccountHandle(call.getAccountHandle()); + } } private MaterialPalette getColorsFromPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) { @@ -1492,10 +1493,20 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { return mTelecomManager; } + InCallActivity getActivity() { + return mInCallActivity; + } + + AnswerPresenter getAnswerPresenter() { + return mAnswerPresenter; + } + /** * Private constructor. Must use getInstance() to get this singleton. */ private InCallPresenter() { + addIncomingCallListener(mAnswerPresenter); + addInCallUiListener(mAnswerPresenter); } /** diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java index 17f4e174d..adb069784 100644 --- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java +++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java @@ -16,6 +16,7 @@ package com.android.incallui; +import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.telecom.InCallService; @@ -53,10 +54,16 @@ public class InCallServiceImpl extends InCallService { @Override public IBinder onBind(Intent intent) { + final Context context = getApplicationContext(); + final ContactInfoCache contactInfoCache = ContactInfoCache.getInstance(context); InCallPresenter.getInstance().setUp( getApplicationContext(), CallList.getInstance(), - AudioModeProvider.getInstance()); + AudioModeProvider.getInstance(), + new StatusBarNotifier(context, contactInfoCache), + contactInfoCache, + new ProximitySensor(context, AudioModeProvider.getInstance()) + ); InCallPresenter.getInstance().onServiceBind(); InCallPresenter.getInstance().maybeStartRevealAnimation(intent); return super.onBind(intent); diff --git a/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java b/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java index 1f61070ed..d65580efe 100644 --- a/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java +++ b/InCallUI/src/com/android/incallui/InCallUIMaterialColorMapUtils.java @@ -45,8 +45,8 @@ public class InCallUIMaterialColorMapUtils extends MaterialColorMapUtils { } 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); + final int primaryColor = resources.getColor(R.color.dialer_theme_color, null); + final int secondaryColor = resources.getColor(R.color.dialer_theme_color_dark, null); return new MaterialPalette(primaryColor, secondaryColor); } }
\ No newline at end of file diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallListener.java b/InCallUI/src/com/android/incallui/InCallVideoCallListener.java index cf2b859e0..df9dfdce7 100644 --- a/InCallUI/src/com/android/incallui/InCallVideoCallListener.java +++ b/InCallUI/src/com/android/incallui/InCallVideoCallListener.java @@ -146,7 +146,9 @@ public class InCallVideoCallListener extends VideoCall.Listener { */ @Override public void onCameraCapabilitiesChanged(CameraCapabilities cameraCapabilities) { - InCallVideoCallListenerNotifier.getInstance().cameraDimensionsChanged( - mCall, cameraCapabilities.getWidth(), cameraCapabilities.getHeight()); + if (cameraCapabilities != null) { + InCallVideoCallListenerNotifier.getInstance().cameraDimensionsChanged( + mCall, cameraCapabilities.getWidth(), cameraCapabilities.getHeight()); + } } } diff --git a/InCallUI/src/com/android/incallui/PostCharDialogFragment.java b/InCallUI/src/com/android/incallui/PostCharDialogFragment.java index e1edbc75b..09b626a92 100644 --- a/InCallUI/src/com/android/incallui/PostCharDialogFragment.java +++ b/InCallUI/src/com/android/incallui/PostCharDialogFragment.java @@ -62,7 +62,6 @@ public class PostCharDialogFragment extends DialogFragment { }); final AlertDialog dialog = builder.create(); - dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); return dialog; } diff --git a/InCallUI/src/com/android/incallui/SmallerHitTargetTouchListener.java b/InCallUI/src/com/android/incallui/SmallerHitTargetTouchListener.java deleted file mode 100644 index 83feaf54a..000000000 --- a/InCallUI/src/com/android/incallui/SmallerHitTargetTouchListener.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2012 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.view.MotionEvent; -import android.view.View; - -/** - * OnTouchListener used to shrink the "hit target" of some onscreen buttons. - * - * We do this for a few specific buttons which are vulnerable to - * "false touches" because either (1) they're near the edge of the - * screen and might be unintentionally touched while holding the - * device in your hand, (2) they're in the upper corners and might - * be touched by the user's ear before the prox sensor has a chance to - * kick in, or (3) they are close to other buttons. - */ -public class SmallerHitTargetTouchListener implements View.OnTouchListener { - private static final String TAG = "SmallerHitTargetTouchListener"; - - /** - * Edge dimensions where a touch does not register an action (in DIP). - */ - private static final int HIT_TARGET_EDGE_IGNORE_DP_X = 30; - private static final int HIT_TARGET_EDGE_IGNORE_DP_Y = 10; - private static final int HIT_TARGET_MIN_SIZE_DP_X = HIT_TARGET_EDGE_IGNORE_DP_X * 3; - private static final int HIT_TARGET_MIN_SIZE_DP_Y = HIT_TARGET_EDGE_IGNORE_DP_Y * 3; - - // True if the most recent DOWN event was a "hit". - boolean mDownEventHit; - - /** - * Called when a touch event is dispatched to a view. This allows listeners to - * get a chance to respond before the target view. - * - * @return True if the listener has consumed the event, false otherwise. - * (In other words, we return true when the touch is *outside* - * the "smaller hit target", which will prevent the actual - * button from handling these events.) - */ - @Override - public boolean onTouch(View v, MotionEvent event) { - // if (DBG) log("SmallerHitTargetTouchListener: " + v + ", event " + event); - - if (event.getAction() == MotionEvent.ACTION_DOWN) { - // Note that event.getX() and event.getY() are already - // translated into the View's coordinates. (In other words, - // "0,0" is a touch on the upper-left-most corner of the view.) - final int touchX = (int) event.getX(); - final int touchY = (int) event.getY(); - - final int viewWidth = v.getWidth(); - final int viewHeight = v.getHeight(); - - final float pixelDensity = v.getResources().getDisplayMetrics().density; - final int targetMinSizeX = (int) (HIT_TARGET_MIN_SIZE_DP_X * pixelDensity); - final int targetMinSizeY = (int) (HIT_TARGET_MIN_SIZE_DP_Y * pixelDensity); - - int edgeIgnoreX = (int) (HIT_TARGET_EDGE_IGNORE_DP_X * pixelDensity); - int edgeIgnoreY = (int) (HIT_TARGET_EDGE_IGNORE_DP_Y * pixelDensity); - - // If we are dealing with smaller buttons where the dead zone defined by - // HIT_TARGET_EDGE_IGNORE_DP_[X|Y] is too large. - if (viewWidth < targetMinSizeX || viewHeight < targetMinSizeY) { - // This really should not happen given our two use cases (as of this writing) - // in the call edge button and secondary calling card. However, we leave - // this is as a precautionary measure. - Log.w(TAG, "onTouch: view is too small for SmallerHitTargetTouchListener"); - edgeIgnoreX = 0; - edgeIgnoreY = 0; - } - - final int minTouchX = edgeIgnoreX; - final int maxTouchX = viewWidth - edgeIgnoreX; - final int minTouchY = edgeIgnoreY; - final int maxTouchY = viewHeight - edgeIgnoreY; - - if (touchX < minTouchX || touchX > maxTouchX || - touchY < minTouchY || touchY > maxTouchY) { - // Missed! - // if (DBG) log(" -> MISSED!"); - mDownEventHit = false; - return true; // Consume this event; don't let the button see it - } else { - // Hit! - // if (DBG) log(" -> HIT!"); - mDownEventHit = true; - return false; // Let this event through to the actual button - } - } else { - // This is a MOVE, UP or CANCEL event. - // - // We only do the "smaller hit target" check on DOWN events. - // For the subsequent MOVE/UP/CANCEL events, we let them - // through to the actual button IFF the previous DOWN event - // got through to the actual button (i.e. it was a "hit".) - return !mDownEventHit; - } - } -} diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java index 006339ce1..c5a0b7f4c 100644 --- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java +++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java @@ -641,8 +641,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { private PendingIntent createLaunchPendingIntent() { final Intent intent = InCallPresenter.getInstance().getInCallIntent( - false /* showDialpad */, false /* newOutgoingCall */, - false /* showCircularReveal */); + false /* showDialpad */, false /* newOutgoingCall */); // 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/InCallUI/src/com/android/incallui/TelecomAdapter.java b/InCallUI/src/com/android/incallui/TelecomAdapter.java index 373bd7874..25813f28f 100644 --- a/InCallUI/src/com/android/incallui/TelecomAdapter.java +++ b/InCallUI/src/com/android/incallui/TelecomAdapter.java @@ -20,7 +20,6 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.os.Looper; -import android.telecom.InCallAdapter; import android.telecom.Phone; import android.telecom.PhoneAccountHandle; @@ -28,7 +27,6 @@ import com.google.common.base.Preconditions; import java.util.List; -/** Wrapper around {@link InCallAdapter} that only forwards calls to the adapter when it's valid. */ final class TelecomAdapter implements InCallPhoneListener { private static final String ADD_CALL_MODE_KEY = "add_call_mode"; diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java index c4d10c569..233ff92a1 100644 --- a/InCallUI/src/com/android/incallui/VideoCallFragment.java +++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java @@ -41,6 +41,8 @@ import com.google.common.base.Objects; */ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, VideoCallPresenter.VideoCallUi> implements VideoCallPresenter.VideoCallUi { + private static final String TAG = VideoCallFragment.class.getSimpleName(); + private static final boolean DEBUG = false; /** * Used to indicate that the surface dimensions are not set. @@ -156,6 +158,14 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, * @param view The {@link TextureView}. */ public void recreateView(TextureView view) { + if (DEBUG) { + Log.i(TAG, "recreateView: " + view); + } + + if (mTextureView == view) { + return; + } + mTextureView = view; mTextureView.setSurfaceTextureListener(this); mTextureView.setOnClickListener(this); @@ -191,6 +201,9 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { boolean surfaceCreated; + if (DEBUG) { + Log.i(TAG, "onSurfaceTextureAvailable: " + surfaceTexture); + } // Where there is no saved {@link SurfaceTexture} available, use the newly created one. // If a saved {@link SurfaceTexture} is available, we are re-creating after an // orientation change. @@ -281,6 +294,19 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, // Not Handled } + @Override + public void onViewAttachedToWindow(View v) { + if (DEBUG) { + Log.i(TAG, "OnViewAttachedToWindow"); + } + if (mSavedSurfaceTexture != null) { + mTextureView.setSurfaceTexture(mSavedSurfaceTexture); + } + } + + @Override + public void onViewDetachedFromWindow(View v) {} + /** * Retrieves the current {@link TextureView}. * @@ -413,33 +439,10 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); final View view = inflater.inflate(R.layout.video_call_fragment, container, false); - // Attempt to center the incoming video view, if it is in the layout. - final ViewTreeObserver observer = view.getViewTreeObserver(); - observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - // Check if the layout includes the incoming video surface -- this will only be the - // case for a video call. - View displayVideo = view.findViewById(R.id.incomingVideo); - if (displayVideo != null) { - centerDisplayView(displayVideo); - } - - mIsLayoutComplete = true; - - // Remove the listener so we don't continually re-layout. - ViewTreeObserver observer = view.getViewTreeObserver(); - if (observer.isAlive()) { - observer.removeOnGlobalLayoutListener(this); - } - } - }); - return view; } @@ -912,6 +915,27 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, sPreviewSurface.recreateView((TextureView) mVideoViews.findViewById( R.id.previewVideo)); } + + // Attempt to center the incoming video view, if it is in the layout. + final ViewTreeObserver observer = mVideoViews.getViewTreeObserver(); + observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + // Check if the layout includes the incoming video surface -- this will only be the + // case for a video call. + View displayVideo = mVideoViews.findViewById(R.id.incomingVideo); + if (displayVideo != null) { + centerDisplayView(displayVideo); + } + mIsLayoutComplete = true; + + // Remove the listener so we don't continually re-layout. + ViewTreeObserver observer = mVideoViews.getViewTreeObserver(); + if (observer.isAlive()) { + observer.removeOnGlobalLayoutListener(this); + } + } + }); } } diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java index 486a38da3..e4a5db97a 100644 --- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java +++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java @@ -71,6 +71,10 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi InCallVideoCallListenerNotifier.SessionModificationListener { public static final String TAG = "VideoCallPresenter"; + private static final String TAG = VideoCallPresenter.class.getSimpleName(); + + public static final boolean DEBUG = false; + /** * Determines the device orientation (portrait/lanscape). */ diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java index fe008f3df..812e883c0 100644 --- a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java +++ b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java @@ -881,7 +881,7 @@ public class GlowPadView extends View { // tx and ty are relative to wave center float tx = eventX - mWaveCenterX; float ty = eventY - mWaveCenterY; - float touchRadius = (float) Math.sqrt(dist2(tx, ty)); + float touchRadius = (float) Math.hypot(tx, ty); final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f; float limitX = tx * scale; float limitY = ty * scale; diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java index 77aac2bda..07a2cb964 100644 --- a/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java +++ b/InCallUI/src/com/android/incallui/widget/multiwaveview/PointCloud.java @@ -20,7 +20,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.drawable.Drawable; -import android.util.FloatMath; import android.util.Log; import java.util.ArrayList; @@ -151,8 +150,8 @@ public class PointCloud { float eta = PI/2.0f; float dEta = 2.0f * PI / pointsInBand; for (int i = 0; i < pointsInBand; i++) { - float x = r * FloatMath.cos(eta); - float y = r * FloatMath.sin(eta); + float x = r * (float) Math.cos(eta); + float y = r * (float) Math.sin(eta); eta += dEta; mPointCloud.add(new Point(x, y, r)); } @@ -168,7 +167,7 @@ public class PointCloud { } private static float hypot(float x, float y) { - return FloatMath.sqrt(x*x + y*y); + return (float) Math.hypot(x, y); } private static float max(float a, float b) { @@ -181,8 +180,8 @@ public class PointCloud { float glowAlpha = 0.0f; if (glowDistance < glowManager.radius) { - float cosf = FloatMath.cos(PI * 0.25f * glowDistance / glowManager.radius); - glowAlpha = glowManager.alpha * max(0.0f, (float) Math.pow(cosf, 10.0f)); + double cos = Math.cos(Math.PI * 0.25d * glowDistance / glowManager.radius); + glowAlpha = glowManager.alpha * max(0.0f, (float) Math.pow(cos, 10.0d)); } // Compute contribution from Wave @@ -190,8 +189,8 @@ public class PointCloud { float distanceToWaveRing = (radius - waveManager.radius); float waveAlpha = 0.0f; if (distanceToWaveRing < waveManager.width * 0.5f && distanceToWaveRing < 0.0f) { - float cosf = FloatMath.cos(PI * 0.25f * distanceToWaveRing / waveManager.width); - waveAlpha = waveManager.alpha * max(0.0f, (float) Math.pow(cosf, 20.0f)); + double cos = Math.cos(Math.PI * 0.25d * distanceToWaveRing / waveManager.width); + waveAlpha = waveManager.alpha * max(0.0f, (float) Math.pow(cos, 20.0d)); } return (int) (max(glowAlpha, waveAlpha) * 255); |