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