Home | History | Annotate | Download | only in animation
      1 /*
      2  * Copyright (C) 2015 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.settingslib.animation;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.ValueAnimator;
     23 import android.content.Context;
     24 import android.view.RenderNodeAnimator;
     25 import android.view.View;
     26 import android.view.ViewPropertyAnimator;
     27 import android.view.animation.AnimationUtils;
     28 import android.view.animation.Interpolator;
     29 
     30 import com.android.internal.widget.LockPatternView;
     31 import com.android.settingslib.R;
     32 
     33 /**
     34  * A class to make nice appear transitions for views in a tabular layout.
     35  */
     36 public class AppearAnimationUtils implements AppearAnimationCreator<View> {
     37 
     38     public static final long DEFAULT_APPEAR_DURATION = 220;
     39 
     40     private final Interpolator mInterpolator;
     41     private final float mStartTranslation;
     42     private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
     43     protected final float mDelayScale;
     44     private final long mDuration;
     45     protected RowTranslationScaler mRowTranslationScaler;
     46     protected boolean mAppearing;
     47 
     48     public AppearAnimationUtils(Context ctx) {
     49         this(ctx, DEFAULT_APPEAR_DURATION,
     50                 1.0f, 1.0f,
     51                 AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in));
     52     }
     53 
     54     public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor,
     55             float delayScaleFactor, Interpolator interpolator) {
     56         mInterpolator = interpolator;
     57         mStartTranslation = ctx.getResources().getDimensionPixelOffset(
     58                 R.dimen.appear_y_translation_start) * translationScaleFactor;
     59         mDelayScale = delayScaleFactor;
     60         mDuration = duration;
     61         mAppearing = true;
     62     }
     63 
     64     public void startAnimation2d(View[][] objects, final Runnable finishListener) {
     65         startAnimation2d(objects, finishListener, this);
     66     }
     67 
     68     public void startAnimation(View[] objects, final Runnable finishListener) {
     69         startAnimation(objects, finishListener, this);
     70     }
     71 
     72     public <T> void startAnimation2d(T[][] objects, final Runnable finishListener,
     73             AppearAnimationCreator<T> creator) {
     74         AppearAnimationProperties properties = getDelays(objects);
     75         startAnimations(properties, objects, finishListener, creator);
     76     }
     77 
     78     public <T> void startAnimation(T[] objects, final Runnable finishListener,
     79             AppearAnimationCreator<T> creator) {
     80         AppearAnimationProperties properties = getDelays(objects);
     81         startAnimations(properties, objects, finishListener, creator);
     82     }
     83 
     84     private <T> void startAnimations(AppearAnimationProperties properties, T[] objects,
     85             final Runnable finishListener, AppearAnimationCreator<T> creator) {
     86         if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
     87             finishListener.run();
     88             return;
     89         }
     90         for (int row = 0; row < properties.delays.length; row++) {
     91             long[] columns = properties.delays[row];
     92             long delay = columns[0];
     93             Runnable endRunnable = null;
     94             if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) {
     95                 endRunnable = finishListener;
     96             }
     97             float translationScale = mRowTranslationScaler != null
     98                     ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length)
     99                     : 1f;
    100             float translation = translationScale * mStartTranslation;
    101             creator.createAnimation(objects[row], delay, mDuration,
    102                     mAppearing ? translation : -translation,
    103                     mAppearing, mInterpolator, endRunnable);
    104         }
    105     }
    106 
    107     private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
    108             final Runnable finishListener, AppearAnimationCreator<T> creator) {
    109         if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
    110             finishListener.run();
    111             return;
    112         }
    113         for (int row = 0; row < properties.delays.length; row++) {
    114             long[] columns = properties.delays[row];
    115             float translationScale = mRowTranslationScaler != null
    116                     ? mRowTranslationScaler.getRowTranslationScale(row, properties.delays.length)
    117                     : 1f;
    118             float translation = translationScale * mStartTranslation;
    119             for (int col = 0; col < columns.length; col++) {
    120                 long delay = columns[col];
    121                 Runnable endRunnable = null;
    122                 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
    123                     endRunnable = finishListener;
    124                 }
    125                 creator.createAnimation(objects[row][col], delay, mDuration,
    126                         mAppearing ? translation : -translation,
    127                         mAppearing, mInterpolator, endRunnable);
    128             }
    129         }
    130     }
    131 
    132     private <T> AppearAnimationProperties getDelays(T[] items) {
    133         long maxDelay = -1;
    134         mProperties.maxDelayColIndex = -1;
    135         mProperties.maxDelayRowIndex = -1;
    136         mProperties.delays = new long[items.length][];
    137         for (int row = 0; row < items.length; row++) {
    138             mProperties.delays[row] = new long[1];
    139             long delay = calculateDelay(row, 0);
    140             mProperties.delays[row][0] = delay;
    141             if (items[row] != null && delay > maxDelay) {
    142                 maxDelay = delay;
    143                 mProperties.maxDelayColIndex = 0;
    144                 mProperties.maxDelayRowIndex = row;
    145             }
    146         }
    147         return mProperties;
    148     }
    149 
    150     private <T> AppearAnimationProperties getDelays(T[][] items) {
    151         long maxDelay = -1;
    152         mProperties.maxDelayColIndex = -1;
    153         mProperties.maxDelayRowIndex = -1;
    154         mProperties.delays = new long[items.length][];
    155         for (int row = 0; row < items.length; row++) {
    156             T[] columns = items[row];
    157             mProperties.delays[row] = new long[columns.length];
    158             for (int col = 0; col < columns.length; col++) {
    159                 long delay = calculateDelay(row, col);
    160                 mProperties.delays[row][col] = delay;
    161                 if (items[row][col] != null && delay > maxDelay) {
    162                     maxDelay = delay;
    163                     mProperties.maxDelayColIndex = col;
    164                     mProperties.maxDelayRowIndex = row;
    165                 }
    166             }
    167         }
    168         return mProperties;
    169     }
    170 
    171     protected long calculateDelay(int row, int col) {
    172         return (long) ((row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20) * mDelayScale);
    173     }
    174 
    175     public Interpolator getInterpolator() {
    176         return mInterpolator;
    177     }
    178 
    179     public float getStartTranslation() {
    180         return mStartTranslation;
    181     }
    182 
    183     @Override
    184     public void createAnimation(final View view, long delay, long duration, float translationY,
    185             boolean appearing, Interpolator interpolator, final Runnable endRunnable) {
    186         if (view != null) {
    187             view.setAlpha(appearing ? 0f : 1.0f);
    188             view.setTranslationY(appearing ? translationY : 0);
    189             Animator alphaAnim;
    190             float targetAlpha =  appearing ? 1f : 0f;
    191             if (view.isHardwareAccelerated()) {
    192                 RenderNodeAnimator alphaAnimRt = new RenderNodeAnimator(RenderNodeAnimator.ALPHA,
    193                         targetAlpha);
    194                 alphaAnimRt.setTarget(view);
    195                 alphaAnim = alphaAnimRt;
    196             } else {
    197                 alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), targetAlpha);
    198             }
    199             alphaAnim.setInterpolator(interpolator);
    200             alphaAnim.setDuration(duration);
    201             alphaAnim.setStartDelay(delay);
    202             if (view.hasOverlappingRendering()) {
    203                 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    204                 alphaAnim.addListener(new AnimatorListenerAdapter() {
    205                     @Override
    206                     public void onAnimationEnd(Animator animation) {
    207                         view.setLayerType(View.LAYER_TYPE_NONE, null);
    208                     }
    209                 });
    210             }
    211             if (endRunnable != null) {
    212                 alphaAnim.addListener(new AnimatorListenerAdapter() {
    213                     @Override
    214                     public void onAnimationEnd(Animator animation) {
    215                         endRunnable.run();
    216                     }
    217                 });
    218             }
    219             alphaAnim.start();
    220             startTranslationYAnimation(view, delay, duration, appearing ? 0 : translationY,
    221                     interpolator);
    222         }
    223     }
    224 
    225     public static void startTranslationYAnimation(View view, long delay, long duration,
    226             float endTranslationY, Interpolator interpolator) {
    227         Animator translationAnim;
    228         if (view.isHardwareAccelerated()) {
    229             RenderNodeAnimator translationAnimRt = new RenderNodeAnimator(
    230                     RenderNodeAnimator.TRANSLATION_Y, endTranslationY);
    231             translationAnimRt.setTarget(view);
    232             translationAnim = translationAnimRt;
    233         } else {
    234             translationAnim = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y,
    235                     view.getTranslationY(), endTranslationY);
    236         }
    237         translationAnim.setInterpolator(interpolator);
    238         translationAnim.setDuration(duration);
    239         translationAnim.setStartDelay(delay);
    240         translationAnim.start();
    241     }
    242 
    243     public class AppearAnimationProperties {
    244         public long[][] delays;
    245         public int maxDelayRowIndex;
    246         public int maxDelayColIndex;
    247     }
    248 
    249     public interface RowTranslationScaler {
    250         float getRowTranslationScale(int row, int numRows);
    251     }
    252 }
    253