| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License |
| */ |
| |
| package com.android.incallui.answer.impl.hint; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.AnimatorSet; |
| import android.animation.ObjectAnimator; |
| import android.content.Context; |
| import android.support.annotation.DimenRes; |
| import android.support.v4.view.animation.FastOutSlowInInterpolator; |
| import android.util.TypedValue; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.animation.Interpolator; |
| import android.view.animation.LinearInterpolator; |
| import android.widget.TextView; |
| |
| /** An Answer hint that uses a green swiping dot. */ |
| public class DotAnswerHint implements AnswerHint { |
| |
| private static final float ANSWER_HINT_SMALL_ALPHA = 0.8f; |
| private static final float ANSWER_HINT_MID_ALPHA = 0.5f; |
| private static final float ANSWER_HINT_LARGE_ALPHA = 0.2f; |
| |
| private static final long FADE_IN_DELAY_SCALE_MILLIS = 380; |
| private static final long FADE_IN_DURATION_SCALE_MILLIS = 200; |
| private static final long FADE_IN_DELAY_ALPHA_MILLIS = 340; |
| private static final long FADE_IN_DURATION_ALPHA_MILLIS = 50; |
| |
| private static final long SWIPE_UP_DURATION_ALPHA_MILLIS = 500; |
| |
| private static final long FADE_OUT_DELAY_SCALE_SMALL_MILLIS = 90; |
| private static final long FADE_OUT_DELAY_SCALE_MID_MILLIS = 70; |
| private static final long FADE_OUT_DELAY_SCALE_LARGE_MILLIS = 10; |
| private static final long FADE_OUT_DURATION_SCALE_MILLIS = 100; |
| private static final long FADE_OUT_DELAY_ALPHA_MILLIS = 130; |
| private static final long FADE_OUT_DURATION_ALPHA_MILLIS = 170; |
| |
| private final Context context; |
| private final long puckUpDurationMillis; |
| private final long puckUpDelayMillis; |
| |
| private View puck; |
| |
| private View answerHintSmall; |
| private View answerHintMid; |
| private View answerHintLarge; |
| private View answerHintContainer; |
| private AnimatorSet answerGestureHintAnim; |
| |
| public DotAnswerHint(Context context, long puckUpDurationMillis, long puckUpDelayMillis) { |
| this.context = context; |
| this.puckUpDurationMillis = puckUpDurationMillis; |
| this.puckUpDelayMillis = puckUpDelayMillis; |
| } |
| |
| @Override |
| public void onCreateView( |
| LayoutInflater inflater, ViewGroup container, View puck, TextView hintText) { |
| this.puck = puck; |
| View view = inflater.inflate(R.layout.dot_hint, container, true); |
| answerHintContainer = view.findViewById(R.id.answer_hint_container); |
| answerHintSmall = view.findViewById(R.id.answer_hint_small); |
| answerHintMid = view.findViewById(R.id.answer_hint_mid); |
| answerHintLarge = view.findViewById(R.id.answer_hint_large); |
| hintText.setTextSize( |
| TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size)); |
| } |
| |
| @Override |
| public void onBounceStart() { |
| if (answerGestureHintAnim == null) { |
| answerGestureHintAnim = new AnimatorSet(); |
| answerHintContainer.setY(puck.getY() + getDimension(R.dimen.hint_initial_offset)); |
| |
| Animator fadeIn = createFadeIn(); |
| |
| Animator swipeUp = |
| ObjectAnimator.ofFloat( |
| answerHintContainer, |
| View.TRANSLATION_Y, |
| puck.getY() - getDimension(R.dimen.hint_offset)); |
| swipeUp.setInterpolator(new FastOutSlowInInterpolator()); |
| swipeUp.setDuration(SWIPE_UP_DURATION_ALPHA_MILLIS); |
| |
| Animator fadeOut = createFadeOut(); |
| |
| answerGestureHintAnim.play(fadeIn).after(puckUpDelayMillis); |
| answerGestureHintAnim.play(swipeUp).after(fadeIn); |
| // The fade out should start fading the alpha just as the puck is dropping. Scaling will start |
| // a bit earlier. |
| answerGestureHintAnim |
| .play(fadeOut) |
| .after(puckUpDelayMillis + puckUpDurationMillis - FADE_OUT_DELAY_ALPHA_MILLIS); |
| |
| fadeIn.addListener( |
| new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationStart(Animator animation) { |
| super.onAnimationStart(animation); |
| answerHintSmall.setAlpha(0); |
| answerHintSmall.setScaleX(1); |
| answerHintSmall.setScaleY(1); |
| answerHintMid.setAlpha(0); |
| answerHintMid.setScaleX(1); |
| answerHintMid.setScaleY(1); |
| answerHintLarge.setAlpha(0); |
| answerHintLarge.setScaleX(1); |
| answerHintLarge.setScaleY(1); |
| answerHintContainer.setY(puck.getY() + getDimension(R.dimen.hint_initial_offset)); |
| answerHintContainer.setVisibility(View.VISIBLE); |
| } |
| }); |
| } |
| |
| answerGestureHintAnim.start(); |
| } |
| |
| private Animator createFadeIn() { |
| AnimatorSet set = new AnimatorSet(); |
| set.play( |
| createFadeInScaleAndAlpha( |
| answerHintSmall, |
| R.dimen.hint_small_begin_size, |
| R.dimen.hint_small_end_size, |
| ANSWER_HINT_SMALL_ALPHA)) |
| .with( |
| createFadeInScaleAndAlpha( |
| answerHintMid, |
| R.dimen.hint_mid_begin_size, |
| R.dimen.hint_mid_end_size, |
| ANSWER_HINT_MID_ALPHA)) |
| .with( |
| createFadeInScaleAndAlpha( |
| answerHintLarge, |
| R.dimen.hint_large_begin_size, |
| R.dimen.hint_large_end_size, |
| ANSWER_HINT_LARGE_ALPHA)); |
| return set; |
| } |
| |
| private Animator createFadeInScaleAndAlpha( |
| View target, @DimenRes int beginSize, @DimenRes int endSize, float endAlpha) { |
| Animator scale = |
| createUniformScaleAnimator( |
| target, |
| getDimension(beginSize), |
| getDimension(beginSize), |
| getDimension(endSize), |
| FADE_IN_DURATION_SCALE_MILLIS, |
| FADE_IN_DELAY_SCALE_MILLIS, |
| new LinearInterpolator()); |
| Animator alpha = |
| createAlphaAnimator( |
| target, |
| 0f, |
| endAlpha, |
| FADE_IN_DURATION_ALPHA_MILLIS, |
| FADE_IN_DELAY_ALPHA_MILLIS, |
| new LinearInterpolator()); |
| AnimatorSet set = new AnimatorSet(); |
| set.play(scale).with(alpha); |
| return set; |
| } |
| |
| private Animator createFadeOut() { |
| AnimatorSet set = new AnimatorSet(); |
| set.play( |
| createFadeOutScaleAndAlpha( |
| answerHintSmall, |
| R.dimen.hint_small_begin_size, |
| R.dimen.hint_small_end_size, |
| FADE_OUT_DELAY_SCALE_SMALL_MILLIS, |
| ANSWER_HINT_SMALL_ALPHA)) |
| .with( |
| createFadeOutScaleAndAlpha( |
| answerHintMid, |
| R.dimen.hint_mid_begin_size, |
| R.dimen.hint_mid_end_size, |
| FADE_OUT_DELAY_SCALE_MID_MILLIS, |
| ANSWER_HINT_MID_ALPHA)) |
| .with( |
| createFadeOutScaleAndAlpha( |
| answerHintLarge, |
| R.dimen.hint_large_begin_size, |
| R.dimen.hint_large_end_size, |
| FADE_OUT_DELAY_SCALE_LARGE_MILLIS, |
| ANSWER_HINT_LARGE_ALPHA)); |
| return set; |
| } |
| |
| private Animator createFadeOutScaleAndAlpha( |
| View target, |
| @DimenRes int beginSize, |
| @DimenRes int endSize, |
| long scaleDelay, |
| float endAlpha) { |
| Animator scale = |
| createUniformScaleAnimator( |
| target, |
| getDimension(beginSize), |
| getDimension(endSize), |
| getDimension(beginSize), |
| FADE_OUT_DURATION_SCALE_MILLIS, |
| scaleDelay, |
| new LinearInterpolator()); |
| Animator alpha = |
| createAlphaAnimator( |
| target, |
| endAlpha, |
| 0.0f, |
| FADE_OUT_DURATION_ALPHA_MILLIS, |
| FADE_OUT_DELAY_ALPHA_MILLIS, |
| new LinearInterpolator()); |
| AnimatorSet set = new AnimatorSet(); |
| set.play(scale).with(alpha); |
| return set; |
| } |
| |
| @Override |
| public void onBounceEnd() { |
| if (answerGestureHintAnim != null) { |
| answerGestureHintAnim.end(); |
| answerGestureHintAnim = null; |
| answerHintContainer.setVisibility(View.GONE); |
| } |
| } |
| |
| @Override |
| public void onAnswered() { |
| AnswerHintFactory.increaseAnsweredCount(context); |
| } |
| |
| private float getDimension(@DimenRes int id) { |
| return context.getResources().getDimension(id); |
| } |
| |
| private static Animator createUniformScaleAnimator( |
| View target, |
| float original, |
| float begin, |
| float end, |
| long duration, |
| long delay, |
| Interpolator interpolator) { |
| float scaleBegin = begin / original; |
| float scaleEnd = end / original; |
| Animator scaleX = ObjectAnimator.ofFloat(target, View.SCALE_X, scaleBegin, scaleEnd); |
| Animator scaleY = ObjectAnimator.ofFloat(target, View.SCALE_Y, scaleBegin, scaleEnd); |
| scaleX.setDuration(duration); |
| scaleY.setDuration(duration); |
| scaleX.setInterpolator(interpolator); |
| scaleY.setInterpolator(interpolator); |
| AnimatorSet set = new AnimatorSet(); |
| set.play(scaleX).with(scaleY).after(delay); |
| return set; |
| } |
| |
| private static Animator createAlphaAnimator( |
| View target, float begin, float end, long duration, long delay, Interpolator interpolator) { |
| Animator alpha = ObjectAnimator.ofFloat(target, View.ALPHA, begin, end); |
| alpha.setDuration(duration); |
| alpha.setInterpolator(interpolator); |
| alpha.setStartDelay(delay); |
| return alpha; |
| } |
| } |