Home | History | Annotate | Download | only in hint
      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 
     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
     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
     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 
    137   private Animator createFadeIn() {
    138     AnimatorSet set = new AnimatorSet();
    139     set.play(createFadeInScaleAndAlpha(payloadView));
    140     return set;
    141   }
    142 
    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 
    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 
    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
    194   public void onBounceEnd() {
    195     if (answerGestureHintAnim != null) {
    196       answerGestureHintAnim.end();
    197       answerGestureHintAnim = null;
    198       answerHintContainer.setVisibility(View.GONE);
    199     }
    200   }
    201 
    202   @Override
    203   public void onAnswered() {
    204     // Do nothing
    205   }
    206 
    207   private float getDimension(@DimenRes int id) {
    208     return context.getResources().getDimension(id);
    209   }
    210 
    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 
    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