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 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 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 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 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 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 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 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 239 public void onBounceEnd() { 240 if (answerGestureHintAnim != null) { 241 answerGestureHintAnim.end(); 242 answerGestureHintAnim = null; 243 answerHintContainer.setVisibility(View.GONE); 244 } 245 } 246 247 @Override 248 public void onAnswered() { 249 AnswerHintFactory.increaseAnsweredCount(context); 250 } 251 252 private float getDimension(@DimenRes int id) { 253 return context.getResources().getDimension(id); 254 } 255 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 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