blob: 01d31e2a9852124254645a8da49da44b1776ce03 [file] [log] [blame]
John Spurlock22def3d2015-06-17 11:56:12 -04001/*
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 */
Jason Monk98ad83c2017-06-13 14:03:45 -040016
John Spurlock22def3d2015-06-17 11:56:12 -040017package com.android.systemui.volume;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.TimeInterpolator;
22import android.animation.ValueAnimator;
23import android.animation.ValueAnimator.AnimatorUpdateListener;
24import android.app.Dialog;
25import android.content.DialogInterface;
26import android.content.DialogInterface.OnDismissListener;
27import android.content.DialogInterface.OnShowListener;
Lucas Dupinc1cc7592017-05-22 15:56:16 -070028import android.graphics.drawable.Drawable;
John Spurlock22def3d2015-06-17 11:56:12 -040029import android.os.Handler;
30import android.util.Log;
31import android.view.View;
32import android.view.ViewGroup;
33import android.view.animation.PathInterpolator;
34
35public class VolumeDialogMotion {
36 private static final String TAG = Util.logTag(VolumeDialogMotion.class);
37
38 private static final float ANIMATION_SCALE = 1.0f;
39 private static final int PRE_DISMISS_DELAY = 50;
John Spurlock22def3d2015-06-17 11:56:12 -040040
41 private final Dialog mDialog;
42 private final View mDialogView;
43 private final ViewGroup mContents; // volume rows + zen footer
44 private final View mChevron;
45 private final Handler mHandler = new Handler();
46 private final Callback mCallback;
47
48 private boolean mAnimating; // show or dismiss animation is running
49 private boolean mShowing; // show animation is running
50 private boolean mDismissing; // dismiss animation is running
51 private ValueAnimator mChevronPositionAnimator;
52 private ValueAnimator mContentsPositionAnimator;
53
54 public VolumeDialogMotion(Dialog dialog, View dialogView, ViewGroup contents, View chevron,
Jason Monk98ad83c2017-06-13 14:03:45 -040055 Callback callback) {
John Spurlock22def3d2015-06-17 11:56:12 -040056 mDialog = dialog;
57 mDialogView = dialogView;
58 mContents = contents;
59 mChevron = chevron;
60 mCallback = callback;
61 mDialog.setOnDismissListener(new OnDismissListener() {
62 @Override
63 public void onDismiss(DialogInterface dialog) {
64 if (D.BUG) Log.d(TAG, "mDialog.onDismiss");
65 }
66 });
67 mDialog.setOnShowListener(new OnShowListener() {
68 @Override
69 public void onShow(DialogInterface dialog) {
70 if (D.BUG) Log.d(TAG, "mDialog.onShow");
Jason Monk98ad83c2017-06-13 14:03:45 -040071 final int h = mDialogView.getHeight();
72 mDialogView.setTranslationY(-h);
Jorim Jaggi6d712e02015-07-06 16:18:11 -070073 startShowAnimation();
John Spurlock22def3d2015-06-17 11:56:12 -040074 }
75 });
76 }
77
78 public boolean isAnimating() {
79 return mAnimating;
80 }
81
82 private void setShowing(boolean showing) {
83 if (showing == mShowing) return;
84 mShowing = showing;
85 if (D.BUG) Log.d(TAG, "mShowing = " + mShowing);
86 updateAnimating();
87 }
88
89 private void setDismissing(boolean dismissing) {
90 if (dismissing == mDismissing) return;
91 mDismissing = dismissing;
92 if (D.BUG) Log.d(TAG, "mDismissing = " + mDismissing);
93 updateAnimating();
94 }
95
96 private void updateAnimating() {
97 final boolean animating = mShowing || mDismissing;
98 if (animating == mAnimating) return;
99 mAnimating = animating;
100 if (D.BUG) Log.d(TAG, "mAnimating = " + mAnimating);
101 if (mCallback != null) {
102 mCallback.onAnimatingChanged(mAnimating);
103 }
104 }
105
106 public void startShow() {
107 if (D.BUG) Log.d(TAG, "startShow");
108 if (mShowing) return;
109 setShowing(true);
110 if (mDismissing) {
111 mDialogView.animate().cancel();
112 setDismissing(false);
113 startShowAnimation();
114 return;
115 }
116 if (D.BUG) Log.d(TAG, "mDialog.show()");
117 mDialog.show();
118 }
119
120 private int chevronDistance() {
Jason Monk98ad83c2017-06-13 14:03:45 -0400121 return mChevron.getHeight() / 6;
John Spurlock22def3d2015-06-17 11:56:12 -0400122 }
123
John Spurlock1a830f02015-06-30 15:14:27 -0400124 private int chevronPosY() {
125 final Object tag = mChevron == null ? null : mChevron.getTag();
126 return tag == null ? 0 : (Integer) tag;
127 }
128
John Spurlock22def3d2015-06-17 11:56:12 -0400129 private void startShowAnimation() {
130 if (D.BUG) Log.d(TAG, "startShowAnimation");
131 mDialogView.animate()
132 .translationY(0)
133 .setDuration(scaledDuration(300))
134 .setInterpolator(new LogDecelerateInterpolator())
135 .setListener(null)
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700136 .setUpdateListener(animation -> {
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700137 if (mChevronPositionAnimator != null) {
138 final float v = (Float) mChevronPositionAnimator.getAnimatedValue();
John Spurlock22def3d2015-06-17 11:56:12 -0400139 if (mChevronPositionAnimator == null) return;
140 // reposition chevron
John Spurlock1a830f02015-06-30 15:14:27 -0400141 final int posY = chevronPosY();
Jason Monk98ad83c2017-06-13 14:03:45 -0400142 mChevron.setTranslationY(posY + v + -mDialogView.getTranslationY());
Julia Reynolds027c7492017-02-15 15:41:14 -0500143 }
144 })
145 .withEndAction(new Runnable() {
146 @Override
147 public void run() {
148 if (mChevronPositionAnimator == null) return;
149 // reposition chevron
150 final int posY = chevronPosY();
Jason Monk98ad83c2017-06-13 14:03:45 -0400151 mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
Julia Reynolds027c7492017-02-15 15:41:14 -0500152 }
153 })
John Spurlock22def3d2015-06-17 11:56:12 -0400154 .start();
155
156 mContentsPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
157 .setDuration(scaledDuration(400));
158 mContentsPositionAnimator.addListener(new AnimatorListenerAdapter() {
159 private boolean mCancelled;
160
161 @Override
162 public void onAnimationEnd(Animator animation) {
163 if (mCancelled) return;
164 if (D.BUG) Log.d(TAG, "show.onAnimationEnd");
165 setShowing(false);
166 }
167 @Override
168 public void onAnimationCancel(Animator animation) {
169 if (D.BUG) Log.d(TAG, "show.onAnimationCancel");
170 mCancelled = true;
171 }
172 });
Jason Monk98ad83c2017-06-13 14:03:45 -0400173 mContentsPositionAnimator.addUpdateListener(new AnimatorUpdateListener() {
174 @Override
175 public void onAnimationUpdate(ValueAnimator animation) {
176 float v = (Float) animation.getAnimatedValue();
177 mContents.setTranslationY(v + -mDialogView.getTranslationY());
178 }
179 });
John Spurlock22def3d2015-06-17 11:56:12 -0400180 mContentsPositionAnimator.setInterpolator(new LogDecelerateInterpolator());
181 mContentsPositionAnimator.start();
182
183 mContents.setAlpha(0);
184 mContents.animate()
185 .alpha(1)
186 .setDuration(scaledDuration(150))
187 .setInterpolator(new PathInterpolator(0f, 0f, .2f, 1f))
188 .start();
189
190 mChevronPositionAnimator = ValueAnimator.ofFloat(-chevronDistance(), 0)
191 .setDuration(scaledDuration(250));
192 mChevronPositionAnimator.setInterpolator(new PathInterpolator(.4f, 0f, .2f, 1f));
193 mChevronPositionAnimator.start();
194
195 mChevron.setAlpha(0);
196 mChevron.animate()
197 .alpha(1)
198 .setStartDelay(scaledDuration(50))
199 .setDuration(scaledDuration(150))
200 .setInterpolator(new PathInterpolator(.4f, 0f, 1f, 1f))
201 .start();
202 }
203
204 public void startDismiss(final Runnable onComplete) {
205 if (D.BUG) Log.d(TAG, "startDismiss");
206 if (mDismissing) return;
207 setDismissing(true);
208 if (mShowing) {
209 mDialogView.animate().cancel();
John Spurlockdc005a82015-06-29 11:42:28 -0400210 if (mContentsPositionAnimator != null) {
211 mContentsPositionAnimator.cancel();
212 }
John Spurlock22def3d2015-06-17 11:56:12 -0400213 mContents.animate().cancel();
John Spurlockdc005a82015-06-29 11:42:28 -0400214 if (mChevronPositionAnimator != null) {
215 mChevronPositionAnimator.cancel();
216 }
John Spurlock22def3d2015-06-17 11:56:12 -0400217 mChevron.animate().cancel();
218 setShowing(false);
219 }
220 mDialogView.animate()
Jason Monk98ad83c2017-06-13 14:03:45 -0400221 .translationY(-mDialogView.getHeight())
John Spurlock22def3d2015-06-17 11:56:12 -0400222 .setDuration(scaledDuration(250))
223 .setInterpolator(new LogAccelerateInterpolator())
Jason Monk98ad83c2017-06-13 14:03:45 -0400224 .setUpdateListener(new AnimatorUpdateListener() {
225 @Override
226 public void onAnimationUpdate(ValueAnimator animation) {
227 mContents.setTranslationY(-mDialogView.getTranslationY());
228 final int posY = chevronPosY();
229 mChevron.setTranslationY(posY + -mDialogView.getTranslationY());
230 }
Lucas Dupinc1cc7592017-05-22 15:56:16 -0700231 })
John Spurlock22def3d2015-06-17 11:56:12 -0400232 .setListener(new AnimatorListenerAdapter() {
233 private boolean mCancelled;
234 @Override
235 public void onAnimationEnd(Animator animation) {
236 if (mCancelled) return;
237 if (D.BUG) Log.d(TAG, "dismiss.onAnimationEnd");
Jason Monk98ad83c2017-06-13 14:03:45 -0400238 mHandler.postDelayed(new Runnable() {
239 @Override
240 public void run() {
241 if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
242 mDialog.dismiss();
243 onComplete.run();
244 setDismissing(false);
245 }
John Spurlock22def3d2015-06-17 11:56:12 -0400246 }, PRE_DISMISS_DELAY);
247
248 }
249 @Override
250 public void onAnimationCancel(Animator animation) {
251 if (D.BUG) Log.d(TAG, "dismiss.onAnimationCancel");
252 mCancelled = true;
253 }
254 }).start();
255 }
256
257 private static int scaledDuration(int base) {
258 return (int) (base * ANIMATION_SCALE);
259 }
260
Jason Monk16fbd9d2017-04-27 14:28:49 -0400261 public static final class LogDecelerateInterpolator implements TimeInterpolator {
John Spurlock22def3d2015-06-17 11:56:12 -0400262 private final float mBase;
263 private final float mDrift;
264 private final float mTimeScale;
265 private final float mOutputScale;
266
Jason Monk16fbd9d2017-04-27 14:28:49 -0400267 public LogDecelerateInterpolator() {
John Spurlock22def3d2015-06-17 11:56:12 -0400268 this(400f, 1.4f, 0);
269 }
270
271 private LogDecelerateInterpolator(float base, float timeScale, float drift) {
272 mBase = base;
273 mDrift = drift;
274 mTimeScale = 1f / timeScale;
275
276 mOutputScale = 1f / computeLog(1f);
277 }
278
279 private float computeLog(float t) {
280 return 1f - (float) Math.pow(mBase, -t * mTimeScale) + (mDrift * t);
281 }
282
283 @Override
284 public float getInterpolation(float t) {
285 return computeLog(t) * mOutputScale;
286 }
287 }
288
Jason Monk16fbd9d2017-04-27 14:28:49 -0400289 public static final class LogAccelerateInterpolator implements TimeInterpolator {
John Spurlock22def3d2015-06-17 11:56:12 -0400290 private final int mBase;
291 private final int mDrift;
292 private final float mLogScale;
293
Jason Monk16fbd9d2017-04-27 14:28:49 -0400294 public LogAccelerateInterpolator() {
John Spurlock22def3d2015-06-17 11:56:12 -0400295 this(100, 0);
296 }
297
298 private LogAccelerateInterpolator(int base, int drift) {
299 mBase = base;
300 mDrift = drift;
301 mLogScale = 1f / computeLog(1, mBase, mDrift);
302 }
303
304 private static float computeLog(float t, int base, int drift) {
305 return (float) -Math.pow(base, -t) + 1 + (drift * t);
306 }
307
308 @Override
309 public float getInterpolation(float t) {
310 return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
311 }
312 }
313
314 public interface Callback {
315 void onAnimatingChanged(boolean animating);
316 }
317}