summaryrefslogtreecommitdiff
path: root/InCallUI/src/com/android/incallui/CallCardFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'InCallUI/src/com/android/incallui/CallCardFragment.java')
-rw-r--r--InCallUI/src/com/android/incallui/CallCardFragment.java1510
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);
- }
- }
-}