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 AppearAnimationUtils(Context ctx)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 AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, float delayScaleFactor, Interpolator interpolator)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 startAnimation2d(View[][] objects, final Runnable finishListener)64 public void startAnimation2d(View[][] objects, final Runnable finishListener) { 65 startAnimation2d(objects, finishListener, this); 66 } 67 startAnimation(View[] objects, final Runnable finishListener)68 public void startAnimation(View[] objects, final Runnable finishListener) { 69 startAnimation(objects, finishListener, this); 70 } 71 startAnimation2d(T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)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 startAnimation(T[] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)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 startAnimations(AppearAnimationProperties properties, T[] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)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 startAnimations(AppearAnimationProperties properties, T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator)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 getDelays(T[] items)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 getDelays(T[][] items)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 calculateDelay(int row, int col)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 getInterpolator()175 public Interpolator getInterpolator() { 176 return mInterpolator; 177 } 178 getStartTranslation()179 public float getStartTranslation() { 180 return mStartTranslation; 181 } 182 183 @Override createAnimation(final View view, long delay, long duration, float translationY, boolean appearing, Interpolator interpolator, final Runnable endRunnable)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 startTranslationYAnimation(View view, long delay, long duration, float endTranslationY, Interpolator interpolator)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 { getRowTranslationScale(int row, int numRows)250 float getRowTranslationScale(int row, int numRows); 251 } 252 } 253