blob: ce13da66e299db3fcd2c9c6dfc37ced01a550e7d [file] [log] [blame]
Winson Chungb745afb2015-03-02 11:51:23 -08001/*
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
17package com.android.launcher3;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
23import android.animation.PropertyValuesHolder;
24import android.animation.TimeInterpolator;
Sunny Goyal70660032015-05-14 00:07:08 -070025import android.annotation.SuppressLint;
26import android.annotation.TargetApi;
Winson Chungb745afb2015-03-02 11:51:23 -080027import android.content.res.Resources;
Sunny Goyal70660032015-05-14 00:07:08 -070028import android.os.Build;
Winson Chungb745afb2015-03-02 11:51:23 -080029import android.util.Log;
30import android.view.View;
31import android.view.ViewAnimationUtils;
32import android.view.animation.AccelerateInterpolator;
33import android.view.animation.DecelerateInterpolator;
Adam Cohen091440a2015-03-18 14:16:05 -070034import com.android.launcher3.util.Thunk;
Hyunyoung Song3f471442015-04-08 19:01:34 -070035import com.android.launcher3.widget.WidgetsContainerView;
Winson Chungb745afb2015-03-02 11:51:23 -080036import java.util.HashMap;
37
38/**
39 * TODO: figure out what kind of tests we can write for this
40 *
41 * Things to test when changing the following class.
42 * - Home from workspace
43 * - from center screen
44 * - from other screens
45 * - Home from all apps
46 * - from center screen
47 * - from other screens
48 * - Back from all apps
49 * - from center screen
50 * - from other screens
51 * - Launch app from workspace and quit
52 * - with back
53 * - with home
54 * - Launch app from all apps and quit
55 * - with back
56 * - with home
57 * - Go to a screen that's not the default, then all
58 * apps, and launch and app, and go back
59 * - with back
60 * -with home
61 * - On workspace, long press power and go back
62 * - with back
63 * - with home
64 * - On all apps, long press power and go back
65 * - with back
66 * - with home
67 * - On workspace, power off
68 * - On all apps, power off
69 * - Launch an app and turn off the screen while in that app
70 * - Go back with home key
71 * - Go back with back key TODO: make this not go to workspace
72 * - From all apps
73 * - From workspace
74 * - Enter and exit car mode (becuase it causes an extra configuration changed)
75 * - From all apps
76 * - From the center workspace
77 * - From another workspace
78 */
79public class LauncherStateTransitionAnimation {
80
81 /**
82 * Callbacks made during the state transition
83 */
84 interface Callbacks {
85 public void onStateTransitionHideSearchBar();
86 }
87
88 /**
89 * Private callbacks made during transition setup.
90 */
91 static abstract class PrivateTransitionCallbacks {
92 void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
93 void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {}
94 float getMaterialRevealViewFinalAlpha(View revealView) {
95 return 0;
96 }
97 float getMaterialRevealViewFinalXDrift(View revealView) {
98 return 0;
99 }
100 float getMaterialRevealViewFinalYDrift(View revealView) {
101 return 0;
102 }
103 float getMaterialRevealViewStartFinalRadius() {
104 return 0;
105 }
106 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
107 View allAppsButtonView) {
108 return null;
109 }
110 }
111
112 public static final String TAG = "LauncherStateTransitionAnimation";
113
114 // Flags to determine how to set the layers on views before the transition animation
115 public static final int BUILD_LAYER = 0;
116 public static final int BUILD_AND_SET_LAYER = 1;
117 public static final int SINGLE_FRAME_DELAY = 16;
118
Adam Cohen091440a2015-03-18 14:16:05 -0700119 @Thunk Launcher mLauncher;
120 @Thunk Callbacks mCb;
121 @Thunk AnimatorSet mStateAnimation;
Winson Chungb745afb2015-03-02 11:51:23 -0800122
123 public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
124 mLauncher = l;
125 mCb = cb;
126 }
127
128 /**
129 * Starts an animation to the apps view.
130 */
131 public void startAnimationToAllApps(final boolean animated) {
132 final AppsContainerView toView = mLauncher.getAppsView();
133 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
134 private int[] mAllAppsToPanelDelta;
135
136 @Override
137 public void onRevealViewVisible(View revealView, View contentView,
138 View allAppsButtonView) {
139 toView.setBackground(null);
140 // Get the y delta between the center of the page and the center of the all apps
141 // button
142 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
143 allAppsButtonView, null);
144 }
145 @Override
146 public float getMaterialRevealViewFinalAlpha(View revealView) {
147 return 1f;
148 }
149 @Override
150 public float getMaterialRevealViewFinalXDrift(View revealView) {
151 return mAllAppsToPanelDelta[0];
152 }
153 @Override
154 public float getMaterialRevealViewFinalYDrift(View revealView) {
155 return mAllAppsToPanelDelta[1];
156 }
157 @Override
158 public float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700159 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800160 return allAppsButtonSize / 2;
161 }
162 @Override
163 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
164 final View revealView, final View allAppsButtonView) {
165 return new AnimatorListenerAdapter() {
166 public void onAnimationStart(Animator animation) {
167 allAppsButtonView.setVisibility(View.INVISIBLE);
168 }
169 public void onAnimationEnd(Animator animation) {
170 allAppsButtonView.setVisibility(View.VISIBLE);
171 }
172 };
173 }
174 };
175 startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
Winson Chungdc61c4d2015-04-20 18:26:57 -0700176 toView.getRevealView(), animated,
177 !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800178 }
179
180 /**
181 * Starts an animation to the widgets view.
182 */
183 public void startAnimationToWidgets(final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700184 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700185 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800186 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
187 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700188 public void onRevealViewVisible(View revealView, View contentView,
189 View allAppsButtonView) {
190 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
191 }
192 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800193 public float getMaterialRevealViewFinalAlpha(View revealView) {
194 return 0.3f;
195 }
196 @Override
197 public float getMaterialRevealViewFinalYDrift(View revealView) {
198 return revealView.getMeasuredHeight() / 2;
199 }
200 };
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700201 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
202 toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */,
203 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800204 }
205
206 /**
207 * Starts and animation to the workspace from the current overlay view.
208 */
209 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700210 final Workspace.State toWorkspaceState, final int toWorkspacePage,
211 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800212 if (toWorkspaceState != Workspace.State.NORMAL &&
213 toWorkspaceState != Workspace.State.SPRING_LOADED &&
214 toWorkspaceState != Workspace.State.OVERVIEW) {
215 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
216 }
217
218 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chungdc61c4d2015-04-20 18:26:57 -0700219 startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage,
220 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800221 } else {
Winson Chungdc61c4d2015-04-20 18:26:57 -0700222 startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage,
223 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800224 }
225 }
226
227 /**
228 * Creates and starts a new animation to a particular overlay view.
229 */
Sunny Goyal70660032015-05-14 00:07:08 -0700230 @SuppressLint("NewApi")
Winson Chungb745afb2015-03-02 11:51:23 -0800231 private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
Winson Chung0f785722015-04-08 10:27:49 -0700232 final View contentView, final View revealView, final boolean animated,
233 final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800234 final Resources res = mLauncher.getResources();
235 final boolean material = Utilities.isLmpOrAbove();
236 final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
237 final int itemsAlphaStagger =
238 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
239
240 final View allAppsButtonView = mLauncher.getAllAppsButton();
241 final View fromView = mLauncher.getWorkspace();
242
243 final HashMap<View, Integer> layerViews = new HashMap<>();
244
245 // If for some reason our views aren't initialized, don't animate
246 boolean initialized = allAppsButtonView != null;
247
248 // Cancel the current animation
249 cancelAnimation();
250
251 // Create the workspace animation.
252 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700253 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
254 animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800255
256 if (animated && initialized) {
257 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
258
259 // Setup the reveal view animation
260 int width = revealView.getMeasuredWidth();
261 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700262 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800263 revealView.setVisibility(View.VISIBLE);
264 revealView.setAlpha(0f);
265 revealView.setTranslationY(0f);
266 revealView.setTranslationX(0f);
267 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
268
269 // Calculate the final animation values
270 final float revealViewToAlpha;
271 final float revealViewToXDrift;
272 final float revealViewToYDrift;
273 if (material) {
274 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
275 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
276 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
277 } else {
278 revealViewToAlpha = 0f;
279 revealViewToYDrift = 2 * height / 3;
280 revealViewToXDrift = 0;
281 }
282
283 // Create the animators
284 PropertyValuesHolder panelAlpha =
285 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
286 PropertyValuesHolder panelDriftY =
287 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
288 PropertyValuesHolder panelDriftX =
289 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
290 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
291 panelAlpha, panelDriftY, panelDriftX);
292 panelAlphaAndDrift.setDuration(revealDuration);
293 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
294
295 // Play the animation
296 layerViews.put(revealView, BUILD_AND_SET_LAYER);
297 mStateAnimation.play(panelAlphaAndDrift);
298
Winson Chungb745afb2015-03-02 11:51:23 -0800299 // Setup the animation for the content view
300 contentView.setVisibility(View.VISIBLE);
301 contentView.setAlpha(0f);
302 contentView.setTranslationY(revealViewToYDrift);
303 layerViews.put(contentView, BUILD_AND_SET_LAYER);
304
305 // Create the individual animators
306 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
307 revealViewToYDrift, 0);
308 pageDrift.setDuration(revealDuration);
309 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
310 pageDrift.setStartDelay(itemsAlphaStagger);
311 mStateAnimation.play(pageDrift);
312
313 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
314 itemsAlpha.setDuration(revealDuration);
315 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
316 itemsAlpha.setStartDelay(itemsAlphaStagger);
317 mStateAnimation.play(itemsAlpha);
318
319 if (material) {
320 // Animate the all apps button
321 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
322 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
323 revealView, allAppsButtonView);
324 Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
325 height / 2, startRadius, revealRadius);
326 reveal.setDuration(revealDuration);
327 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
328 if (listener != null) {
329 reveal.addListener(listener);
330 }
331 mStateAnimation.play(reveal);
332 }
333
334 mStateAnimation.addListener(new AnimatorListenerAdapter() {
335 @Override
336 public void onAnimationEnd(Animator animation) {
337 dispatchOnLauncherTransitionEnd(fromView, animated, false);
338 dispatchOnLauncherTransitionEnd(toView, animated, false);
339
340 // Hide the reveal view
341 revealView.setVisibility(View.INVISIBLE);
342 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
343
344 // Disable all necessary layers
345 for (View v : layerViews.keySet()) {
346 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
347 v.setLayerType(View.LAYER_TYPE_NONE, null);
348 }
349 }
350
Winson Chung0f785722015-04-08 10:27:49 -0700351 if (hideSearchBar) {
352 mCb.onStateTransitionHideSearchBar();
353 }
Winson Chungb745afb2015-03-02 11:51:23 -0800354
355 // This can hold unnecessary references to views.
356 mStateAnimation = null;
357 }
358
359 });
360
361 // Play the workspace animation
362 if (workspaceAnim != null) {
363 mStateAnimation.play(workspaceAnim);
364 }
365
366 // Dispatch the prepare transition signal
367 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
368 dispatchOnLauncherTransitionPrepare(toView, animated, false);
369
370
371 final AnimatorSet stateAnimation = mStateAnimation;
372 final Runnable startAnimRunnable = new Runnable() {
373 public void run() {
374 // Check that mStateAnimation hasn't changed while
375 // we waited for a layout/draw pass
376 if (mStateAnimation != stateAnimation)
377 return;
378 dispatchOnLauncherTransitionStart(fromView, animated, false);
379 dispatchOnLauncherTransitionStart(toView, animated, false);
380
381 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700382 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800383 for (View v : layerViews.keySet()) {
384 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
385 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
386 }
Winson Chung11509ad2015-05-12 18:55:26 -0700387 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800388 v.buildLayer();
389 }
390 }
391
392 // Focus the new view
393 toView.requestFocus();
394
395 mStateAnimation.start();
396 }
397 };
Winson Chungb745afb2015-03-02 11:51:23 -0800398 toView.bringToFront();
399 toView.setVisibility(View.VISIBLE);
400 toView.post(startAnimRunnable);
401 } else {
402 toView.setTranslationX(0.0f);
403 toView.setTranslationY(0.0f);
404 toView.setScaleX(1.0f);
405 toView.setScaleY(1.0f);
406 toView.setVisibility(View.VISIBLE);
407 toView.bringToFront();
408
409 // Show the content view
410 contentView.setVisibility(View.VISIBLE);
411
Winson Chung0f785722015-04-08 10:27:49 -0700412 if (hideSearchBar) {
413 mCb.onStateTransitionHideSearchBar();
414 }
Winson Chungb745afb2015-03-02 11:51:23 -0800415
416 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
417 dispatchOnLauncherTransitionStart(fromView, animated, false);
418 dispatchOnLauncherTransitionEnd(fromView, animated, false);
419 dispatchOnLauncherTransitionPrepare(toView, animated, false);
420 dispatchOnLauncherTransitionStart(toView, animated, false);
421 dispatchOnLauncherTransitionEnd(toView, animated, false);
422 }
423 }
424
425 /**
426 * Starts and animation to the workspace from the apps view.
427 */
428 private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700429 final Workspace.State toWorkspaceState, final int toWorkspacePage,
430 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800431 AppsContainerView appsView = mLauncher.getAppsView();
432 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
433 int[] mAllAppsToPanelDelta;
434
435 @Override
436 public void onRevealViewVisible(View revealView, View contentView,
437 View allAppsButtonView) {
438 // Get the y delta between the center of the page and the center of the all apps
439 // button
440 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
441 allAppsButtonView, null);
442 }
443 @Override
444 public float getMaterialRevealViewFinalXDrift(View revealView) {
445 return mAllAppsToPanelDelta[0];
446 }
447 @Override
448 public float getMaterialRevealViewFinalYDrift(View revealView) {
449 return mAllAppsToPanelDelta[1];
450 }
451 @Override
452 float getMaterialRevealViewFinalAlpha(View revealView) {
453 // No alpha anim from all apps
454 return 1f;
455 }
456 @Override
457 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700458 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800459 return allAppsButtonSize / 2;
460 }
461 @Override
462 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
463 final View revealView, final View allAppsButtonView) {
464 return new AnimatorListenerAdapter() {
465 public void onAnimationStart(Animator animation) {
466 // We set the alpha instead of visibility to ensure that the focus does not
467 // get taken from the all apps view
468 allAppsButtonView.setVisibility(View.VISIBLE);
469 allAppsButtonView.setAlpha(0f);
470 }
471 public void onAnimationEnd(Animator animation) {
472 // Hide the reveal view
473 revealView.setVisibility(View.INVISIBLE);
474
475 // Show the all apps button, and focus it
476 allAppsButtonView.setAlpha(1f);
477 }
478 };
479 }
480 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700481 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
482 appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable,
483 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800484 }
485
486 /**
487 * Starts and animation to the workspace from the widgets view.
488 */
489 private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700490 final Workspace.State toWorkspaceState, final int toWorkspacePage,
491 final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700492 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
493 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800494 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
495 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700496 public void onRevealViewVisible(View revealView, View contentView,
497 View allAppsButtonView) {
498 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
499 }
500 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800501 public float getMaterialRevealViewFinalYDrift(View revealView) {
502 return revealView.getMeasuredHeight() / 2;
503 }
504 @Override
505 float getMaterialRevealViewFinalAlpha(View revealView) {
506 return 0.4f;
507 }
508 @Override
509 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
510 final View revealView, final View allAppsButtonView) {
511 return new AnimatorListenerAdapter() {
512 public void onAnimationEnd(Animator animation) {
513 // Hide the reveal view
514 revealView.setVisibility(View.INVISIBLE);
515 }
516 };
517 }
518 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700519 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
Winson Chung0f785722015-04-08 10:27:49 -0700520 widgetsView.getContentView(), widgetsView.getRevealView(), animated,
521 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800522 }
523
524 /**
525 * Creates and starts a new animation to the workspace.
526 */
527 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700528 final int toWorkspacePage, final View fromView, final View contentView,
529 final View revealView, final boolean animated, final Runnable onCompleteRunnable,
Winson Chung0f785722015-04-08 10:27:49 -0700530 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800531 final Resources res = mLauncher.getResources();
532 final boolean material = Utilities.isLmpOrAbove();
533 final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
534 final int itemsAlphaStagger =
535 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
536
537 final View allAppsButtonView = mLauncher.getAllAppsButton();
538 final View toView = mLauncher.getWorkspace();
539
540 final HashMap<View, Integer> layerViews = new HashMap<>();
541
542 // If for some reason our views aren't initialized, don't animate
543 boolean initialized = allAppsButtonView != null;
544
545 // Cancel the current animation
546 cancelAnimation();
547
548 // Create the workspace animation.
549 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700550 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
551 toWorkspacePage, animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800552
553 if (animated && initialized) {
554 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
555
556 // Play the workspace animation
557 if (workspaceAnim != null) {
558 mStateAnimation.play(workspaceAnim);
559 }
560
561 // hideAppsCustomizeHelper is called in some cases when it is already hidden
562 // don't perform all these no-op animations. In particularly, this was causing
563 // the all-apps button to pop in and out.
564 if (fromView.getVisibility() == View.VISIBLE) {
565 int width = revealView.getMeasuredWidth();
566 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700567 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800568 revealView.setVisibility(View.VISIBLE);
569 revealView.setAlpha(1f);
570 revealView.setTranslationY(0);
571 layerViews.put(revealView, BUILD_AND_SET_LAYER);
572 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
573
574 // Calculate the final animation values
575 final float revealViewToXDrift;
576 final float revealViewToYDrift;
577 if (material) {
578 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
579 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
580 } else {
581 revealViewToYDrift = 2 * height / 3;
582 revealViewToXDrift = 0;
583 }
584
585 // The vertical motion of the apps panel should be delayed by one frame
586 // from the conceal animation in order to give the right feel. We correspondingly
587 // shorten the duration so that the slide and conceal end at the same time.
588 TimeInterpolator decelerateInterpolator = material ?
589 new LogDecelerateInterpolator(100, 0) :
590 new DecelerateInterpolator(1f);
591 ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
592 0, revealViewToYDrift);
593 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
594 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
595 panelDriftY.setInterpolator(decelerateInterpolator);
596 mStateAnimation.play(panelDriftY);
597
598 ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
599 0, revealViewToXDrift);
600 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
601 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
602 panelDriftX.setInterpolator(decelerateInterpolator);
603 mStateAnimation.play(panelDriftX);
604
605 // Setup animation for the reveal panel alpha
606 final float revealViewToAlpha = !material ? 0f :
607 pCb.getMaterialRevealViewFinalAlpha(revealView);
608 if (revealViewToAlpha != 1f) {
609 ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
610 1f, revealViewToAlpha);
611 panelAlpha.setDuration(material ? revealDuration : 150);
612 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
613 panelAlpha.setInterpolator(decelerateInterpolator);
614 mStateAnimation.play(panelAlpha);
615 }
616
617 // Setup the animation for the content view
618 layerViews.put(contentView, BUILD_AND_SET_LAYER);
619
620 // Create the individual animators
621 ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY",
622 0, revealViewToYDrift);
623 contentView.setTranslationY(0);
624 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
625 pageDrift.setInterpolator(decelerateInterpolator);
626 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
627 mStateAnimation.play(pageDrift);
628
629 contentView.setAlpha(1f);
630 ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f);
631 itemsAlpha.setDuration(100);
632 itemsAlpha.setInterpolator(decelerateInterpolator);
633 mStateAnimation.play(itemsAlpha);
634
Winson Chungb745afb2015-03-02 11:51:23 -0800635 if (material) {
636 // Animate the all apps button
637 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
638 AnimatorListenerAdapter listener =
639 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
640 Animator reveal =
641 LauncherAnimUtils.createCircularReveal(revealView, width / 2,
642 height / 2, revealRadius, finalRadius);
643 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
644 reveal.setDuration(revealDuration);
645 reveal.setStartDelay(itemsAlphaStagger);
646 if (listener != null) {
647 reveal.addListener(listener);
648 }
649 mStateAnimation.play(reveal);
650 }
651
652 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
653 dispatchOnLauncherTransitionPrepare(toView, animated, true);
654 }
655
656 mStateAnimation.addListener(new AnimatorListenerAdapter() {
657 @Override
658 public void onAnimationEnd(Animator animation) {
659 fromView.setVisibility(View.GONE);
660 dispatchOnLauncherTransitionEnd(fromView, animated, true);
661 dispatchOnLauncherTransitionEnd(toView, animated, true);
662
663 // Run any queued runnables
664 if (onCompleteRunnable != null) {
665 onCompleteRunnable.run();
666 }
667
668 // Animation complete callback
669 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
670
671 // Disable all necessary layers
672 for (View v : layerViews.keySet()) {
673 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
674 v.setLayerType(View.LAYER_TYPE_NONE, null);
675 }
676 }
677
678 // Reset page transforms
679 if (contentView != null) {
680 contentView.setTranslationX(0);
681 contentView.setTranslationY(0);
682 contentView.setAlpha(1);
683 }
684
685 // This can hold unnecessary references to views.
686 mStateAnimation = null;
687 }
688 });
689
690 final AnimatorSet stateAnimation = mStateAnimation;
691 final Runnable startAnimRunnable = new Runnable() {
692 public void run() {
693 // Check that mStateAnimation hasn't changed while
694 // we waited for a layout/draw pass
695 if (mStateAnimation != stateAnimation)
696 return;
697 dispatchOnLauncherTransitionStart(fromView, animated, false);
698 dispatchOnLauncherTransitionStart(toView, animated, false);
699
700 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700701 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800702 for (View v : layerViews.keySet()) {
703 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
704 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
705 }
Winson Chung11509ad2015-05-12 18:55:26 -0700706 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800707 v.buildLayer();
708 }
709 }
710 mStateAnimation.start();
711 }
712 };
713 fromView.post(startAnimRunnable);
714 } else {
715 fromView.setVisibility(View.GONE);
716 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
717 dispatchOnLauncherTransitionStart(fromView, animated, true);
718 dispatchOnLauncherTransitionEnd(fromView, animated, true);
719 dispatchOnLauncherTransitionPrepare(toView, animated, true);
720 dispatchOnLauncherTransitionStart(toView, animated, true);
721 dispatchOnLauncherTransitionEnd(toView, animated, true);
722
723 // Run any queued runnables
724 if (onCompleteRunnable != null) {
725 onCompleteRunnable.run();
726 }
727 }
728 }
729
730
731 /**
732 * Dispatches the prepare-transition event to suitable views.
733 */
734 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
735 if (v instanceof LauncherTransitionable) {
736 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
737 toWorkspace);
738 }
739 }
740
741 /**
742 * Dispatches the start-transition event to suitable views.
743 */
744 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
745 if (v instanceof LauncherTransitionable) {
746 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
747 toWorkspace);
748 }
749
750 // Update the workspace transition step as well
751 dispatchOnLauncherTransitionStep(v, 0f);
752 }
753
754 /**
755 * Dispatches the step-transition event to suitable views.
756 */
757 void dispatchOnLauncherTransitionStep(View v, float t) {
758 if (v instanceof LauncherTransitionable) {
759 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
760 }
761 }
762
763 /**
764 * Dispatches the end-transition event to suitable views.
765 */
766 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
767 if (v instanceof LauncherTransitionable) {
768 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
769 toWorkspace);
770 }
771
772 // Update the workspace transition step as well
773 dispatchOnLauncherTransitionStep(v, 1f);
774 }
775
776 /**
777 * Cancels the current animation.
778 */
779 private void cancelAnimation() {
780 if (mStateAnimation != null) {
781 mStateAnimation.setDuration(0);
782 mStateAnimation.cancel();
783 mStateAnimation = null;
784 }
785 }
786}