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.support.annotation.DimenRes; 25 import android.support.v4.view.animation.FastOutSlowInInterpolator; 26 import android.util.TypedValue; 27 import android.view.LayoutInflater; 28 import android.view.View; 29 import android.view.ViewGroup; 30 import android.view.animation.Interpolator; 31 import android.view.animation.LinearInterpolator; 32 import android.widget.TextView; 33 34 /** An Answer hint that uses a green swiping dot. */ 35 public class DotAnswerHint implements AnswerHint { 36 37 private static final float ANSWER_HINT_SMALL_ALPHA = 0.8f; 38 private static final float ANSWER_HINT_MID_ALPHA = 0.5f; 39 private static final float ANSWER_HINT_LARGE_ALPHA = 0.2f; 40 41 private static final long FADE_IN_DELAY_SCALE_MILLIS = 380; 42 private static final long FADE_IN_DURATION_SCALE_MILLIS = 200; 43 private static final long FADE_IN_DELAY_ALPHA_MILLIS = 340; 44 private static final long FADE_IN_DURATION_ALPHA_MILLIS = 50; 45 46 private static final long SWIPE_UP_DURATION_ALPHA_MILLIS = 500; 47 48 private static final long FADE_OUT_DELAY_SCALE_SMALL_MILLIS = 90; 49 private static final long FADE_OUT_DELAY_SCALE_MID_MILLIS = 70; 50 private static final long FADE_OUT_DELAY_SCALE_LARGE_MILLIS = 10; 51 private static final long FADE_OUT_DURATION_SCALE_MILLIS = 100; 52 private static final long FADE_OUT_DELAY_ALPHA_MILLIS = 130; 53 private static final long FADE_OUT_DURATION_ALPHA_MILLIS = 170; 54 55 private final Context context; 56 private final long puckUpDurationMillis; 57 private final long puckUpDelayMillis; 58 59 private View puck; 60 61 private View answerHintSmall; 62 private View answerHintMid; 63 private View answerHintLarge; 64 private View answerHintContainer; 65 private AnimatorSet answerGestureHintAnim; 66 DotAnswerHint(Context context, long puckUpDurationMillis, long puckUpDelayMillis)67 public DotAnswerHint(Context context, long puckUpDurationMillis, long puckUpDelayMillis) { 68 this.context = context; 69 this.puckUpDurationMillis = puckUpDurationMillis; 70 this.puckUpDelayMillis = puckUpDelayMillis; 71 } 72 73 @Override onCreateView( LayoutInflater inflater, ViewGroup container, View puck, TextView hintText)74 public void onCreateView( 75 LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) { 76 this.puck = puck; 77 View view = inflater.inflate(R.layout.dot_hint, container, true); 78 answerHintContainer = view.findViewById(R.id.answer_hint_container); 79 answerHintSmall = view.findViewById(R.id.answer_hint_small); 80 answerHintMid = view.findViewById(R.id.answer_hint_mid); 81 answerHintLarge = view.findViewById(R.id.answer_hint_large); 82 hintText.setTextSize( 83 TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size)); 84 } 85 86 @Override onBounceStart()87 public void onBounceStart() { 88 if (answerGestureHintAnim == null) { 89 answerGestureHintAnim = new AnimatorSet(); 90 int[] puckLocation = new int[2]; 91 puck.getLocationInWindow(puckLocation); 92 answerHintContainer.setY(puckLocation[1] + getDimension(R.dimen.hint_initial_offset)); 93 94 Animator fadeIn = createFadeIn(); 95 96 Animator swipeUp = 97 ObjectAnimator.ofFloat( 98 answerHintContainer, 99 View.TRANSLATION_Y, 100 puckLocation[1] - getDimension(R.dimen.hint_offset)); 101 swipeUp.setInterpolator(new FastOutSlowInInterpolator()); 102 swipeUp.setDuration(SWIPE_UP_DURATION_ALPHA_MILLIS); 103 104 Animator fadeOut = createFadeOut(); 105 106 answerGestureHintAnim.play(fadeIn).after(puckUpDelayMillis); 107 answerGestureHintAnim.play(swipeUp).after(fadeIn); 108 // The fade out should start fading the alpha just as the puck is dropping. Scaling will start 109 // a bit earlier. 110 answerGestureHintAnim 111 .play(fadeOut) 112 .after(puckUpDelayMillis + puckUpDurationMillis - FADE_OUT_DELAY_ALPHA_MILLIS); 113 114 fadeIn.addListener( 115 new AnimatorListenerAdapter() { 116 @Override 117 public void onAnimationStart(Animator animation) { 118 super.onAnimationStart(animation); 119 answerHintSmall.setAlpha(0); 120 answerHintSmall.setScaleX(1); 121 answerHintSmall.setScaleY(1); 122 answerHintMid.setAlpha(0); 123 answerHintMid.setScaleX(1); 124 answerHintMid.setScaleY(1); 125 answerHintLarge.setAlpha(0); 126 answerHintLarge.setScaleX(1); 127 answerHintLarge.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( 140 createFadeInScaleAndAlpha( 141 answerHintSmall, 142 R.dimen.hint_small_begin_size, 143 R.dimen.hint_small_end_size, 144 ANSWER_HINT_SMALL_ALPHA)) 145 .with( 146 createFadeInScaleAndAlpha( 147 answerHintMid, 148 R.dimen.hint_mid_begin_size, 149 R.dimen.hint_mid_end_size, 150 ANSWER_HINT_MID_ALPHA)) 151 .with( 152 createFadeInScaleAndAlpha( 153 answerHintLarge, 154 R.dimen.hint_large_begin_size, 155 R.dimen.hint_large_end_size, 156 ANSWER_HINT_LARGE_ALPHA)); 157 return set; 158 } 159 createFadeInScaleAndAlpha( View target, @DimenRes int beginSize, @DimenRes int endSize, float endAlpha)160 private Animator createFadeInScaleAndAlpha( 161 View target, @DimenRes int beginSize, @DimenRes int endSize, float endAlpha) { 162 Animator scale = 163 createUniformScaleAnimator( 164 target, 165 getDimension(beginSize), 166 getDimension(beginSize), 167 getDimension(endSize), 168 FADE_IN_DURATION_SCALE_MILLIS, 169 FADE_IN_DELAY_SCALE_MILLIS, 170 new LinearInterpolator()); 171 Animator alpha = 172 createAlphaAnimator( 173 target, 174 0f, 175 endAlpha, 176 FADE_IN_DURATION_ALPHA_MILLIS, 177 FADE_IN_DELAY_ALPHA_MILLIS, 178 new LinearInterpolator()); 179 AnimatorSet set = new AnimatorSet(); 180 set.play(scale).with(alpha); 181 return set; 182 } 183 createFadeOut()184 private Animator createFadeOut() { 185 AnimatorSet set = new AnimatorSet(); 186 set.play( 187 createFadeOutScaleAndAlpha( 188 answerHintSmall, 189 R.dimen.hint_small_begin_size, 190 R.dimen.hint_small_end_size, 191 FADE_OUT_DELAY_SCALE_SMALL_MILLIS, 192 ANSWER_HINT_SMALL_ALPHA)) 193 .with( 194 createFadeOutScaleAndAlpha( 195 answerHintMid, 196 R.dimen.hint_mid_begin_size, 197 R.dimen.hint_mid_end_size, 198 FADE_OUT_DELAY_SCALE_MID_MILLIS, 199 ANSWER_HINT_MID_ALPHA)) 200 .with( 201 createFadeOutScaleAndAlpha( 202 answerHintLarge, 203 R.dimen.hint_large_begin_size, 204 R.dimen.hint_large_end_size, 205 FADE_OUT_DELAY_SCALE_LARGE_MILLIS, 206 ANSWER_HINT_LARGE_ALPHA)); 207 return set; 208 } 209 createFadeOutScaleAndAlpha( View target, @DimenRes int beginSize, @DimenRes int endSize, long scaleDelay, float endAlpha)210 private Animator createFadeOutScaleAndAlpha( 211 View target, 212 @DimenRes int beginSize, 213 @DimenRes int endSize, 214 long scaleDelay, 215 float endAlpha) { 216 Animator scale = 217 createUniformScaleAnimator( 218 target, 219 getDimension(beginSize), 220 getDimension(endSize), 221 getDimension(beginSize), 222 FADE_OUT_DURATION_SCALE_MILLIS, 223 scaleDelay, 224 new LinearInterpolator()); 225 Animator alpha = 226 createAlphaAnimator( 227 target, 228 endAlpha, 229 0.0f, 230 FADE_OUT_DURATION_ALPHA_MILLIS, 231 FADE_OUT_DELAY_ALPHA_MILLIS, 232 new LinearInterpolator()); 233 AnimatorSet set = new AnimatorSet(); 234 set.play(scale).with(alpha); 235 return set; 236 } 237 238 @Override onBounceEnd()239 public void onBounceEnd() { 240 if (answerGestureHintAnim != null) { 241 answerGestureHintAnim.end(); 242 answerGestureHintAnim = null; 243 answerHintContainer.setVisibility(View.GONE); 244 } 245 } 246 247 @Override onAnswered()248 public void onAnswered() { 249 AnswerHintFactory.increaseAnsweredCount(context); 250 } 251 getDimension(@imenRes int id)252 private float getDimension(@DimenRes int id) { 253 return context.getResources().getDimension(id); 254 } 255 createUniformScaleAnimator( View target, float original, float begin, float end, long duration, long delay, Interpolator interpolator)256 private static Animator createUniformScaleAnimator( 257 View target, 258 float original, 259 float begin, 260 float end, 261 long duration, 262 long delay, 263 Interpolator interpolator) { 264 float scaleBegin = begin / original; 265 float scaleEnd = end / original; 266 Animator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, scaleBegin, scaleEnd); 267 Animator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, scaleBegin, scaleEnd); 268 scaleX.setDuration(duration); 269 scaleY.setDuration(duration); 270 scaleX.setInterpolator(interpolator); 271 scaleY.setInterpolator(interpolator); 272 AnimatorSet set = new AnimatorSet(); 273 set.play(scaleX).with(scaleY).after(delay); 274 return set; 275 } 276 createAlphaAnimator( View target, float begin, float end, long duration, long delay, Interpolator interpolator)277 private static Animator createAlphaAnimator( 278 View target, float begin, float end, long duration, long delay, Interpolator interpolator) { 279 Animator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, begin, end); 280 alpha.setDuration(duration); 281 alpha.setInterpolator(interpolator); 282 alpha.setStartDelay(delay); 283 return alpha; 284 } 285 } 286