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