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