diff options
Diffstat (limited to 'InCallUI/src/com/android/incallui/CallCardFragment.java')
-rw-r--r-- | InCallUI/src/com/android/incallui/CallCardFragment.java | 1510 |
1 files changed, 0 insertions, 1510 deletions
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java deleted file mode 100644 index c2022d18c..000000000 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ /dev/null @@ -1,1510 +0,0 @@ -/* - * Copyright (C) 2013 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.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.AnimationDrawable; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Trace; -import android.support.v4.graphics.drawable.RoundedBitmapDrawable; -import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; -import android.telecom.DisconnectCause; -import android.telephony.PhoneNumberUtils; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnLayoutChangeListener; -import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; -import android.view.ViewTreeObserver; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.contacts.common.compat.PhoneNumberUtilsCompat; -import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; -import com.android.contacts.common.widget.FloatingActionButtonController; -import com.android.dialer.R; -import com.android.phone.common.animation.AnimUtils; - -import java.util.List; - -/** - * Fragment for call card. - */ -public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPresenter.CallCardUi> - implements CallCardPresenter.CallCardUi { - private static final String TAG = "CallCardFragment"; - - /** - * Internal class which represents the call state label which is to be applied. - */ - private class CallStateLabel { - private CharSequence mCallStateLabel; - private boolean mIsAutoDismissing; - - public CallStateLabel(CharSequence callStateLabel, boolean isAutoDismissing) { - mCallStateLabel = callStateLabel; - mIsAutoDismissing = isAutoDismissing; - } - - public CharSequence getCallStateLabel() { - return mCallStateLabel; - } - - /** - * Determines if the call state label should auto-dismiss. - * - * @return {@code true} if the call state label should auto-dismiss. - */ - public boolean isAutoDismissing() { - return mIsAutoDismissing; - } - }; - - private static final String IS_DIALPAD_SHOWING_KEY = "is_dialpad_showing"; - - /** - * The duration of time (in milliseconds) a call state label should remain visible before - * resetting to its previous value. - */ - private static final long CALL_STATE_LABEL_RESET_DELAY_MS = 3000; - /** - * Amount of time to wait before sending an announcement via the accessibility manager. - * When the call state changes to an outgoing or incoming state for the first time, the - * UI can often be changing due to call updates or contact lookup. This allows the UI - * to settle to a stable state to ensure that the correct information is announced. - */ - private static final long ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS = 500; - - private AnimatorSet mAnimatorSet; - private int mShrinkAnimationDuration; - private int mFabNormalDiameter; - private int mFabSmallDiameter; - private boolean mIsLandscape; - private boolean mHasLargePhoto; - private boolean mIsDialpadShowing; - - // Primary caller info - private TextView mPhoneNumber; - private TextView mNumberLabel; - private TextView mPrimaryName; - private View mCallStateButton; - private ImageView mCallStateIcon; - private ImageView mCallStateVideoCallIcon; - private TextView mCallStateLabel; - private TextView mCallTypeLabel; - private ImageView mHdAudioIcon; - private ImageView mForwardIcon; - private ImageView mSpamIcon; - private View mCallNumberAndLabel; - private TextView mElapsedTime; - private Drawable mPrimaryPhotoDrawable; - private TextView mCallSubject; - private ImageView mWorkProfileIcon; - - // Container view that houses the entire primary call card, including the call buttons - private View mPrimaryCallCardContainer; - // Container view that houses the primary call information - private ViewGroup mPrimaryCallInfo; - private View mCallButtonsContainer; - private ImageView mPhotoSmall; - - // Secondary caller info - private View mSecondaryCallInfo; - private TextView mSecondaryCallName; - private View mSecondaryCallProviderInfo; - private TextView mSecondaryCallProviderLabel; - private View mSecondaryCallConferenceCallIcon; - private View mSecondaryCallVideoCallIcon; - private View mProgressSpinner; - - // Call card content - private View mCallCardContent; - private ImageView mPhotoLarge; - private View mContactContext; - private TextView mContactContextTitle; - private ListView mContactContextListView; - private LinearLayout mContactContextListHeaders; - - private View mManageConferenceCallButton; - - // Dark number info bar - private TextView mInCallMessageLabel; - - private FloatingActionButtonController mFloatingActionButtonController; - private View mFloatingActionButtonContainer; - private ImageButton mFloatingActionButton; - private int mFloatingActionButtonVerticalOffset; - - 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; - - /** - * Call state label to set when an auto-dismissing call state label is dismissed. - */ - private CharSequence mPostResetCallStateLabel; - private boolean mCallStateLabelResetPending = false; - private Handler mHandler; - - /** - * Determines if secondary call info is populated in the secondary call info UI. - */ - private boolean mHasSecondaryCallInfo = false; - - @Override - public CallCardPresenter.CallCardUi getUi() { - return this; - } - - @Override - public CallCardPresenter createPresenter() { - return new CallCardPresenter(); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mHandler = new Handler(Looper.getMainLooper()); - mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration); - mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration); - mFloatingActionButtonVerticalOffset = getResources().getDimensionPixelOffset( - R.dimen.floating_action_button_vertical_offset); - mFabNormalDiameter = getResources().getDimensionPixelOffset( - R.dimen.end_call_floating_action_button_diameter); - mFabSmallDiameter = getResources().getDimensionPixelOffset( - R.dimen.end_call_floating_action_button_small_diameter); - - if (savedInstanceState != null) { - mIsDialpadShowing = savedInstanceState.getBoolean(IS_DIALPAD_SHOWING_KEY, false); - } - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - final CallList calls = CallList.getInstance(); - final Call call = calls.getFirstCall(); - getPresenter().init(getActivity(), call); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - outState.putBoolean(IS_DIALPAD_SHOWING_KEY, mIsDialpadShowing); - super.onSaveInstanceState(outState); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - Trace.beginSection(TAG + " onCreate"); - mTranslationOffset = - getResources().getDimensionPixelSize(R.dimen.call_card_anim_translate_y_offset); - final View view = inflater.inflate(R.layout.call_card_fragment, container, false); - Trace.endSection(); - return view; - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - mPulseAnimation = - AnimationUtils.loadAnimation(view.getContext(), R.anim.call_status_pulse); - - mPhoneNumber = (TextView) view.findViewById(R.id.phoneNumber); - mPrimaryName = (TextView) view.findViewById(R.id.name); - mNumberLabel = (TextView) view.findViewById(R.id.label); - mSecondaryCallInfo = view.findViewById(R.id.secondary_call_info); - mSecondaryCallProviderInfo = view.findViewById(R.id.secondary_call_provider_info); - mCallCardContent = view.findViewById(R.id.call_card_content); - mPhotoLarge = (ImageView) view.findViewById(R.id.photoLarge); - mPhotoLarge.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getPresenter().onContactPhotoClick(); - } - }); - - mContactContext = view.findViewById(R.id.contact_context); - mContactContextTitle = (TextView) view.findViewById(R.id.contactContextTitle); - mContactContextListView = (ListView) view.findViewById(R.id.contactContextInfo); - // This layout stores all the list header layouts so they can be easily removed. - mContactContextListHeaders = new LinearLayout(getView().getContext()); - mContactContextListView.addHeaderView(mContactContextListHeaders); - - mCallStateIcon = (ImageView) view.findViewById(R.id.callStateIcon); - mCallStateVideoCallIcon = (ImageView) view.findViewById(R.id.videoCallIcon); - mWorkProfileIcon = (ImageView) view.findViewById(R.id.workProfileIcon); - mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel); - mHdAudioIcon = (ImageView) view.findViewById(R.id.hdAudioIcon); - mForwardIcon = (ImageView) view.findViewById(R.id.forwardIcon); - mSpamIcon = (ImageView) view.findViewById(R.id.spamIcon); - mCallNumberAndLabel = view.findViewById(R.id.labelAndNumber); - mCallTypeLabel = (TextView) view.findViewById(R.id.callTypeLabel); - mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime); - mPrimaryCallCardContainer = view.findViewById(R.id.primary_call_info_container); - mPrimaryCallInfo = (ViewGroup) view.findViewById(R.id.primary_call_banner); - mCallButtonsContainer = view.findViewById(R.id.callButtonFragment); - mPhotoSmall = (ImageView) view.findViewById(R.id.photoSmall); - mPhotoSmall.setVisibility(View.GONE); - mInCallMessageLabel = (TextView) view.findViewById(R.id.connectionServiceMessage); - mProgressSpinner = view.findViewById(R.id.progressSpinner); - - mFloatingActionButtonContainer = view.findViewById( - R.id.floating_end_call_action_button_container); - mFloatingActionButton = (ImageButton) view.findViewById( - R.id.floating_end_call_action_button); - mFloatingActionButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getPresenter().endCallClicked(); - } - }); - mFloatingActionButtonController = new FloatingActionButtonController(getActivity(), - mFloatingActionButtonContainer, mFloatingActionButton); - - mSecondaryCallInfo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - getPresenter().secondaryInfoClicked(); - updateFabPositionForSecondaryCallInfo(); - } - }); - - mCallStateButton = view.findViewById(R.id.callStateButton); - mCallStateButton.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - getPresenter().onCallStateButtonTouched(); - return false; - } - }); - - mManageConferenceCallButton = view.findViewById(R.id.manage_conference_call_button); - mManageConferenceCallButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - InCallActivity activity = (InCallActivity) getActivity(); - activity.showConferenceFragment(true); - } - }); - - mPrimaryName.setElegantTextHeight(false); - mCallStateLabel.setElegantTextHeight(false); - mCallSubject = (TextView) view.findViewById(R.id.callSubject); - } - - @Override - public void setVisible(boolean on) { - if (on) { - getView().setVisibility(View.VISIBLE); - } else { - getView().setVisibility(View.INVISIBLE); - } - } - - /** - * Hides or shows the progress spinner. - * - * @param visible {@code True} if the progress spinner should be visible. - */ - @Override - public void setProgressSpinnerVisible(boolean visible) { - mProgressSpinner.setVisibility(visible ? View.VISIBLE : View.GONE); - } - - @Override - public void setContactContextTitle(View headerView) { - mContactContextListHeaders.removeAllViews(); - mContactContextListHeaders.addView(headerView); - } - - @Override - public void setContactContextContent(ListAdapter listAdapter) { - mContactContextListView.setAdapter(listAdapter); - } - - @Override - public void showContactContext(boolean show) { - showImageView(mPhotoLarge, !show); - showImageView(mPhotoSmall, show); - mPrimaryCallCardContainer.setElevation( - show ? 0 : getResources().getDimension(R.dimen.primary_call_elevation)); - mContactContext.setVisibility(show ? View.VISIBLE : View.GONE); - } - - /** - * Sets the visibility of the primary call card. - * Ensures that when the primary call card is hidden, the video surface slides over to fill the - * entire screen. - * - * @param visible {@code True} if the primary call card should be visible. - */ - @Override - public void setCallCardVisible(final boolean visible) { - Log.v(this, "setCallCardVisible : isVisible = " + visible); - // When animating the hide/show of the views in a landscape layout, we need to take into - // account whether we are in a left-to-right locale or a right-to-left locale and adjust - // the animations accordingly. - final boolean isLayoutRtl = InCallPresenter.isRtl(); - - // Retrieve here since at fragment creation time the incoming video view is not inflated. - final View videoView = getView().findViewById(R.id.incomingVideo); - if (videoView == null) { - return; - } - - // Determine how much space there is below or to the side of the call card. - final float spaceBesideCallCard = getSpaceBesideCallCard(); - - // We need to translate the video surface, but we need to know its position after the layout - // has occurred so use a {@code ViewTreeObserver}. - final ViewTreeObserver observer = getView().getViewTreeObserver(); - observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - // We don't want to continue getting called. - getView().getViewTreeObserver().removeOnPreDrawListener(this); - - float videoViewTranslation = 0f; - - // Translate the call card to its pre-animation state. - if (!mIsLandscape) { - mPrimaryCallCardContainer.setTranslationY(visible ? - -mPrimaryCallCardContainer.getHeight() : 0); - - ViewGroup.LayoutParams p = videoView.getLayoutParams(); - videoViewTranslation = p.height / 2 - spaceBesideCallCard / 2; - } - - // Perform animation of video view. - ViewPropertyAnimator videoViewAnimator = videoView.animate() - .setInterpolator(AnimUtils.EASE_OUT_EASE_IN) - .setDuration(mVideoAnimationDuration); - if (mIsLandscape) { - videoViewAnimator - .translationX(visible ? videoViewTranslation : 0); - } else { - videoViewAnimator - .translationY(visible ? videoViewTranslation : 0); - } - videoViewAnimator.start(); - - // Animate the call card sliding. - ViewPropertyAnimator callCardAnimator = mPrimaryCallCardContainer.animate() - .setInterpolator(AnimUtils.EASE_OUT_EASE_IN) - .setDuration(mVideoAnimationDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - super.onAnimationEnd(animation); - if (!visible) { - mPrimaryCallCardContainer.setVisibility(View.GONE); - } - } - - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - if (visible) { - mPrimaryCallCardContainer.setVisibility(View.VISIBLE); - } - } - }); - - if (mIsLandscape) { - float translationX = mPrimaryCallCardContainer.getWidth(); - translationX *= isLayoutRtl ? 1 : -1; - callCardAnimator - .translationX(visible ? 0 : translationX) - .start(); - } else { - callCardAnimator - .translationY(visible ? 0 : -mPrimaryCallCardContainer.getHeight()) - .start(); - } - - return true; - } - }); - } - - /** - * Determines the amount of space below the call card for portrait layouts), or beside the - * call card for landscape layouts. - * - * @return The amount of space below or beside the call card. - */ - public float getSpaceBesideCallCard() { - if (mIsLandscape) { - return getView().getWidth() - mPrimaryCallCardContainer.getWidth(); - } else { - 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; - } - } - - @Override - public void setPrimaryName(String name, boolean nameIsNumber) { - if (TextUtils.isEmpty(name)) { - mPrimaryName.setText(null); - } else { - mPrimaryName.setText(nameIsNumber - ? PhoneNumberUtilsCompat.createTtsSpannable(name) - : name); - - // Set direction of the name field - int nameDirection = View.TEXT_DIRECTION_INHERIT; - if (nameIsNumber) { - nameDirection = View.TEXT_DIRECTION_LTR; - } - mPrimaryName.setTextDirection(nameDirection); - } - } - - /** - * Sets the primary image for the contact photo. - * - * @param image The drawable to set. - * @param isVisible Whether the contact photo should be visible after being set. - */ - @Override - public void setPrimaryImage(Drawable image, boolean isVisible) { - if (image != null) { - setDrawableToImageViews(image); - showImageView(mPhotoLarge, isVisible); - } - } - - @Override - public void setPrimaryPhoneNumber(String number) { - // Set the number - if (TextUtils.isEmpty(number)) { - mPhoneNumber.setText(null); - mPhoneNumber.setVisibility(View.GONE); - } else { - mPhoneNumber.setText(PhoneNumberUtilsCompat.createTtsSpannable(number)); - mPhoneNumber.setVisibility(View.VISIBLE); - mPhoneNumber.setTextDirection(View.TEXT_DIRECTION_LTR); - } - } - - @Override - public void setPrimaryLabel(String label) { - if (!TextUtils.isEmpty(label)) { - mNumberLabel.setText(label); - mNumberLabel.setVisibility(View.VISIBLE); - } else { - mNumberLabel.setVisibility(View.GONE); - } - - } - - /** - * Sets the primary caller information. - * - * @param number The caller phone number. - * @param name The caller name. - * @param nameIsNumber {@code true} if the name should be shown in place of the phone number. - * @param label The label. - * @param photo The contact photo drawable. - * @param isSipCall {@code true} if this is a SIP call. - * @param isContactPhotoShown {@code true} if the contact photo should be shown (it will be - * updated even if it is not shown). - * @param isWorkCall Whether the call is placed through a work phone account or caller is a work - contact. - */ - @Override - public void setPrimary(String number, String name, boolean nameIsNumber, String label, - Drawable photo, boolean isSipCall, boolean isContactPhotoShown, boolean isWorkCall) { - Log.d(this, "Setting primary call"); - // set the name field. - setPrimaryName(name, nameIsNumber); - - if (TextUtils.isEmpty(number) && TextUtils.isEmpty(label)) { - mCallNumberAndLabel.setVisibility(View.GONE); - mElapsedTime.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); - } else { - mCallNumberAndLabel.setVisibility(View.VISIBLE); - mElapsedTime.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_END); - } - - setPrimaryPhoneNumber(number); - - // Set the label (Mobile, Work, etc) - setPrimaryLabel(label); - - showInternetCallLabel(isSipCall); - - setDrawableToImageViews(photo); - showImageView(mPhotoLarge, isContactPhotoShown); - showImageView(mWorkProfileIcon, isWorkCall); - } - - @Override - public void setSecondary(boolean show, String name, boolean nameIsNumber, String label, - String providerLabel, boolean isConference, boolean isVideoCall, boolean isFullscreen) { - - if (show) { - mHasSecondaryCallInfo = true; - boolean hasProvider = !TextUtils.isEmpty(providerLabel); - initializeSecondaryCallInfo(hasProvider); - - // Do not show the secondary caller info in fullscreen mode, but ensure it is populated - // in case fullscreen mode is exited in the future. - setSecondaryInfoVisible(!isFullscreen); - - mSecondaryCallConferenceCallIcon.setVisibility(isConference ? View.VISIBLE : View.GONE); - mSecondaryCallVideoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE); - - mSecondaryCallName.setText(nameIsNumber - ? PhoneNumberUtilsCompat.createTtsSpannable(name) - : name); - if (hasProvider) { - mSecondaryCallProviderLabel.setText(providerLabel); - } - - int nameDirection = View.TEXT_DIRECTION_INHERIT; - if (nameIsNumber) { - nameDirection = View.TEXT_DIRECTION_LTR; - } - mSecondaryCallName.setTextDirection(nameDirection); - } else { - mHasSecondaryCallInfo = false; - setSecondaryInfoVisible(false); - } - } - - /** - * Sets the visibility of the secondary caller info box. Note, if the {@code visible} parameter - * is passed in {@code true}, and there is no secondary caller info populated (as determined by - * {@code mHasSecondaryCallInfo}, the secondary caller info box will not be shown. - * - * @param visible {@code true} if the secondary caller info should be shown, {@code false} - * otherwise. - */ - @Override - public void setSecondaryInfoVisible(final boolean visible) { - boolean wasVisible = mSecondaryCallInfo.isShown(); - final boolean isVisible = visible && mHasSecondaryCallInfo; - Log.v(this, "setSecondaryInfoVisible: wasVisible = " + wasVisible + " isVisible = " - + isVisible); - - // If visibility didn't change, nothing to do. - if (wasVisible == isVisible) { - return; - } - - // If we are showing the secondary info, we need to show it before animating so that its - // height will be determined on layout. - if (isVisible) { - mSecondaryCallInfo.setVisibility(View.VISIBLE); - } else { - mSecondaryCallInfo.setVisibility(View.GONE); - } - - updateFabPositionForSecondaryCallInfo(); - // We need to translate the secondary caller info, but we need to know its position after - // the layout has occurred so use a {@code ViewTreeObserver}. - final ViewTreeObserver observer = getView().getViewTreeObserver(); - - observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - // We don't want to continue getting called. - getView().getViewTreeObserver().removeOnPreDrawListener(this); - - // Get the height of the secondary call info now, and then re-hide the view prior - // to doing the actual animation. - int secondaryHeight = mSecondaryCallInfo.getHeight(); - if (isVisible) { - mSecondaryCallInfo.setVisibility(View.GONE); - } else { - mSecondaryCallInfo.setVisibility(View.VISIBLE); - } - Log.v(this, "setSecondaryInfoVisible: secondaryHeight = " + secondaryHeight); - - // Set the position of the secondary call info card to its starting location. - mSecondaryCallInfo.setTranslationY(visible ? secondaryHeight : 0); - - // Animate the secondary card info slide up/down as it appears and disappears. - ViewPropertyAnimator secondaryInfoAnimator = mSecondaryCallInfo.animate() - .setInterpolator(AnimUtils.EASE_OUT_EASE_IN) - .setDuration(mVideoAnimationDuration) - .translationY(isVisible ? 0 : secondaryHeight) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (!isVisible) { - mSecondaryCallInfo.setVisibility(View.GONE); - } - } - - @Override - public void onAnimationStart(Animator animation) { - if (isVisible) { - mSecondaryCallInfo.setVisibility(View.VISIBLE); - } - } - }); - secondaryInfoAnimator.start(); - - // Notify listeners of a change in the visibility of the secondary info. This is - // important when in a video call so that the video call presenter can shift the - // video preview up or down to accommodate the secondary caller info. - InCallPresenter.getInstance().notifySecondaryCallerInfoVisibilityChanged(visible, - secondaryHeight); - - return true; - } - }); - } - - @Override - public void setCallState( - int state, - int videoState, - int sessionModificationState, - DisconnectCause disconnectCause, - String connectionLabel, - Drawable callStateIcon, - String gatewayNumber, - boolean isWifi, - boolean isConference, - boolean isWorkCall) { - boolean isGatewayCall = !TextUtils.isEmpty(gatewayNumber); - CallStateLabel callStateLabel = getCallStateLabelFromState(state, videoState, - sessionModificationState, disconnectCause, connectionLabel, isGatewayCall, isWifi, - isConference, isWorkCall); - - Log.v(this, "setCallState " + callStateLabel.getCallStateLabel()); - Log.v(this, "AutoDismiss " + callStateLabel.isAutoDismissing()); - Log.v(this, "DisconnectCause " + disconnectCause.toString()); - Log.v(this, "gateway " + connectionLabel + gatewayNumber); - - // Check for video state change and update the visibility of the contact photo. The contact - // photo is hidden when the incoming video surface is shown. - // The contact photo visibility can also change in setPrimary(). - boolean showContactPhoto = !VideoCallPresenter.showIncomingVideo(videoState, state); - mPhotoLarge.setVisibility(showContactPhoto ? View.VISIBLE : View.GONE); - - // Check if the call subject is showing -- if it is, we want to bypass showing the call - // state. - boolean isSubjectShowing = mCallSubject.getVisibility() == View.VISIBLE; - - if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText()) && - !isSubjectShowing) { - // Nothing to do if the labels are the same - if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) { - mCallStateLabel.clearAnimation(); - mCallStateIcon.clearAnimation(); - } - return; - } - - if (isSubjectShowing) { - changeCallStateLabel(null); - callStateIcon = null; - } else { - // Update the call state label and icon. - setCallStateLabel(callStateLabel); - } - - if (!TextUtils.isEmpty(callStateLabel.getCallStateLabel())) { - if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) { - mCallStateLabel.clearAnimation(); - } else { - mCallStateLabel.startAnimation(mPulseAnimation); - } - } else { - mCallStateLabel.clearAnimation(); - } - - if (callStateIcon != null) { - mCallStateIcon.setVisibility(View.VISIBLE); - // Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is - // needed because the pulse animation operates on the view alpha. - mCallStateIcon.setAlpha(1.0f); - mCallStateIcon.setImageDrawable(callStateIcon); - - if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED - || TextUtils.isEmpty(callStateLabel.getCallStateLabel())) { - mCallStateIcon.clearAnimation(); - } else { - mCallStateIcon.startAnimation(mPulseAnimation); - } - - if (callStateIcon instanceof AnimationDrawable) { - ((AnimationDrawable) callStateIcon).start(); - } - } else { - mCallStateIcon.clearAnimation(); - - // Invoke setAlpha(float) instead of setAlpha(int) to set the view's alpha. This is - // needed because the pulse animation operates on the view alpha. - mCallStateIcon.setAlpha(0.0f); - mCallStateIcon.setVisibility(View.GONE); - } - - if (VideoUtils.isVideoCall(videoState) - || (state == Call.State.ACTIVE && sessionModificationState - == Call.SessionModificationState.WAITING_FOR_RESPONSE)) { - mCallStateVideoCallIcon.setVisibility(View.VISIBLE); - } else { - mCallStateVideoCallIcon.setVisibility(View.GONE); - } - } - - private void setCallStateLabel(CallStateLabel callStateLabel) { - Log.v(this, "setCallStateLabel : label = " + callStateLabel.getCallStateLabel()); - - if (callStateLabel.isAutoDismissing()) { - mCallStateLabelResetPending = true; - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - Log.v(this, "restoringCallStateLabel : label = " + - mPostResetCallStateLabel); - changeCallStateLabel(mPostResetCallStateLabel); - mCallStateLabelResetPending = false; - } - }, CALL_STATE_LABEL_RESET_DELAY_MS); - - changeCallStateLabel(callStateLabel.getCallStateLabel()); - } else { - // Keep track of the current call state label; used when resetting auto dismissing - // call state labels. - mPostResetCallStateLabel = callStateLabel.getCallStateLabel(); - - if (!mCallStateLabelResetPending) { - changeCallStateLabel(callStateLabel.getCallStateLabel()); - } - } - } - - private void changeCallStateLabel(CharSequence callStateLabel) { - Log.v(this, "changeCallStateLabel : label = " + callStateLabel); - if (!TextUtils.isEmpty(callStateLabel)) { - mCallStateLabel.setText(callStateLabel); - mCallStateLabel.setAlpha(1); - mCallStateLabel.setVisibility(View.VISIBLE); - } else { - Animation callStateLabelAnimation = mCallStateLabel.getAnimation(); - if (callStateLabelAnimation != null) { - callStateLabelAnimation.cancel(); - } - mCallStateLabel.setText(null); - mCallStateLabel.setAlpha(0); - mCallStateLabel.setVisibility(View.GONE); - } - } - - @Override - public void setCallbackNumber(String callbackNumber, boolean isEmergencyCall) { - if (mInCallMessageLabel == null) { - return; - } - - if (TextUtils.isEmpty(callbackNumber)) { - mInCallMessageLabel.setVisibility(View.GONE); - return; - } - - // TODO: The new Locale-specific methods don't seem to be working. Revisit this. - callbackNumber = PhoneNumberUtils.formatNumber(callbackNumber); - - int stringResourceId = isEmergencyCall ? R.string.card_title_callback_number_emergency - : R.string.card_title_callback_number; - - String text = getString(stringResourceId, callbackNumber); - mInCallMessageLabel.setText(text); - - mInCallMessageLabel.setVisibility(View.VISIBLE); - } - - /** - * Sets and shows the call subject if it is not empty. Hides the call subject otherwise. - * - * @param callSubject The call subject. - */ - @Override - public void setCallSubject(String callSubject) { - boolean showSubject = !TextUtils.isEmpty(callSubject); - - mCallSubject.setVisibility(showSubject ? View.VISIBLE : View.GONE); - if (showSubject) { - mCallSubject.setText(callSubject); - } else { - mCallSubject.setText(null); - } - } - - public boolean isAnimating() { - return mIsAnimating; - } - - private void showInternetCallLabel(boolean show) { - if (show) { - final String label = getView().getContext().getString( - R.string.incall_call_type_label_sip); - mCallTypeLabel.setVisibility(View.VISIBLE); - mCallTypeLabel.setText(label); - } else { - mCallTypeLabel.setVisibility(View.GONE); - } - } - - @Override - public void setPrimaryCallElapsedTime(boolean show, long duration) { - if (show) { - if (mElapsedTime.getVisibility() != View.VISIBLE) { - AnimUtils.fadeIn(mElapsedTime, AnimUtils.DEFAULT_DURATION); - } - String callTimeElapsed = DateUtils.formatElapsedTime(duration / 1000); - mElapsedTime.setText(callTimeElapsed); - - String durationDescription = - InCallDateUtils.formatDuration(duration); - mElapsedTime.setContentDescription( - !TextUtils.isEmpty(durationDescription) ? durationDescription : null); - } else { - // hide() animation has no effect if it is already hidden. - AnimUtils.fadeOut(mElapsedTime, AnimUtils.DEFAULT_DURATION); - } - } - - /** - * Set all the ImageViews to the same photo. Currently there are 2 photo views: the large one - * (which fills about the bottom half of the screen) and the small one, which displays as a - * circle next to the primary contact info. This method does not handle whether the ImageView - * is shown or not. - * - * @param photo The photo to set for the image views. - */ - private void setDrawableToImageViews(Drawable photo) { - if (photo == null) { - photo = ContactInfoCache.getInstance(getView().getContext()) - .getDefaultContactPhotoDrawable(); - } - - if (mPrimaryPhotoDrawable == photo){ - return; - } - mPrimaryPhotoDrawable = photo; - - mPhotoLarge.setImageDrawable(photo); - - // Modify the drawable to be round for the smaller ImageView. - Bitmap bitmap = drawableToBitmap(photo); - if (bitmap != null) { - final RoundedBitmapDrawable drawable = - RoundedBitmapDrawableFactory.create(getResources(), bitmap); - drawable.setAntiAlias(true); - drawable.setCornerRadius(bitmap.getHeight() / 2); - photo = drawable; - } - mPhotoSmall.setImageDrawable(photo); - } - - /** - * Helper method for image view to handle animations. - * - * @param view The image view to show or hide. - * @param isVisible {@code true} if we want to show the image, {@code false} to hide it. - */ - private void showImageView(ImageView view, boolean isVisible) { - if (view.getDrawable() == null) { - if (isVisible) { - AnimUtils.fadeIn(mElapsedTime, AnimUtils.DEFAULT_DURATION); - } - } else { - // Cross fading is buggy and not noticeable due to the multiple calls to this method - // that switch drawables in the middle of the cross-fade animations. Just show the - // photo directly instead. - view.setVisibility(isVisible ? View.VISIBLE : View.GONE); - } - } - - /** - * Converts a drawable into a bitmap. - * - * @param drawable the drawable to be converted. - */ - public static Bitmap drawableToBitmap(Drawable drawable) { - Bitmap bitmap; - if (drawable instanceof BitmapDrawable) { - bitmap = ((BitmapDrawable) drawable).getBitmap(); - } else { - if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { - // Needed for drawables that are just a colour. - bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); - } else { - bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - } - - Log.i(TAG, "Created bitmap with width " + bitmap.getWidth() + ", height " - + bitmap.getHeight()); - - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - } - return bitmap; - } - - /** - * Gets the call state label based on the state of the call or cause of disconnect. - * - * Additional labels are applied as follows: - * 1. All outgoing calls with display "Calling via [Provider]". - * 2. Ongoing calls will display the name of the provider. - * 3. Incoming calls will only display "Incoming via..." for accounts. - * 4. Video calls, and session modification states (eg. requesting video). - * 5. Incoming and active Wi-Fi calls will show label provided by hint. - * - * TODO: Move this to the CallCardPresenter. - */ - private CallStateLabel getCallStateLabelFromState(int state, int videoState, - int sessionModificationState, DisconnectCause disconnectCause, String label, - boolean isGatewayCall, boolean isWifi, boolean isConference, boolean isWorkCall) { - final Context context = getView().getContext(); - CharSequence callStateLabel = null; // Label to display as part of the call banner - - boolean hasSuggestedLabel = label != null; - boolean isAccount = hasSuggestedLabel && !isGatewayCall; - boolean isAutoDismissing = false; - - switch (state) { - case Call.State.IDLE: - // "Call state" is meaningless in this state. - break; - case Call.State.ACTIVE: - // We normally don't show a "call state label" at all in this state - // (but we can use the call state label to display the provider name). - if ((isAccount || isWifi || isConference) && hasSuggestedLabel) { - callStateLabel = label; - } else if (sessionModificationState - == Call.SessionModificationState.REQUEST_REJECTED) { - callStateLabel = context.getString(R.string.card_title_video_call_rejected); - isAutoDismissing = true; - } else if (sessionModificationState - == Call.SessionModificationState.REQUEST_FAILED) { - callStateLabel = context.getString(R.string.card_title_video_call_error); - isAutoDismissing = true; - } else if (sessionModificationState - == Call.SessionModificationState.WAITING_FOR_RESPONSE) { - callStateLabel = context.getString(R.string.card_title_video_call_requesting); - } else if (sessionModificationState - == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { - callStateLabel = context.getString(R.string.card_title_video_call_requesting); - } else if (VideoUtils.isVideoCall(videoState)) { - callStateLabel = context.getString(R.string.card_title_video_call); - } - break; - case Call.State.ONHOLD: - callStateLabel = context.getString(R.string.card_title_on_hold); - break; - case Call.State.CONNECTING: - case Call.State.DIALING: - if (hasSuggestedLabel && !isWifi) { - callStateLabel = context.getString(R.string.calling_via_template, label); - } else { - callStateLabel = context.getString(R.string.card_title_dialing); - } - break; - case Call.State.REDIALING: - callStateLabel = context.getString(R.string.card_title_redialing); - break; - case Call.State.INCOMING: - case Call.State.CALL_WAITING: - if (isWifi && hasSuggestedLabel) { - callStateLabel = label; - } else if (isAccount) { - callStateLabel = context.getString(R.string.incoming_via_template, label); - } else if (VideoUtils.isVideoCall(videoState)) { - callStateLabel = context.getString(R.string.notification_incoming_video_call); - } else { - callStateLabel = - context.getString(isWorkCall ? R.string.card_title_incoming_work_call - : R.string.card_title_incoming_call); - } - break; - case Call.State.DISCONNECTING: - // While in the DISCONNECTING state we display a "Hanging up" - // message in order to make the UI feel more responsive. (In - // GSM it's normal to see a delay of a couple of seconds while - // negotiating the disconnect with the network, so the "Hanging - // up" state at least lets the user know that we're doing - // something. This state is currently not used with CDMA.) - callStateLabel = context.getString(R.string.card_title_hanging_up); - break; - case Call.State.DISCONNECTED: - callStateLabel = disconnectCause.getLabel(); - if (TextUtils.isEmpty(callStateLabel)) { - callStateLabel = context.getString(R.string.card_title_call_ended); - } - break; - case Call.State.CONFERENCED: - callStateLabel = context.getString(R.string.card_title_conf_call); - break; - default: - Log.wtf(this, "updateCallStateWidgets: unexpected call: " + state); - } - return new CallStateLabel(callStateLabel, isAutoDismissing); - } - - private void initializeSecondaryCallInfo(boolean hasProvider) { - // mSecondaryCallName is initialized here (vs. onViewCreated) because it is inaccessible - // until mSecondaryCallInfo is inflated in the call above. - if (mSecondaryCallName == null) { - mSecondaryCallName = (TextView) getView().findViewById(R.id.secondaryCallName); - mSecondaryCallConferenceCallIcon = - getView().findViewById(R.id.secondaryCallConferenceCallIcon); - mSecondaryCallVideoCallIcon = - getView().findViewById(R.id.secondaryCallVideoCallIcon); - } - - if (mSecondaryCallProviderLabel == null && hasProvider) { - mSecondaryCallProviderInfo.setVisibility(View.VISIBLE); - mSecondaryCallProviderLabel = (TextView) getView() - .findViewById(R.id.secondaryCallProviderLabel); - } - } - - public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { - if (event.getEventType() == AccessibilityEvent.TYPE_ANNOUNCEMENT) { - // Indicate this call is in active if no label is provided. The label is empty when - // the call is in active, not in other status such as onhold or dialing etc. - if (!mCallStateLabel.isShown() || TextUtils.isEmpty(mCallStateLabel.getText())) { - event.getText().add( - TextUtils.expandTemplate( - getResources().getText(R.string.accessibility_call_is_active), - mPrimaryName.getText())); - } else { - dispatchPopulateAccessibilityEvent(event, mCallStateLabel); - dispatchPopulateAccessibilityEvent(event, mPrimaryName); - dispatchPopulateAccessibilityEvent(event, mCallTypeLabel); - dispatchPopulateAccessibilityEvent(event, mPhoneNumber); - } - return; - } - dispatchPopulateAccessibilityEvent(event, mCallStateLabel); - dispatchPopulateAccessibilityEvent(event, mPrimaryName); - dispatchPopulateAccessibilityEvent(event, mPhoneNumber); - dispatchPopulateAccessibilityEvent(event, mCallTypeLabel); - dispatchPopulateAccessibilityEvent(event, mSecondaryCallName); - dispatchPopulateAccessibilityEvent(event, mSecondaryCallProviderLabel); - - return; - } - - @Override - public void sendAccessibilityAnnouncement() { - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (getView() != null && getView().getParent() != null && - isAccessibilityEnabled(getContext())) { - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_ANNOUNCEMENT); - dispatchPopulateAccessibilityEvent(event); - getView().getParent().requestSendAccessibilityEvent(getView(), event); - } - } - - private boolean isAccessibilityEnabled(Context context) { - AccessibilityManager accessibilityManager = - (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); - return accessibilityManager != null && accessibilityManager.isEnabled(); - - } - }, ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS); - } - - @Override - public void setEndCallButtonEnabled(boolean enabled, boolean animate) { - if (enabled != mFloatingActionButton.isEnabled()) { - if (animate) { - if (enabled) { - mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY); - } else { - mFloatingActionButtonController.scaleOut(); - } - } else { - if (enabled) { - mFloatingActionButtonContainer.setScaleX(1); - mFloatingActionButtonContainer.setScaleY(1); - mFloatingActionButtonContainer.setVisibility(View.VISIBLE); - } else { - mFloatingActionButtonContainer.setVisibility(View.GONE); - } - } - mFloatingActionButton.setEnabled(enabled); - updateFabPosition(); - } - } - - /** - * Changes the visibility of the HD audio icon. - * - * @param visible {@code true} if the UI should show the HD audio icon. - */ - @Override - public void showHdAudioIndicator(boolean visible) { - mHdAudioIcon.setVisibility(visible ? View.VISIBLE : View.GONE); - } - - /** - * Changes the visibility of the forward icon. - * - * @param visible {@code true} if the UI should show the forward icon. - */ - @Override - public void showForwardIndicator(boolean visible) { - mForwardIcon.setVisibility(visible ? View.VISIBLE : View.GONE); - } - - /** - * Changes the visibility of the spam icon. - * - * @param visible {@code true} if the UI should show the spam icon. - */ - @Override - public void showSpamIndicator(boolean visible) { - if (visible) { - mSpamIcon.setVisibility(View.VISIBLE); - mNumberLabel.setText(R.string.label_spam_caller); - mPhoneNumber.setVisibility(View.GONE); - } - } - - /** - * Changes the visibility of the "manage conference call" button. - * - * @param visible Whether to set the button to be visible or not. - */ - @Override - public void showManageConferenceCallButton(boolean visible) { - mManageConferenceCallButton.setVisibility(visible ? View.VISIBLE : View.GONE); - } - - /** - * Determines the current visibility of the manage conference button. - * - * @return {@code true} if the button is visible. - */ - @Override - public boolean isManageConferenceVisible() { - return mManageConferenceCallButton.getVisibility() == View.VISIBLE; - } - - /** - * Determines the current visibility of the call subject. - * - * @return {@code true} if the subject is visible. - */ - @Override - public boolean isCallSubjectVisible() { - return mCallSubject.getVisibility() == View.VISIBLE; - } - - /** - * Get the overall InCallUI background colors and apply to call card. - */ - public void updateColors() { - MaterialPalette themeColors = InCallPresenter.getInstance().getThemeColors(); - - if (mCurrentThemeColors != null && mCurrentThemeColors.equals(themeColors)) { - return; - } - - if (getResources().getBoolean(R.bool.is_layout_landscape)) { - final GradientDrawable drawable = - (GradientDrawable) mPrimaryCallCardContainer.getBackground(); - drawable.setColor(themeColors.mPrimaryColor); - } else { - mPrimaryCallCardContainer.setBackgroundColor(themeColors.mPrimaryColor); - } - mCallButtonsContainer.setBackgroundColor(themeColors.mPrimaryColor); - mCallSubject.setTextColor(themeColors.mPrimaryColor); - mContactContext.setBackgroundColor(themeColors.mPrimaryColor); - //TODO: set color of message text in call context "recent messages" to be the theme color. - - mCurrentThemeColors = themeColors; - } - - private void dispatchPopulateAccessibilityEvent(AccessibilityEvent event, View view) { - if (view == null) return; - final List<CharSequence> eventText = event.getText(); - int size = eventText.size(); - view.dispatchPopulateAccessibilityEvent(event); - // if no text added write null to keep relative position - if (size == eventText.size()) { - eventText.add(null); - } - } - - @Override - public void animateForNewOutgoingCall() { - final ViewGroup parent = (ViewGroup) mPrimaryCallCardContainer.getParent(); - - final ViewTreeObserver observer = getView().getViewTreeObserver(); - - mIsAnimating = true; - - observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - final ViewTreeObserver observer = getView().getViewTreeObserver(); - if (!observer.isAlive()) { - return; - } - observer.removeOnGlobalLayoutListener(this); - - final LayoutIgnoringListener listener = new LayoutIgnoringListener(); - mPrimaryCallCardContainer.addOnLayoutChangeListener(listener); - - // 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); - - 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(); - if (animator != null) { - animator.removeListener(this); - } - } - }); - animator.start(); - } - }); - } - - @Override - public void showNoteSentToast() { - Toast.makeText(getContext(), R.string.note_sent, Toast.LENGTH_LONG).show(); - } - - public void onDialpadVisibilityChange(boolean isShown) { - mIsDialpadShowing = isShown; - updateFabPosition(); - } - - private void updateFabPosition() { - int offsetY = 0; - if (!mIsDialpadShowing) { - offsetY = mFloatingActionButtonVerticalOffset; - if (mSecondaryCallInfo.isShown() && mHasLargePhoto) { - offsetY -= mSecondaryCallInfo.getHeight(); - } - } - - mFloatingActionButtonController.align( - FloatingActionButtonController.ALIGN_MIDDLE, - 0 /* offsetX */, - offsetY, - true); - - mFloatingActionButtonController.resize( - mIsDialpadShowing ? mFabSmallDiameter : mFabNormalDiameter, true); - } - - @Override - public Context getContext() { - return getActivity(); - } - - @Override - public void onResume() { - super.onResume(); - // If the previous launch animation is still running, cancel it so that we don't get - // stuck in an intermediate animation state. - if (mAnimatorSet != null && mAnimatorSet.isRunning()) { - mAnimatorSet.cancel(); - } - - mIsLandscape = getResources().getBoolean(R.bool.is_layout_landscape); - mHasLargePhoto = getResources().getBoolean(R.bool.has_large_photo); - - final ViewGroup parent = ((ViewGroup) mPrimaryCallCardContainer.getParent()); - final ViewTreeObserver observer = parent.getViewTreeObserver(); - parent.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - ViewTreeObserver viewTreeObserver = observer; - if (!viewTreeObserver.isAlive()) { - viewTreeObserver = parent.getViewTreeObserver(); - } - viewTreeObserver.removeOnGlobalLayoutListener(this); - mFloatingActionButtonController.setScreenWidth(parent.getWidth()); - updateFabPosition(); - } - }); - - updateColors(); - } - - /** - * Adds a global layout listener to update the FAB's positioning on the next layout. This allows - * us to position the FAB after the secondary call info's height has been calculated. - */ - private void updateFabPositionForSecondaryCallInfo() { - mSecondaryCallInfo.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - final ViewTreeObserver observer = mSecondaryCallInfo.getViewTreeObserver(); - if (!observer.isAlive()) { - return; - } - observer.removeOnGlobalLayoutListener(this); - - onDialpadVisibilityChange(mIsDialpadShowing); - } - }); - } - - /** - * Animator that performs the upwards shrinking animation of the blue call card scrim. - * At the start of the animation, each child view is moved downwards by a pre-specified amount - * and then translated upwards together with the scrim. - */ - private Animator getShrinkAnimator(int startHeight, int endHeight) { - final ObjectAnimator shrinkAnimator = - ObjectAnimator.ofInt(mPrimaryCallCardContainer, "bottom", startHeight, endHeight); - shrinkAnimator.setDuration(mShrinkAnimationDuration); - shrinkAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - mFloatingActionButton.setEnabled(true); - } - }); - shrinkAnimator.setInterpolator(AnimUtils.EASE_IN); - return shrinkAnimator; - } - - 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); - } - - private void setViewStatePostAnimation(View view) { - view.setTranslationY(0); - view.setAlpha(1); - } - - private void setViewStatePostAnimation(OnLayoutChangeListener layoutChangeListener) { - setViewStatePostAnimation(mCallButtonsContainer); - setViewStatePostAnimation(mCallStateLabel); - setViewStatePostAnimation(mPrimaryName); - setViewStatePostAnimation(mCallTypeLabel); - setViewStatePostAnimation(mCallNumberAndLabel); - setViewStatePostAnimation(mCallStateIcon); - - mPrimaryCallCardContainer.removeOnLayoutChangeListener(layoutChangeListener); - - mFloatingActionButtonController.scaleIn(AnimUtils.NO_DELAY); - } - - private final class LayoutIgnoringListener implements View.OnLayoutChangeListener { - @Override - public void onLayoutChange(View v, - int left, - int top, - int right, - int bottom, - int oldLeft, - int oldTop, - int oldRight, - int oldBottom) { - v.setLeft(oldLeft); - v.setRight(oldRight); - v.setTop(oldTop); - v.setBottom(oldBottom); - } - } -} |