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