1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/* 2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2016 The Android Open Source Project 3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License"); 5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License. 6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at 7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * http://www.apache.org/licenses/LICENSE-2.0 9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * 10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software 11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS, 12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and 14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License 15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */ 16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.incallui.answer.impl.answermethod; 18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.animation.Animator; 20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.animation.AnimatorListenerAdapter; 21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.animation.AnimatorSet; 22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.animation.ObjectAnimator; 23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.animation.PropertyValuesHolder; 24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.animation.ValueAnimator; 25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.annotation.SuppressLint; 26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Context; 27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.res.ColorStateList; 28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.PorterDuff.Mode; 29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.drawable.Drawable; 30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Bundle; 31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.ColorInt; 32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.FloatRange; 33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.IntDef; 34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.NonNull; 35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.Nullable; 36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.VisibleForTesting; 37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.graphics.ColorUtils; 38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.view.animation.FastOutLinearInInterpolator; 39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.view.animation.FastOutSlowInInterpolator; 40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.view.animation.LinearOutSlowInInterpolator; 41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.v4.view.animation.PathInterpolatorCompat; 42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.LayoutInflater; 43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.MotionEvent; 44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.View; 45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.View.AccessibilityDelegate; 46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.ViewGroup; 47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.accessibility.AccessibilityNodeInfo; 48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.animation.BounceInterpolator; 50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.animation.DecelerateInterpolator; 51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.view.animation.Interpolator; 52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.widget.ImageView; 53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.widget.TextView; 54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.DpUtil; 55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.LogUtil; 56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.MathUtil; 57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.util.DrawableConverter; 58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.util.ViewUtil; 59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.answer.impl.answermethod.FlingUpDownTouchHandler.OnProgressChangedListener; 60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.answer.impl.classifier.FalsingManager; 61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.answer.impl.hint.AnswerHint; 62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.answer.impl.hint.AnswerHintFactory; 63d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanianimport com.android.incallui.answer.impl.hint.PawImageLoaderImpl; 64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.lang.annotation.Retention; 65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.lang.annotation.RetentionPolicy; 66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/** Answer method that swipes up to answer or down to reject. */ 68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian@SuppressLint("ClickableViewAccessibility") 69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic class FlingUpDownMethod extends AnswerMethod implements OnProgressChangedListener { 70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final float SWIPE_LERP_PROGRESS_FACTOR = 0.5f; 72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long ANIMATE_DURATION_SHORT_MILLIS = 667; 73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long ANIMATE_DURATION_NORMAL_MILLIS = 1_333; 74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long ANIMATE_DURATION_LONG_MILLIS = 1_500; 75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long BOUNCE_ANIMATION_DELAY = 167; 76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long VIBRATION_TIME_MILLIS = 1_833; 77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long SETTLE_ANIMATION_DURATION_MILLIS = 100; 78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final int HINT_JUMP_DP = 60; 79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final int HINT_DIP_DP = 8; 80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final float HINT_SCALE_RATIO = 1.15f; 81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long SWIPE_TO_DECLINE_FADE_IN_DELAY_MILLIS = 333; 82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final int HINT_REJECT_SHOW_DURATION_MILLIS = 2000; 83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final int ICON_END_CALL_ROTATION_DEGREES = 135; 84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final int HINT_REJECT_FADE_TRANSLATION_Y_DP = -8; 85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final float SWIPE_TO_ANSWER_MAX_TRANSLATION_Y_DP = 150; 86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final int SWIPE_TO_REJECT_MAX_TRANSLATION_Y_DP = 24; 87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Retention(RetentionPolicy.SOURCE) 89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @IntDef( 90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian value = { 91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.NONE, 92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.ENTRY, 93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.BOUNCE, 94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.SWIPE, 95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.SETTLE, 96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.HINT, 97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimationState.COMPLETED 98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ) 100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @VisibleForTesting 101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @interface AnimationState { 102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int NONE = 0; 104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int ENTRY = 1; // Entry animation for incoming call 105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int BOUNCE = 2; // An idle state in which text and icon slightly bounces off its base repeatedly 106ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int SWIPE = 3; // A special state in which text and icon follows the finger movement 107ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int SETTLE = 4; // A short animation to reset from swipe and prepare for hint or bounce 108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int HINT = 5; // Jump animation to suggest what to do 109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int COMPLETED = 6; // Animation loop completed. Occurs after user swipes beyond threshold 110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static void moveTowardY(View view, float newY) { 113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian view.setTranslationY(MathUtil.lerp(view.getTranslationY(), newY, SWIPE_LERP_PROGRESS_FACTOR)); 114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static void moveTowardX(View view, float newX) { 117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian view.setTranslationX(MathUtil.lerp(view.getTranslationX(), newX, SWIPE_LERP_PROGRESS_FACTOR)); 118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 120ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static void fadeToward(View view, float newAlpha) { 121ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian view.setAlpha(MathUtil.lerp(view.getAlpha(), newAlpha, SWIPE_LERP_PROGRESS_FACTOR)); 122ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static void rotateToward(View view, float newRotation) { 125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian view.setRotation(MathUtil.lerp(view.getRotation(), newRotation, SWIPE_LERP_PROGRESS_FACTOR)); 126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private TextView swipeToAnswerText; 129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private TextView swipeToRejectText; 130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private View contactPuckContainer; 131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private ImageView contactPuckBackground; 132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private ImageView contactPuckIcon; 133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private View incomingDisconnectText; 1348369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian private View spaceHolder; 135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Animator lockBounceAnim; 136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private AnimatorSet lockEntryAnim; 137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private AnimatorSet lockHintAnim; 138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private AnimatorSet lockSettleAnim; 139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @AnimationState private int animationState = AnimationState.NONE; 140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @AnimationState private int afterSettleAnimationState = AnimationState.NONE; 141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // a value for finger swipe progress. -1 or less for "reject"; 1 or more for "accept". 142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private float swipeProgress; 143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Animator rejectHintHide; 144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Animator vibrationAnimator; 145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Drawable contactPhoto; 146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private boolean incomingWillDisconnect; 147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private FlingUpDownTouchHandler touchHandler; 148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private FalsingManager falsingManager; 149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 150ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private AnswerHint answerHint; 151ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 152ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 153ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onCreate(@Nullable Bundle bundle) { 154ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onCreate(bundle); 155ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian falsingManager = new FalsingManager(getContext()); 156ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 157ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 158ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 159ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onStart() { 160ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onStart(); 161ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian falsingManager.onScreenOn(); 162ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (getView() != null) { 163ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (animationState == AnimationState.SWIPE || animationState == AnimationState.HINT) { 164ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeProgress = 0; 165ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian updateContactPuck(); 166ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian onMoveReset(false); 167ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else if (animationState == AnimationState.ENTRY) { 168ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // When starting from the lock screen, the activity may be stopped and started briefly. 169ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Don't let that interrupt the entry animation 170ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian startSwipeToAnswerEntryAnimation(); 171ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 172ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 173ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 174ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 175ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 176ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onStop() { 177ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 178ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian falsingManager.onScreenOff(); 179ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (getActivity().isFinishing()) { 180ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.COMPLETED); 181ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 182ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onStop(); 183ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 184ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 185ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Nullable 186ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 187ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public View onCreateView( 188ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) { 189ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View view = layoutInflater.inflate(R.layout.swipe_up_down_method, viewGroup, false); 190ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 191ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer = view.findViewById(R.id.incoming_call_puck_container); 192ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground = (ImageView) view.findViewById(R.id.incoming_call_puck_bg); 193ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon = (ImageView) view.findViewById(R.id.incoming_call_puck_icon); 194ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText = (TextView) view.findViewById(R.id.incoming_swipe_to_answer_text); 195ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText = (TextView) view.findViewById(R.id.incoming_swipe_to_reject_text); 196ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian incomingDisconnectText = view.findViewById(R.id.incoming_will_disconnect_text); 1978369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian incomingDisconnectText.setVisibility(incomingWillDisconnect ? View.VISIBLE : View.GONE); 198ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian incomingDisconnectText.setAlpha(incomingWillDisconnect ? 1 : 0); 1998369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian spaceHolder = view.findViewById(R.id.incoming_bouncer_space_holder); 2008369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian spaceHolder.setVisibility(incomingWillDisconnect ? View.GONE : View.VISIBLE); 2018369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian 2028369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian view.findViewById(R.id.incoming_swipe_to_answer_container) 2038369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian .setAccessibilityDelegate( 2048369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian new AccessibilityDelegate() { 2058369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian @Override 2068369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 2078369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian super.onInitializeAccessibilityNodeInfo(host, info); 2088369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian info.addAction( 2098369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian new AccessibilityAction( 2108369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian R.id.accessibility_action_answer, 2118369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian getString(R.string.call_incoming_answer))); 2128369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian info.addAction( 2138369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian new AccessibilityAction( 2148369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian R.id.accessibility_action_decline, 2158369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian getString(R.string.call_incoming_decline))); 2168369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } 2178369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian 2188369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian @Override 2198369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian public boolean performAccessibilityAction(View host, int action, Bundle args) { 2208369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian if (action == R.id.accessibility_action_answer) { 2218369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian performAccept(); 2228369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian return true; 2238369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } else if (action == R.id.accessibility_action_decline) { 2248369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian performReject(); 2258369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian return true; 2268369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } 2278369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian return super.performAccessibilityAction(host, action, args); 2288369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } 2298369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian }); 230ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 231ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeProgress = 0; 232ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 233ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian updateContactPuck(); 234ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 235ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian touchHandler = FlingUpDownTouchHandler.attach(view, this, falsingManager); 236ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 237ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian answerHint = 238d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian new AnswerHintFactory(new PawImageLoaderImpl()) 239ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .create(getContext(), ANIMATE_DURATION_LONG_MILLIS, BOUNCE_ANIMATION_DELAY); 240ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian answerHint.onCreateView( 241ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian layoutInflater, 242ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian (ViewGroup) view.findViewById(R.id.hint_container), 243ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 244ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText); 245ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return view; 246ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 247ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 248ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 249ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onViewCreated(View view, @Nullable Bundle bundle) { 250ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onViewCreated(view, bundle); 251ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.ENTRY); 252ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 253ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 254ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 255ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onDestroyView() { 256ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onDestroyView(); 257ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (touchHandler != null) { 258ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian touchHandler.detach(); 259ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian touchHandler = null; 260ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 261ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 262ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 263ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 264ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onProgressChanged(@FloatRange(from = -1f, to = 1f) float progress) { 265ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeProgress = progress; 266ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (animationState == AnimationState.SWIPE && getContext() != null && isVisible()) { 267ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian updateSwipeTextAndPuckForTouch(); 268ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 269ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 270ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 271ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 272ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onTrackingStart() { 273ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.SWIPE); 274ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 275ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 276ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 277ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onTrackingStopped() {} 278ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 279ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 280ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onMoveReset(boolean showHint) { 281ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (showHint) { 282ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian showSwipeHint(); 283ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 284ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.BOUNCE); 285ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 286ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian resetTouchState(); 287ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getParent().resetAnswerProgress(); 288ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 289ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 290ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 291ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onMoveFinish(boolean accept) { 292ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian touchHandler.setTouchEnabled(false); 293ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian answerHint.onAnswered(); 294ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (accept) { 295ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian performAccept(); 296ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 297ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian performReject(); 298ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 299ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 300ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 301ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 302ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public boolean shouldUseFalsing(@NonNull MotionEvent downEvent) { 303ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (contactPuckContainer == null) { 304ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return false; 305ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 306ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 307ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float puckCenterX = contactPuckContainer.getX() + (contactPuckContainer.getWidth() / 2); 308ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float puckCenterY = contactPuckContainer.getY() + (contactPuckContainer.getHeight() / 2); 309ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian double radius = contactPuckContainer.getHeight() / 2; 310ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 311ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Squaring a number is more performant than taking a sqrt, so we compare the square of the 312ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // distance with the square of the radius. 313ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian double distSq = 314ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Math.pow(downEvent.getX() - puckCenterX, 2) + Math.pow(downEvent.getY() - puckCenterY, 2); 315ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return distSq >= Math.pow(radius, 2); 316ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 317ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 318ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 319ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void setContactPhoto(Drawable contactPhoto) { 320ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian this.contactPhoto = contactPhoto; 321ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 322ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian updateContactPuck(); 323ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 324ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 325ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void updateContactPuck() { 326ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (contactPuckIcon == null) { 327ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return; 328ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 329d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) { 330ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.setImageResource(R.drawable.quantum_ic_videocam_white_24); 331ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 332ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.setImageResource(R.drawable.quantum_ic_call_white_24); 333ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 334ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 335ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int size = 336ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground 337ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .getResources() 338ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .getDimensionPixelSize( 339ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian shouldShowPhotoInPuck() 340ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ? R.dimen.answer_contact_puck_size_photo 341ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian : R.dimen.answer_contact_puck_size_no_photo); 342ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setImageDrawable( 343ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian shouldShowPhotoInPuck() 344ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ? makeRoundedDrawable(contactPuckBackground.getContext(), contactPhoto, size) 345ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian : null); 346ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ViewGroup.LayoutParams contactPuckParams = contactPuckBackground.getLayoutParams(); 347ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckParams.height = size; 348ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckParams.width = size; 349ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setLayoutParams(contactPuckParams); 350ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.setAlpha(shouldShowPhotoInPuck() ? 0f : 1f); 351ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 352ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 353ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Drawable makeRoundedDrawable(Context context, Drawable contactPhoto, int size) { 354ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return DrawableConverter.getRoundedDrawable(context, contactPhoto, size, size); 355ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 356ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 357ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private boolean shouldShowPhotoInPuck() { 358d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian return (getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) 359d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian && contactPhoto != null; 360ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 361ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 362ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 363ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void setHintText(@Nullable CharSequence hintText) { 364ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (hintText == null) { 365ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.setText(R.string.call_incoming_swipe_to_answer); 366ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText.setText(R.string.call_incoming_swipe_to_reject); 367ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 368ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.setText(hintText); 369ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText.setText(null); 370ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 371ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 372ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 373ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 374ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void setShowIncomingWillDisconnect(boolean incomingWillDisconnect) { 375ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian this.incomingWillDisconnect = incomingWillDisconnect; 376ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (incomingDisconnectText != null) { 3778369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian if (incomingWillDisconnect) { 3788369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian incomingDisconnectText.setVisibility(View.VISIBLE); 3798369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian spaceHolder.setVisibility(View.GONE); 3808369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian incomingDisconnectText.animate().alpha(1); 3818369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } else { 3828369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian incomingDisconnectText 3838369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian .animate() 3848369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian .alpha(0) 3858369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian .setListener( 3868369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian new AnimatorListenerAdapter() { 3878369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian @Override 3888369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian public void onAnimationEnd(Animator animation) { 3898369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian super.onAnimationEnd(animation); 3908369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian incomingDisconnectText.setVisibility(View.GONE); 3918369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian spaceHolder.setVisibility(View.VISIBLE); 3928369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } 3938369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian }); 3948369df095a73a77b3715f8ae7ba06089cebca4ceEric Erfanian } 395ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 396ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 397ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 398ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void showSwipeHint() { 399ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.HINT); 400ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 401ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 402ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void updateSwipeTextAndPuckForTouch() { 403ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Clamp progress value between -1 and 1. 404ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final float clampedProgress = MathUtil.clamp(swipeProgress, -1 /* min */, 1 /* max */); 405ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final float positiveAdjustedProgress = Math.abs(clampedProgress); 406ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final boolean isAcceptingFlow = clampedProgress >= 0; 407ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 408ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Cancel view property animators on views we're about to mutate 409ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.animate().cancel(); 410ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.animate().cancel(); 411ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 412ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Since the animation progression is controlled by user gesture instead of real timeline, the 413ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // spec timeline can be divided into 9 slots. Each slot is equivalent to 83ms in the spec. 414ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Therefore, we use 9 slots of 83ms to map user gesture into the spec timeline. 4152df4538eb90b896be15eebc1d9adf1206131c8a3Sailesh Nepal // 4162df4538eb90b896be15eebc1d9adf1206131c8a3Sailesh Nepal // See specs - 4172df4538eb90b896be15eebc1d9adf1206131c8a3Sailesh Nepal // Accept: https://direct.googleplex.com/#/spec/8510001 4182df4538eb90b896be15eebc1d9adf1206131c8a3Sailesh Nepal // Decline: https://direct.googleplex.com/#/spec/3850001 419ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final float progressSlots = 9; 420ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 421ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Fade out the "swipe up to answer". It only takes 1 slot to complete the fade. 422ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float swipeTextAlpha = Math.max(0, 1 - Math.abs(clampedProgress) * progressSlots); 423ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian fadeToward(swipeToAnswerText, swipeTextAlpha); 424ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Fade out the "swipe down to dismiss" at the same time. Don't ever increase its alpha 425ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian fadeToward(swipeToRejectText, Math.min(swipeTextAlpha, swipeToRejectText.getAlpha())); 426ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Fade out the "incoming will disconnect" text 427ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian fadeToward(incomingDisconnectText, incomingWillDisconnect ? swipeTextAlpha : 0); 428ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 429ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Move swipe text back to zero. 430ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian moveTowardX(swipeToAnswerText, 0 /* newX */); 431ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian moveTowardY(swipeToAnswerText, 0 /* newY */); 432ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 433ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Animate puck color 434ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @ColorInt 435ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int destPuckColor = 436ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getContext() 437ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .getColor( 438ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian isAcceptingFlow ? R.color.call_accept_background : R.color.call_hangup_background); 439ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian destPuckColor = 440ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ColorUtils.setAlphaComponent(destPuckColor, (int) (0xFF * positiveAdjustedProgress)); 441ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setBackgroundTintList(ColorStateList.valueOf(destPuckColor)); 442ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setBackgroundTintMode(Mode.SRC_ATOP); 443ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setColorFilter(destPuckColor); 444ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 445ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Animate decline icon 446d5e47f6da5b08b13ecdfa7f1edc7e12aeb83fab9Eric Erfanian if (isAcceptingFlow || getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) { 447ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rotateToward(contactPuckIcon, 0f); 448ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 449ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rotateToward(contactPuckIcon, positiveAdjustedProgress * ICON_END_CALL_ROTATION_DEGREES); 450ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 451ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 452ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Fade in icon 453ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (shouldShowPhotoInPuck()) { 454ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian fadeToward(contactPuckIcon, positiveAdjustedProgress); 455ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 456ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float iconProgress = Math.min(1f, positiveAdjustedProgress * 4); 457ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @ColorInt 458ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int iconColor = 459ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ColorUtils.setAlphaComponent( 460ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.getContext().getColor(R.color.incoming_answer_icon), 461ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian (int) (0xFF * (1 - iconProgress))); 462ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.setImageTintList(ColorStateList.valueOf(iconColor)); 463ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 464ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Move puck. 465ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (isAcceptingFlow) { 466ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian moveTowardY( 467ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 468ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian -clampedProgress * DpUtil.dpToPx(getContext(), SWIPE_TO_ANSWER_MAX_TRANSLATION_Y_DP)); 469ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 470ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian moveTowardY( 471ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 472ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian -clampedProgress * DpUtil.dpToPx(getContext(), SWIPE_TO_REJECT_MAX_TRANSLATION_Y_DP)); 473ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 474ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 475ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getParent().onAnswerProgressUpdate(clampedProgress); 476ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 477ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 478ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void startSwipeToAnswerSwipeAnimation() { 479ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.startSwipeToAnswerSwipeAnimation", "Start swipe animation."); 480ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian resetTouchState(); 481ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 482ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 483ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 484ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void setPuckTouchState() { 485ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setActivated(touchHandler.isTracking()); 486ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 487ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 488ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void resetTouchState() { 489ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (getContext() == null) { 490ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // State will be reset in onStart(), so just abort. 491ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return; 492ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 493ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.animate().scaleX(1 /* scaleX */); 494ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.animate().scaleY(1 /* scaleY */); 495ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.animate().scaleX(1 /* scaleX */); 496ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.animate().scaleY(1 /* scaleY */); 497ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setBackgroundTintList(null); 498ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setColorFilter(null); 499ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.setImageTintList( 500ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ColorStateList.valueOf(getContext().getColor(R.color.incoming_answer_icon))); 501ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.animate().rotation(0); 502ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 503ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getParent().resetAnswerProgress(); 504ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setPuckTouchState(); 505ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 506ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final float alpha = 1; 507ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.animate().alpha(alpha); 508ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.animate().alpha(alpha); 509ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.animate().alpha(alpha); 510ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon.animate().alpha(shouldShowPhotoInPuck() ? 0 : alpha); 511ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 512ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 513ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @VisibleForTesting 514ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian void setAnimationState(@AnimationState int state) { 515ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (state != AnimationState.HINT && animationState == state) { 516ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return; 517ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 518ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 519ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (animationState == AnimationState.COMPLETED) { 520ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.e( 521ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian "FlingUpDownMethod.setAnimationState", 522ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian "Animation loop has completed. Cannot switch to new state: " + state); 523ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return; 524ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 525ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 526ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (state == AnimationState.HINT || state == AnimationState.BOUNCE) { 527ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (animationState == AnimationState.SWIPE) { 528ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian afterSettleAnimationState = state; 529ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian state = AnimationState.SETTLE; 530ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 531ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 532ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 533ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.setAnimationState", "animation state: " + state); 534ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian animationState = state; 535ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 536ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Start animation after the current one is finished completely. 537ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View view = getView(); 538ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (view != null) { 539ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // As long as the fragment is added, we can start update the animation state. 540ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (isAdded() && (animationState == state)) { 541ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian updateAnimationState(); 542ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else { 543ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 544ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 545ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 546ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 547ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 548ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @AnimationState 549ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @VisibleForTesting 550ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int getAnimationState() { 551ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return animationState; 552ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 553ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 554ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void updateAnimationState() { 555ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian switch (animationState) { 556ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.ENTRY: 557ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian startSwipeToAnswerEntryAnimation(); 558ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 559ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.BOUNCE: 560ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian startSwipeToAnswerBounceAnimation(); 561ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 562ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.SWIPE: 563ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian startSwipeToAnswerSwipeAnimation(); 564ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 565ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.SETTLE: 566ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian startSwipeToAnswerSettleAnimation(); 567ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 568ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.COMPLETED: 569ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian clearSwipeToAnswerUi(); 570ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 571ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.HINT: 572ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian startSwipeToAnswerHintAnimation(); 573ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 574ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian case AnimationState.NONE: 575ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian default: 576ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.e( 577ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian "FlingUpDownMethod.updateAnimationState", 578ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian "Unexpected animation state: " + animationState); 579ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian break; 580ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 581ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 582ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 583ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void startSwipeToAnswerEntryAnimation() { 584ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.startSwipeToAnswerEntryAnimation", "Swipe entry animation."); 585ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 586ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 587ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim = new AnimatorSet(); 588ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator textUp = 589ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 590ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText, 591ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View.TRANSLATION_Y, 592ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), 192 /* dp */), 593ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), -20 /* dp */)); 594ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 595ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setInterpolator(new LinearOutSlowInInterpolator()); 596ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 597ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator textDown = 598ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 599ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText, 600ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View.TRANSLATION_Y, 601ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), -20) /* dp */, 602ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0 /* end pos */); 603ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 604ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setInterpolator(new FastOutSlowInInterpolator()); 605ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 606ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // "Swipe down to reject" text fades in with a slight translation 607ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText.setAlpha(0f); 608ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectTextShow = 609ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 610ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText, 611ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.ALPHA, 1f), 612ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat( 613ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View.TRANSLATION_Y, 614ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), HINT_REJECT_FADE_TRANSLATION_Y_DP), 615ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0f)); 616ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextShow.setInterpolator(new FastOutLinearInInterpolator()); 617ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextShow.setDuration(ANIMATE_DURATION_SHORT_MILLIS); 618ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextShow.setStartDelay(SWIPE_TO_DECLINE_FADE_IN_DELAY_MILLIS); 619ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 620ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckUp = 621ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 622ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 623ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View.TRANSLATION_Y, 624ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), 400 /* dp */), 625ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), -12 /* dp */)); 626ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckUp.setDuration(ANIMATE_DURATION_LONG_MILLIS); 627ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckUp.setInterpolator( 628ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PathInterpolatorCompat.create( 629ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0 /* controlX1 */, 0 /* controlY1 */, 0 /* controlX2 */, 1 /* controlY2 */)); 630ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 631ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckDown = 632ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 633ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 634ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View.TRANSLATION_Y, 635ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), -12 /* dp */), 636ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0 /* end pos */); 637ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 638ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckDown.setInterpolator(new FastOutSlowInInterpolator()); 639ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 640ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckScaleUp = 641ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createUniformScaleAnimators( 642ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, 643ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0.33f /* beginScale */, 644ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1.1f /* endScale */, 645ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ANIMATE_DURATION_NORMAL_MILLIS, 646ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PathInterpolatorCompat.create( 647ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0.4f /* controlX1 */, 0 /* controlY1 */, 0 /* controlX2 */, 1 /* controlY2 */)); 648ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckScaleDown = 649ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createUniformScaleAnimators( 650ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, 651ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1.1f /* beginScale */, 652ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1 /* endScale */, 653ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ANIMATE_DURATION_NORMAL_MILLIS, 654ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new FastOutSlowInInterpolator()); 655ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 656ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Upward animation chain. 657ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim.play(textUp).with(puckScaleUp).with(puckUp); 658ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 659ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Downward animation chain. 660ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim.play(textDown).with(puckDown).with(puckScaleDown).after(puckUp); 661ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 662ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim.play(rejectTextShow).after(puckUp); 663ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 664ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Add vibration animation. 665ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian addVibrationAnimator(lockEntryAnim); 666ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 667ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim.addListener( 668ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new AnimatorListenerAdapter() { 669ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 670ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public boolean canceled; 671ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 672ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 673ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationCancel(Animator animation) { 674ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationCancel(animation); 675ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian canceled = true; 676ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 677ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 678ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 679ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationEnd(Animator animation) { 680ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationEnd(animation); 681ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (!canceled) { 682ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian onEntryAnimationDone(); 683ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 684ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 685ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian }); 686ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim.start(); 687ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 688ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 689ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @VisibleForTesting 690ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian void onEntryAnimationDone() { 691ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.onEntryAnimationDone", "Swipe entry anim ends."); 692ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (animationState == AnimationState.ENTRY) { 693ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.BOUNCE); 694ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 695ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 696ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 697ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void startSwipeToAnswerBounceAnimation() { 698ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.startSwipeToAnswerBounceAnimation", "Swipe bounce animation."); 699ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 700ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 701ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (ViewUtil.areAnimationsDisabled(getContext())) { 702ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.setTranslationY(0); 703ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.setTranslationY(0); 704ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setScaleY(1f); 705ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground.setScaleX(1f); 706ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText.setAlpha(1f); 707ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText.setTranslationY(0); 708ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return; 709ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 710ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 711ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim = createBreatheAnimation(); 712ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 713ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian answerHint.onBounceStart(); 714ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim.addListener( 715ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new AnimatorListenerAdapter() { 716ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian boolean firstPass = true; 717ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 718ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 719ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationEnd(Animator animation) { 720ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationEnd(animation); 721ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (getContext() != null 722ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian && lockBounceAnim != null 723ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian && animationState == AnimationState.BOUNCE) { 724ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // AnimatorSet doesn't have repeat settings. Instead, we start a new one after the 725ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // previous set is completed, until endAnimation is called. 726ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.v("FlingUpDownMethod.onAnimationEnd", "Bounce again."); 727ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 728ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // If this is the first time repeating the animation, we should recreate it so its 729ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // starting values will be correct 730ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (firstPass) { 731ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim = createBreatheAnimation(); 732ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim.addListener(this); 733ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 734ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian firstPass = false; 735ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian answerHint.onBounceStart(); 736ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim.start(); 737ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 738ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 739ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian }); 740ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim.start(); 741ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 742ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 743ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Animator createBreatheAnimation() { 744ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian AnimatorSet breatheAnimation = new AnimatorSet(); 745ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float textOffset = DpUtil.dpToPx(getContext(), 42 /* dp */); 746ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator textUp = 747ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 748ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText, View.TRANSLATION_Y, 0 /* begin pos */, -textOffset); 749ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setInterpolator(new FastOutSlowInInterpolator()); 750ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 751ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 752ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator textDown = 753ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat(swipeToAnswerText, View.TRANSLATION_Y, -textOffset, 0 /* end pos */); 754ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textDown.setInterpolator(new FastOutSlowInInterpolator()); 755ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 756ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 757ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // "Swipe down to reject" text fade in 758ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectTextShow = ObjectAnimator.ofFloat(swipeToRejectText, View.ALPHA, 1f); 759ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextShow.setInterpolator(new LinearOutSlowInInterpolator()); 760ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextShow.setDuration(ANIMATE_DURATION_SHORT_MILLIS); 761ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextShow.setStartDelay(SWIPE_TO_DECLINE_FADE_IN_DELAY_MILLIS); 762ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 763ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // reject hint text translate in 764ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectTextTranslate = 765ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 766ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText, 767ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View.TRANSLATION_Y, 768ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian DpUtil.dpToPx(getContext(), HINT_REJECT_FADE_TRANSLATION_Y_DP), 769ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0f); 770ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextTranslate.setInterpolator(new FastOutSlowInInterpolator()); 771ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextTranslate.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 772ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 773ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // reject hint text fade out 774ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectTextHide = ObjectAnimator.ofFloat(swipeToRejectText, View.ALPHA, 0f); 775ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextHide.setInterpolator(new FastOutLinearInInterpolator()); 776ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectTextHide.setDuration(ANIMATE_DURATION_SHORT_MILLIS); 777ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 778ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Interpolator curve = 779ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PathInterpolatorCompat.create( 780ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 0.4f /* controlX1 */, 0 /* controlY1 */, 0 /* controlX2 */, 1 /* controlY2 */); 781ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float puckOffset = DpUtil.dpToPx(getContext(), 42 /* dp */); 782ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckUp = ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, -puckOffset); 783ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckUp.setInterpolator(curve); 784ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckUp.setDuration(ANIMATE_DURATION_LONG_MILLIS); 785ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 786ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian final float scale = 1.0625f; 787ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckScaleUp = 788ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createUniformScaleAnimators( 789ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, 790ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1 /* beginScale */, 791ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian scale, 792ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ANIMATE_DURATION_NORMAL_MILLIS, 793ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian curve); 794ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 795ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckDown = 796ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, 0 /* end pos */); 797ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckDown.setInterpolator(new FastOutSlowInInterpolator()); 798ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckDown.setDuration(ANIMATE_DURATION_NORMAL_MILLIS); 799ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 800ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckScaleDown = 801ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createUniformScaleAnimators( 802ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, 803ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian scale, 804ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1 /* endScale */, 805ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ANIMATE_DURATION_NORMAL_MILLIS, 806ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new FastOutSlowInInterpolator()); 807ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 808ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Bounce upward animation chain. 809ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian breatheAnimation 810ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .play(textUp) 811ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(rejectTextHide) 812ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(puckUp) 813ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(puckScaleUp) 814ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .after(167 /* delay */); 815ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 816ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Bounce downward animation chain. 817ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian breatheAnimation 818ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .play(puckDown) 819ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(textDown) 820ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(puckScaleDown) 821ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(rejectTextShow) 822ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(rejectTextTranslate) 823ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .after(puckUp); 824ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 825ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Add vibration animation to the animator set. 826ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian addVibrationAnimator(breatheAnimation); 827ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 828ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return breatheAnimation; 829ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 830ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 831ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void startSwipeToAnswerSettleAnimation() { 832ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 833ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 834ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator puckScale = 835ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 836ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, 837ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.SCALE_X, 1), 838ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.SCALE_Y, 1)); 839ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckScale.setDuration(SETTLE_ANIMATION_DURATION_MILLIS); 840ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 841ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator iconRotation = ObjectAnimator.ofFloat(contactPuckIcon, View.ROTATION, 0); 842ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian iconRotation.setDuration(SETTLE_ANIMATION_DURATION_MILLIS); 843ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 844ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator swipeToAnswerTextFade = 845ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createFadeAnimation(swipeToAnswerText, 1, SETTLE_ANIMATION_DURATION_MILLIS); 846ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 847ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator contactPuckContainerFade = 848ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createFadeAnimation(contactPuckContainer, 1, SETTLE_ANIMATION_DURATION_MILLIS); 849ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 850ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator contactPuckBackgroundFade = 851ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createFadeAnimation(contactPuckBackground, 1, SETTLE_ANIMATION_DURATION_MILLIS); 852ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 853ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator contactPuckIconFade = 854ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createFadeAnimation( 855ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckIcon, shouldShowPhotoInPuck() ? 0 : 1, SETTLE_ANIMATION_DURATION_MILLIS); 856ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 857ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator contactPuckTranslation = 858ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 859ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 860ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0), 861ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0)); 862ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckTranslation.setDuration(SETTLE_ANIMATION_DURATION_MILLIS); 863ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 864ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim = new AnimatorSet(); 865ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim 866ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .play(puckScale) 867ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(iconRotation) 868ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(swipeToAnswerTextFade) 869ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(contactPuckContainerFade) 870ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(contactPuckBackgroundFade) 871ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(contactPuckIconFade) 872ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(contactPuckTranslation); 873ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 874ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim.addListener( 875ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new AnimatorListenerAdapter() { 876ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 877ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationCancel(Animator animation) { 878ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian afterSettleAnimationState = AnimationState.NONE; 879ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 880ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 881ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 882ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationEnd(Animator animation) { 883ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian onSettleAnimationDone(); 884ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 885ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian }); 886ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 887ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim.start(); 888ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 889ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 890ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @VisibleForTesting 891ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian void onSettleAnimationDone() { 892ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (afterSettleAnimationState != AnimationState.NONE) { 893ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int nextState = afterSettleAnimationState; 894ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian afterSettleAnimationState = AnimationState.NONE; 895ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim = null; 896ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 897ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(nextState); 898ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 899ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 900ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 901ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private ObjectAnimator createFadeAnimation(View target, float targetAlpha, long duration) { 902ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(target, View.ALPHA, targetAlpha); 903ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian objectAnimator.setDuration(duration); 904ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return objectAnimator; 905ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 906ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 907ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void startSwipeToAnswerHintAnimation() { 908ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (rejectHintHide != null) { 909ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide.cancel(); 910ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 911ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 912ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 913ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian resetTouchState(); 914ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 915ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (ViewUtil.areAnimationsDisabled(getContext())) { 916ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian onHintAnimationDone(false); 917ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return; 918ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 919ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 920ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim = new AnimatorSet(); 921ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float jumpOffset = DpUtil.dpToPx(getContext(), HINT_JUMP_DP); 922ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float dipOffset = DpUtil.dpToPx(getContext(), HINT_DIP_DP); 923ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float scaleSize = HINT_SCALE_RATIO; 924ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float textOffset = jumpOffset + (scaleSize - 1) * contactPuckBackground.getHeight(); 925ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int shortAnimTime = 926ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getContext().getResources().getInteger(android.R.integer.config_shortAnimTime); 927ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian int mediumAnimTime = 928ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getContext().getResources().getInteger(android.R.integer.config_mediumAnimTime); 929ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 930ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Puck squashes to anticipate jump 931ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator puckAnticipate = 932ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 933ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 934ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.SCALE_Y, .95f), 935ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.SCALE_X, 1.05f)); 936ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckAnticipate.setRepeatCount(1); 937ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckAnticipate.setRepeatMode(ValueAnimator.REVERSE); 938ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckAnticipate.setDuration(shortAnimTime / 2); 939ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckAnticipate.setInterpolator(new DecelerateInterpolator()); 940ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckAnticipate.addListener( 941ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new AnimatorListenerAdapter() { 942ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 943ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationStart(Animator animation) { 944ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationStart(animation); 945ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.setPivotY(contactPuckContainer.getHeight()); 946ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 947ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 948ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 949ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationEnd(Animator animation) { 950ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationEnd(animation); 951ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.setPivotY(contactPuckContainer.getHeight() / 2); 952ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 953ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian }); 954ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 955ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Ensure puck is at the right starting point for the jump 956ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator puckResetTranslation = 957ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 958ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, 959ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0), 960ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.TRANSLATION_X, 0)); 961ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckResetTranslation.setDuration(shortAnimTime / 2); 962ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckAnticipate.setInterpolator(new DecelerateInterpolator()); 963ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 964ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator textUp = ObjectAnimator.ofFloat(swipeToAnswerText, View.TRANSLATION_Y, -textOffset); 965ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setInterpolator(new LinearOutSlowInInterpolator()); 966ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textUp.setDuration(shortAnimTime); 967ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 968ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckUp = ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, -jumpOffset); 969ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckUp.setInterpolator(new LinearOutSlowInInterpolator()); 970ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckUp.setDuration(shortAnimTime); 971ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 972ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckScaleUp = 973ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createUniformScaleAnimators( 974ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, 1f, scaleSize, shortAnimTime, new LinearOutSlowInInterpolator()); 975ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 976ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectHintShow = 977ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 978ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToRejectText, 979ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.ALPHA, 1f), 980ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 0f)); 981ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintShow.setDuration(shortAnimTime); 982ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 983ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectHintDip = 984ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat(swipeToRejectText, View.TRANSLATION_Y, dipOffset); 985ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintDip.setInterpolator(new LinearOutSlowInInterpolator()); 986ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintDip.setDuration(shortAnimTime); 987ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 988ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator textDown = ObjectAnimator.ofFloat(swipeToAnswerText, View.TRANSLATION_Y, 0); 989ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textDown.setInterpolator(new LinearOutSlowInInterpolator()); 990ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian textDown.setDuration(mediumAnimTime); 991ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 992ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckDown = ObjectAnimator.ofFloat(contactPuckContainer, View.TRANSLATION_Y, 0); 993ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian BounceInterpolator bounce = new BounceInterpolator(); 994ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckDown.setInterpolator(bounce); 995ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian puckDown.setDuration(mediumAnimTime); 996ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 997ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator puckScaleDown = 998ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian createUniformScaleAnimators( 999ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckBackground, scaleSize, 1f, shortAnimTime, new LinearOutSlowInInterpolator()); 1000ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1001ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian Animator rejectHintUp = ObjectAnimator.ofFloat(swipeToRejectText, View.TRANSLATION_Y, 0); 1002ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintUp.setInterpolator(new LinearOutSlowInInterpolator()); 1003ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintUp.setDuration(mediumAnimTime); 1004ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1005ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim.play(puckAnticipate).with(puckResetTranslation).before(puckUp); 1006ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim 1007ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .play(textUp) 1008ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(puckUp) 1009ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(puckScaleUp) 1010ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(rejectHintDip) 1011ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian .with(rejectHintShow); 1012ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim.play(textDown).with(puckDown).with(puckScaleDown).with(rejectHintUp).after(puckUp); 1013ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim.start(); 1014ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1015ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide = ObjectAnimator.ofFloat(swipeToRejectText, View.ALPHA, 0); 1016ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide.setStartDelay(HINT_REJECT_SHOW_DURATION_MILLIS); 1017ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide.addListener( 1018ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian new AnimatorListenerAdapter() { 1019ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1020ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private boolean canceled; 1021ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1022ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 1023ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationCancel(Animator animation) { 1024ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationCancel(animation); 1025ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian canceled = true; 1026ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide = null; 1027ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1028ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1029ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 1030ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public void onAnimationEnd(Animator animation) { 1031ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian super.onAnimationEnd(animation); 1032ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian onHintAnimationDone(canceled); 1033ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1034ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian }); 1035ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide.start(); 1036ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1037ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1038ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @VisibleForTesting 1039ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian void onHintAnimationDone(boolean canceled) { 1040ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (!canceled && animationState == AnimationState.HINT) { 1041ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.BOUNCE); 1042ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1043ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide = null; 1044ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1045ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1046ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void clearSwipeToAnswerUi() { 1047ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.clearSwipeToAnswerUi", "Clear swipe animation."); 1048ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian endAnimation(); 1049ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.setVisibility(View.GONE); 1050ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.setVisibility(View.GONE); 1051ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1052ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1053ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void endAnimation() { 1054ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.endAnimation", "End animations."); 1055ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (lockSettleAnim != null) { 1056ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim.cancel(); 1057ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockSettleAnim = null; 1058ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1059ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (lockBounceAnim != null) { 1060ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim.cancel(); 1061ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockBounceAnim = null; 1062ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1063ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (lockEntryAnim != null) { 1064ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim.cancel(); 1065ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockEntryAnim = null; 1066ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1067ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (lockHintAnim != null) { 1068ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim.cancel(); 1069ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian lockHintAnim = null; 1070ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1071ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (rejectHintHide != null) { 1072ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide.cancel(); 1073ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian rejectHintHide = null; 1074ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1075ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (vibrationAnimator != null) { 1076ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian vibrationAnimator.end(); 1077ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian vibrationAnimator = null; 1078ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1079ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian answerHint.onBounceEnd(); 1080ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1081ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1082ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Create an animator to scale on X/Y directions uniformly. 1083ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Animator createUniformScaleAnimators( 1084ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian View target, float begin, float end, long duration, Interpolator interpolator) { 1085ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator animator = 1086ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofPropertyValuesHolder( 1087ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian target, 1088ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.SCALE_X, begin, end), 1089ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian PropertyValuesHolder.ofFloat(View.SCALE_Y, begin, end)); 1090ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian animator.setDuration(duration); 1091ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian animator.setInterpolator(interpolator); 1092ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return animator; 1093ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1094ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1095ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void addVibrationAnimator(AnimatorSet animatorSet) { 1096ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (vibrationAnimator != null) { 1097ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian vibrationAnimator.end(); 1098ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1099ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Note that we animate the value between 0 and 1, but internally VibrateInterpolator will 1101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // translate it into actually X translation value. 1102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian vibrationAnimator = 1103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ObjectAnimator.ofFloat( 1104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer, View.TRANSLATION_X, 0 /* begin value */, 1 /* end value */); 1105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian vibrationAnimator.setDuration(VIBRATION_TIME_MILLIS); 1106ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian vibrationAnimator.setInterpolator(new VibrateInterpolator(getContext())); 1107ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian animatorSet.play(vibrationAnimator).after(0 /* delay */); 1109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void performAccept() { 1112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.performAccept", null); 1113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.setVisibility(View.GONE); 1114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.setVisibility(View.GONE); 1115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Complete the animation loop. 1117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.COMPLETED); 1118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getParent().answerFromMethod(); 1119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1120ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1121ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private void performReject() { 1122ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian LogUtil.i("FlingUpDownMethod.performReject", null); 1123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian swipeToAnswerText.setVisibility(View.GONE); 1124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian contactPuckContainer.setVisibility(View.GONE); 1125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Complete the animation loop. 1127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian setAnimationState(AnimationState.COMPLETED); 1128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian getParent().rejectFromMethod(); 1129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian /** Custom interpolator class for puck vibration. */ 1132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static class VibrateInterpolator implements Interpolator { 1133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_UP_BEGIN_MS = 583; 1135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_UP_DURATION_MS = 167; 1136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_UP_END_MS = RAMP_UP_BEGIN_MS + RAMP_UP_DURATION_MS; 1137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_DOWN_BEGIN_MS = 1_583; 1138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_DOWN_DURATION_MS = 250; 1139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_DOWN_END_MS = RAMP_DOWN_BEGIN_MS + RAMP_DOWN_DURATION_MS; 1140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private static final long RAMP_TOTAL_TIME_MS = RAMP_DOWN_END_MS; 1141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private final float ampMax; 1142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private final float freqMax = 80; 1143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian private Interpolator sliderInterpolator = new FastOutSlowInInterpolator(); 1144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian VibrateInterpolator(Context context) { 1146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian ampMax = DpUtil.dpToPx(context, 1 /* dp */); 1147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian @Override 1150ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian public float getInterpolation(float t) { 1151ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float slider = 0; 1152ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float time = t * RAMP_TOTAL_TIME_MS; 1153ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1154ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Calculate the slider value based on RAMP_UP and RAMP_DOWN times. Between RAMP_UP and 1155ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // RAMP_DOWN, the slider remains the maximum value of 1. 1156ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian if (time > RAMP_UP_BEGIN_MS && time < RAMP_UP_END_MS) { 1157ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Ramp up. 1158ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian slider = 1159ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian sliderInterpolator.getInterpolation( 1160ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian (time - RAMP_UP_BEGIN_MS) / (float) RAMP_UP_DURATION_MS); 1161ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else if ((time >= RAMP_UP_END_MS) && time <= RAMP_DOWN_BEGIN_MS) { 1162ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Vibrate at maximum 1163ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian slider = 1; 1164ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } else if (time > RAMP_DOWN_BEGIN_MS && time < RAMP_DOWN_END_MS) { 1165ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian // Ramp down. 1166ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian slider = 1167ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1 1168ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian - sliderInterpolator.getInterpolation( 1169ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian (time - RAMP_DOWN_BEGIN_MS) / (float) RAMP_DOWN_DURATION_MS); 1170ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1171ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1172ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float ampNormalized = ampMax * slider; 1173ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian float freqNormalized = freqMax * slider; 1174ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian 1175ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian return (float) (ampNormalized * Math.sin(time * freqNormalized)); 1176ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1177ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian } 1178ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian} 1179