| /* |
| * 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.graphics.drawable.Drawable; |
| import android.support.annotation.DimenRes; |
| import android.support.annotation.NonNull; |
| 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.ImageView; |
| import android.widget.TextView; |
| import com.android.dialer.common.Assert; |
| |
| /** |
| * An Answer hint that animates a {@link Drawable} payload with animation similar to {@link |
| * DotAnswerHint}. |
| */ |
| public final class PawAnswerHint implements AnswerHint { |
| |
| 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_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 static final float IMAGE_SCALE = 1.5f; |
| private static final float FADE_SCALE = 2.0f; |
| |
| private final Context context; |
| private final Drawable payload; |
| private final long puckUpDurationMillis; |
| private final long puckUpDelayMillis; |
| |
| private View puck; |
| private View payloadView; |
| private View answerHintContainer; |
| private AnimatorSet answerGestureHintAnim; |
| |
| public PawAnswerHint( |
| @NonNull Context context, |
| @NonNull Drawable payload, |
| long puckUpDurationMillis, |
| long puckUpDelayMillis) { |
| this.context = Assert.isNotNull(context); |
| this.payload = Assert.isNotNull(payload); |
| 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.paw_hint, container, true); |
| answerHintContainer = view.findViewById(R.id.answer_hint_container); |
| payloadView = view.findViewById(R.id.paw_image); |
| hintText.setTextSize( |
| TypedValue.COMPLEX_UNIT_PX, context.getResources().getDimension(R.dimen.hint_text_size)); |
| ((ImageView) payloadView).setImageDrawable(payload); |
| } |
| |
| @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); |
| payloadView.setAlpha(0); |
| payloadView.setScaleX(1); |
| payloadView.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(payloadView)); |
| return set; |
| } |
| |
| private static Animator createFadeInScaleAndAlpha(View target) { |
| Animator scale = |
| createUniformScaleAnimator( |
| target, |
| FADE_SCALE, |
| IMAGE_SCALE, |
| FADE_IN_DURATION_SCALE_MILLIS, |
| FADE_IN_DELAY_SCALE_MILLIS, |
| new LinearInterpolator()); |
| Animator alpha = |
| createAlphaAnimator( |
| target, |
| 0f, |
| 1.0f, |
| 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(payloadView, FADE_OUT_DELAY_SCALE_SMALL_MILLIS)); |
| return set; |
| } |
| |
| private static Animator createFadeOutScaleAndAlpha(View target, long scaleDelay) { |
| Animator scale = |
| createUniformScaleAnimator( |
| target, |
| IMAGE_SCALE, |
| FADE_SCALE, |
| FADE_OUT_DURATION_SCALE_MILLIS, |
| scaleDelay, |
| new LinearInterpolator()); |
| Animator alpha = |
| createAlphaAnimator( |
| target, |
| 1.0f, |
| 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() { |
| // Do nothing |
| } |
| |
| private float getDimension(@DimenRes int id) { |
| return context.getResources().getDimension(id); |
| } |
| |
| private static Animator createUniformScaleAnimator( |
| View target, |
| float scaleBegin, |
| float scaleEnd, |
| long duration, |
| long delay, |
| Interpolator interpolator) { |
| 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; |
| } |
| } |