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