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