1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.incallui.answer.impl.hint; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.AnimatorSet; 22 import android.animation.ObjectAnimator; 23 import android.content.Context; 24 import android.graphics.drawable.Drawable; 25 import android.support.annotation.DimenRes; 26 import android.support.annotation.NonNull; 27 import android.support.v4.view.animation.FastOutSlowInInterpolator; 28 import android.util.TypedValue; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.view.animation.Interpolator; 33 import android.view.animation.LinearInterpolator; 34 import android.widget.ImageView; 35 import android.widget.TextView; 36 import com.android.dialer.common.Assert; 37 38 /** 39 * An Answer hint that animates a {@link Drawable} payload with animation similar to {@link 40 * DotAnswerHint}. 41 */ 42 public final class PawAnswerHint implements AnswerHint { 43 44 private static final long FADE_IN_DELAY_SCALE_MILLIS = 380; 45 private static final long FADE_IN_DURATION_SCALE_MILLIS = 200; 46 private static final long FADE_IN_DELAY_ALPHA_MILLIS = 340; 47 private static final long FADE_IN_DURATION_ALPHA_MILLIS = 50; 48 49 private static final long SWIPE_UP_DURATION_ALPHA_MILLIS = 500; 50 51 private static final long FADE_OUT_DELAY_SCALE_SMALL_MILLIS = 90; 52 private static final long FADE_OUT_DURATION_SCALE_MILLIS = 100; 53 private static final long FADE_OUT_DELAY_ALPHA_MILLIS = 130; 54 private static final long FADE_OUT_DURATION_ALPHA_MILLIS = 170; 55 56 private static final float IMAGE_SCALE = 1.5f; 57 private static final float FADE_SCALE = 2.0f; 58 59 private final Context context; 60 private final Drawable payload; 61 private final long puckUpDurationMillis; 62 private final long puckUpDelayMillis; 63 64 private View puck; 65 private View payloadView; 66 private View answerHintContainer; 67 private AnimatorSet answerGestureHintAnim; 68 PawAnswerHint( @onNull Context context, @NonNull Drawable payload, long puckUpDurationMillis, long puckUpDelayMillis)69 public PawAnswerHint( 70 @NonNull Context context, 71 @NonNull Drawable payload, 72 long puckUpDurationMillis, 73 long puckUpDelayMillis) { 74 this.context = Assert.isNotNull(context); 75 this.payload = Assert.isNotNull(payload); 76 this.puckUpDurationMillis = puckUpDurationMillis; 77 this.puckUpDelayMillis = puckUpDelayMillis; 78 } 79 80 @Override onCreateView( LayoutInflater inflater, ViewGroup container, View puck, TextView hintText)81 public void onCreateView( 82 LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) { 83 this.puck = puck; 84 View view = inflater.inflate(R.layout.paw_hint, container, true); 85 answerHintContainer = view.findViewById(R.id.answer_hint_container); 86 payloadView = view.findViewById(R.id.paw_image); 87 hintText.setTextSize( 88 TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size)); 89 ((ImageView) payloadView).setImageDrawable(payload); 90 } 91 92 @Override onBounceStart()93 public void onBounceStart() { 94 if (answerGestureHintAnim == null) { 95 answerGestureHintAnim = new AnimatorSet(); 96 int[] puckLocation = new int[2]; 97 puck.getLocationInWindow(puckLocation); 98 answerHintContainer.setY(puckLocation[1] + getDimension(R.dimen.hint_initial_offset)); 99 100 Animator fadeIn = createFadeIn(); 101 102 Animator swipeUp = 103 ObjectAnimator.ofFloat( 104 answerHintContainer, 105 View.TRANSLATION_Y, 106 puckLocation[1] - getDimension(R.dimen.hint_offset)); 107 swipeUp.setInterpolator(new FastOutSlowInInterpolator()); 108 swipeUp.setDuration(SWIPE_UP_DURATION_ALPHA_MILLIS); 109 110 Animator fadeOut = createFadeOut(); 111 112 answerGestureHintAnim.play(fadeIn).after(puckUpDelayMillis); 113 answerGestureHintAnim.play(swipeUp).after(fadeIn); 114 // The fade out should start fading the alpha just as the puck is dropping. Scaling will start 115 // a bit earlier. 116 answerGestureHintAnim 117 .play(fadeOut) 118 .after(puckUpDelayMillis + puckUpDurationMillis - FADE_OUT_DELAY_ALPHA_MILLIS); 119 120 fadeIn.addListener( 121 new AnimatorListenerAdapter() { 122 @Override 123 public void onAnimationStart(Animator animation) { 124 super.onAnimationStart(animation); 125 payloadView.setAlpha(0); 126 payloadView.setScaleX(1); 127 payloadView.setScaleY(1); 128 answerHintContainer.setY(puckLocation[1] + getDimension(R.dimen.hint_initial_offset)); 129 answerHintContainer.setVisibility(View.VISIBLE); 130 } 131 }); 132 } 133 134 answerGestureHintAnim.start(); 135 } 136 createFadeIn()137 private Animator createFadeIn() { 138 AnimatorSet set = new AnimatorSet(); 139 set.play(createFadeInScaleAndAlpha(payloadView)); 140 return set; 141 } 142 createFadeInScaleAndAlpha(View target)143 private static Animator createFadeInScaleAndAlpha(View target) { 144 Animator scale = 145 createUniformScaleAnimator( 146 target, 147 FADE_SCALE, 148 IMAGE_SCALE, 149 FADE_IN_DURATION_SCALE_MILLIS, 150 FADE_IN_DELAY_SCALE_MILLIS, 151 new LinearInterpolator()); 152 Animator alpha = 153 createAlphaAnimator( 154 target, 155 0f, 156 1.0f, 157 FADE_IN_DURATION_ALPHA_MILLIS, 158 FADE_IN_DELAY_ALPHA_MILLIS, 159 new LinearInterpolator()); 160 AnimatorSet set = new AnimatorSet(); 161 set.play(scale).with(alpha); 162 return set; 163 } 164 createFadeOut()165 private Animator createFadeOut() { 166 AnimatorSet set = new AnimatorSet(); 167 set.play(createFadeOutScaleAndAlpha(payloadView, FADE_OUT_DELAY_SCALE_SMALL_MILLIS)); 168 return set; 169 } 170 createFadeOutScaleAndAlpha(View target, long scaleDelay)171 private static Animator createFadeOutScaleAndAlpha(View target, long scaleDelay) { 172 Animator scale = 173 createUniformScaleAnimator( 174 target, 175 IMAGE_SCALE, 176 FADE_SCALE, 177 FADE_OUT_DURATION_SCALE_MILLIS, 178 scaleDelay, 179 new LinearInterpolator()); 180 Animator alpha = 181 createAlphaAnimator( 182 target, 183 1.0f, 184 0.0f, 185 FADE_OUT_DURATION_ALPHA_MILLIS, 186 FADE_OUT_DELAY_ALPHA_MILLIS, 187 new LinearInterpolator()); 188 AnimatorSet set = new AnimatorSet(); 189 set.play(scale).with(alpha); 190 return set; 191 } 192 193 @Override onBounceEnd()194 public void onBounceEnd() { 195 if (answerGestureHintAnim != null) { 196 answerGestureHintAnim.end(); 197 answerGestureHintAnim = null; 198 answerHintContainer.setVisibility(View.GONE); 199 } 200 } 201 202 @Override onAnswered()203 public void onAnswered() { 204 // Do nothing 205 } 206 getDimension(@imenRes int id)207 private float getDimension(@DimenRes int id) { 208 return context.getResources().getDimension(id); 209 } 210 createUniformScaleAnimator( View target, float scaleBegin, float scaleEnd, long duration, long delay, Interpolator interpolator)211 private static Animator createUniformScaleAnimator( 212 View target, 213 float scaleBegin, 214 float scaleEnd, 215 long duration, 216 long delay, 217 Interpolator interpolator) { 218 Animator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, scaleBegin, scaleEnd); 219 Animator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, scaleBegin, scaleEnd); 220 scaleX.setDuration(duration); 221 scaleY.setDuration(duration); 222 scaleX.setInterpolator(interpolator); 223 scaleY.setInterpolator(interpolator); 224 AnimatorSet set = new AnimatorSet(); 225 set.play(scaleX).with(scaleY).after(delay); 226 return set; 227 } 228 createAlphaAnimator( View target, float begin, float end, long duration, long delay, Interpolator interpolator)229 private static Animator createAlphaAnimator( 230 View target, float begin, float end, long duration, long delay, Interpolator interpolator) { 231 Animator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, begin, end); 232 alpha.setDuration(duration); 233 alpha.setInterpolator(interpolator); 234 alpha.setStartDelay(delay); 235 return alpha; 236 } 237 } 238