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