Home | History | Annotate | Download | only in incallui
      1 /*
      2  * Copyright (C) 2012 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;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ObjectAnimator;
     22 import android.graphics.Canvas;
     23 import android.graphics.drawable.BitmapDrawable;
     24 import android.graphics.drawable.Drawable;
     25 import android.graphics.drawable.LayerDrawable;
     26 import android.view.ViewPropertyAnimator;
     27 import android.widget.ImageView;
     28 
     29 /**
     30  * Utilities for Animation.
     31  */
     32 public class InCallAnimationUtils {
     33     private static final String LOG_TAG = InCallAnimationUtils.class.getSimpleName();
     34     /**
     35      * Turn on when you're interested in fading animation. Intentionally untied from other debug
     36      * settings.
     37      */
     38     private static final boolean FADE_DBG = false;
     39 
     40     /**
     41      * Duration for animations in msec, which can be used with
     42      * {@link ViewPropertyAnimator#setDuration(long)} for example.
     43      */
     44     public static final int ANIMATION_DURATION = 250;
     45 
     46     private InCallAnimationUtils() {
     47     }
     48 
     49     /**
     50      * Drawable achieving cross-fade, just like TransitionDrawable. We can have
     51      * call-backs via animator object (see also {@link CrossFadeDrawable#getAnimator()}).
     52      */
     53     private static class CrossFadeDrawable extends LayerDrawable {
     54         private final ObjectAnimator mAnimator;
     55 
     56         public CrossFadeDrawable(Drawable[] layers) {
     57             super(layers);
     58             mAnimator = ObjectAnimator.ofInt(this, "crossFadeAlpha", 0xff, 0);
     59         }
     60 
     61         private int mCrossFadeAlpha;
     62 
     63         /**
     64          * This will be used from ObjectAnimator.
     65          * Note: this method is protected by proguard.flags so that it won't be removed
     66          * automatically.
     67          */
     68         @SuppressWarnings("unused")
     69         public void setCrossFadeAlpha(int alpha) {
     70             mCrossFadeAlpha = alpha;
     71             invalidateSelf();
     72         }
     73 
     74         public ObjectAnimator getAnimator() {
     75             return mAnimator;
     76         }
     77 
     78         @Override
     79         public void draw(Canvas canvas) {
     80             Drawable first = getDrawable(0);
     81             Drawable second = getDrawable(1);
     82 
     83             if (mCrossFadeAlpha > 0) {
     84                 first.setAlpha(mCrossFadeAlpha);
     85                 first.draw(canvas);
     86                 first.setAlpha(255);
     87             }
     88 
     89             if (mCrossFadeAlpha < 0xff) {
     90                 second.setAlpha(0xff - mCrossFadeAlpha);
     91                 second.draw(canvas);
     92                 second.setAlpha(0xff);
     93             }
     94         }
     95     }
     96 
     97     private static CrossFadeDrawable newCrossFadeDrawable(Drawable first, Drawable second) {
     98         Drawable[] layers = new Drawable[2];
     99         layers[0] = first;
    100         layers[1] = second;
    101         return new CrossFadeDrawable(layers);
    102     }
    103 
    104     /**
    105      * Starts cross-fade animation using TransitionDrawable. Nothing will happen if "from" and "to"
    106      * are the same.
    107      */
    108     public static void startCrossFade(
    109             final ImageView imageView, final Drawable from, final Drawable to) {
    110         // We skip the cross-fade when those two Drawables are equal, or they are BitmapDrawables
    111         // pointing to the same Bitmap.
    112         final boolean drawableIsEqual = (from != null && to != null && from.equals(to));
    113         final boolean hasFromImage = ((from instanceof BitmapDrawable) &&
    114                 ((BitmapDrawable) from).getBitmap() != null);
    115         final boolean hasToImage = ((to instanceof BitmapDrawable) &&
    116                 ((BitmapDrawable) to).getBitmap() != null);
    117         final boolean areSameImage = drawableIsEqual || (hasFromImage && hasToImage &&
    118                 ((BitmapDrawable) from).getBitmap().equals(((BitmapDrawable) to).getBitmap()));
    119 
    120         if (!areSameImage) {
    121             if (FADE_DBG) {
    122                 log("Start cross-fade animation for " + imageView
    123                         + "(" + Integer.toHexString(from.hashCode()) + " -> "
    124                         + Integer.toHexString(to.hashCode()) + ")");
    125             }
    126 
    127             CrossFadeDrawable crossFadeDrawable = newCrossFadeDrawable(from, to);
    128             ObjectAnimator animator = crossFadeDrawable.getAnimator();
    129             imageView.setImageDrawable(crossFadeDrawable);
    130             animator.setDuration(ANIMATION_DURATION);
    131             animator.addListener(new AnimatorListenerAdapter() {
    132                 @Override
    133                 public void onAnimationStart(Animator animation) {
    134                     if (FADE_DBG) {
    135                         log("cross-fade animation start ("
    136                                 + Integer.toHexString(from.hashCode()) + " -> "
    137                                 + Integer.toHexString(to.hashCode()) + ")");
    138                     }
    139                 }
    140 
    141                 @Override
    142                 public void onAnimationEnd(Animator animation) {
    143                     if (FADE_DBG) {
    144                         log("cross-fade animation ended ("
    145                                 + Integer.toHexString(from.hashCode()) + " -> "
    146                                 + Integer.toHexString(to.hashCode()) + ")");
    147                     }
    148                     animation.removeAllListeners();
    149                     // Workaround for issue 6300562; this will force the drawable to the
    150                     // resultant one regardless of animation glitch.
    151                     imageView.setImageDrawable(to);
    152                 }
    153             });
    154             animator.start();
    155 
    156             /* We could use TransitionDrawable here, but it may cause some weird animation in
    157              * some corner cases. See issue 6300562
    158              * TODO: decide which to be used in the long run. TransitionDrawable is old but system
    159              * one. Ours uses new animation framework and thus have callback (great for testing),
    160              * while no framework support for the exact class.
    161 
    162             Drawable[] layers = new Drawable[2];
    163             layers[0] = from;
    164             layers[1] = to;
    165             TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
    166             imageView.setImageDrawable(transitionDrawable);
    167             transitionDrawable.startTransition(ANIMATION_DURATION); */
    168             imageView.setTag(to);
    169         } else if (!hasFromImage && hasToImage) {
    170             imageView.setImageDrawable(to);
    171             imageView.setTag(to);
    172         } else {
    173             if (FADE_DBG) {
    174                 log("*Not* start cross-fade. " + imageView);
    175             }
    176         }
    177     }
    178 
    179     // Debugging / testing code
    180 
    181     private static void log(String msg) {
    182         Log.d(LOG_TAG, msg);
    183     }
    184 }