blob: f373fde2dda9c85b3bd7b405df26cde90d60164a [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;
Sunny Goyal70660032015-05-14 00:07:08 -070025import android.annotation.SuppressLint;
Winson Chungb745afb2015-03-02 11:51:23 -080026import android.content.res.Resources;
27import android.util.Log;
28import android.view.View;
29import android.view.ViewAnimationUtils;
30import android.view.animation.AccelerateInterpolator;
31import android.view.animation.DecelerateInterpolator;
Winson Chung5f4e0fd2015-05-22 11:12:27 -070032import com.android.launcher3.allapps.AllAppsContainerView;
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;
Winson Chungb745afb2015-03-02 11:51:23 -080035import java.util.HashMap;
36
37/**
38 * TODO: figure out what kind of tests we can write for this
39 *
40 * Things to test when changing the following class.
41 * - Home from workspace
42 * - from center screen
43 * - from other screens
44 * - Home from all apps
45 * - from center screen
46 * - from other screens
47 * - Back from all apps
48 * - from center screen
49 * - from other screens
50 * - Launch app from workspace and quit
51 * - with back
52 * - with home
53 * - Launch app from all apps and quit
54 * - with back
55 * - with home
56 * - Go to a screen that's not the default, then all
57 * apps, and launch and app, and go back
58 * - with back
59 * -with home
60 * - On workspace, long press power and go back
61 * - with back
62 * - with home
63 * - On all apps, long press power and go back
64 * - with back
65 * - with home
66 * - On workspace, power off
67 * - On all apps, power off
68 * - Launch an app and turn off the screen while in that app
69 * - Go back with home key
70 * - Go back with back key TODO: make this not go to workspace
71 * - From all apps
72 * - From workspace
73 * - Enter and exit car mode (becuase it causes an extra configuration changed)
74 * - From all apps
75 * - From the center workspace
76 * - From another workspace
77 */
78public class LauncherStateTransitionAnimation {
79
80 /**
81 * Callbacks made during the state transition
82 */
83 interface Callbacks {
84 public void onStateTransitionHideSearchBar();
85 }
86
87 /**
88 * Private callbacks made during transition setup.
89 */
90 static abstract class PrivateTransitionCallbacks {
91 void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
92 void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {}
93 float getMaterialRevealViewFinalAlpha(View revealView) {
94 return 0;
95 }
96 float getMaterialRevealViewFinalXDrift(View revealView) {
97 return 0;
98 }
99 float getMaterialRevealViewFinalYDrift(View revealView) {
100 return 0;
101 }
102 float getMaterialRevealViewStartFinalRadius() {
103 return 0;
104 }
105 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
106 View allAppsButtonView) {
107 return null;
108 }
109 }
110
111 public static final String TAG = "LauncherStateTransitionAnimation";
112
113 // Flags to determine how to set the layers on views before the transition animation
114 public static final int BUILD_LAYER = 0;
115 public static final int BUILD_AND_SET_LAYER = 1;
116 public static final int SINGLE_FRAME_DELAY = 16;
117
Adam Cohen091440a2015-03-18 14:16:05 -0700118 @Thunk Launcher mLauncher;
119 @Thunk Callbacks mCb;
120 @Thunk AnimatorSet mStateAnimation;
Winson Chungb745afb2015-03-02 11:51:23 -0800121
122 public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
123 mLauncher = l;
124 mCb = cb;
125 }
126
127 /**
128 * Starts an animation to the apps view.
129 */
130 public void startAnimationToAllApps(final boolean animated) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700131 final AllAppsContainerView toView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800132 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
133 private int[] mAllAppsToPanelDelta;
134
135 @Override
136 public void onRevealViewVisible(View revealView, View contentView,
137 View allAppsButtonView) {
138 toView.setBackground(null);
139 // Get the y delta between the center of the page and the center of the all apps
140 // button
141 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
142 allAppsButtonView, null);
143 }
144 @Override
145 public float getMaterialRevealViewFinalAlpha(View revealView) {
146 return 1f;
147 }
148 @Override
149 public float getMaterialRevealViewFinalXDrift(View revealView) {
150 return mAllAppsToPanelDelta[0];
151 }
152 @Override
153 public float getMaterialRevealViewFinalYDrift(View revealView) {
154 return mAllAppsToPanelDelta[1];
155 }
156 @Override
157 public float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700158 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800159 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(),
Winson Chungdc61c4d2015-04-20 18:26:57 -0700175 toView.getRevealView(), animated,
176 !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800177 }
178
179 /**
180 * Starts an animation to the widgets view.
181 */
182 public void startAnimationToWidgets(final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700183 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700184 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800185 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
186 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700187 public void onRevealViewVisible(View revealView, View contentView,
188 View allAppsButtonView) {
189 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
190 }
191 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800192 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 };
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700200 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
201 toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */,
202 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800203 }
204
205 /**
206 * Starts and animation to the workspace from the current overlay view.
207 */
208 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700209 final Workspace.State toWorkspaceState, final int toWorkspacePage,
210 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800211 if (toWorkspaceState != Workspace.State.NORMAL &&
212 toWorkspaceState != Workspace.State.SPRING_LOADED &&
213 toWorkspaceState != Workspace.State.OVERVIEW) {
214 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
215 }
216
217 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chungdc61c4d2015-04-20 18:26:57 -0700218 startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage,
219 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800220 } else {
Winson Chungdc61c4d2015-04-20 18:26:57 -0700221 startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage,
222 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800223 }
224 }
225
226 /**
227 * Creates and starts a new animation to a particular overlay view.
228 */
Sunny Goyal70660032015-05-14 00:07:08 -0700229 @SuppressLint("NewApi")
Winson Chungb745afb2015-03-02 11:51:23 -0800230 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();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700235 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800236 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700237 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800238
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
Winson Chungcd99cd32015-04-29 11:03:24 -0700252 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
253 animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800254
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();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700261 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800262 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
Winson Chung11509ad2015-05-12 18:55:26 -0700381 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800382 for (View v : layerViews.keySet()) {
383 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
384 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
385 }
Winson Chung11509ad2015-05-12 18:55:26 -0700386 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800387 v.buildLayer();
388 }
389 }
390
391 // Focus the new view
392 toView.requestFocus();
393
394 mStateAnimation.start();
395 }
396 };
Winson Chungb745afb2015-03-02 11:51:23 -0800397 toView.bringToFront();
398 toView.setVisibility(View.VISIBLE);
399 toView.post(startAnimRunnable);
400 } else {
401 toView.setTranslationX(0.0f);
402 toView.setTranslationY(0.0f);
403 toView.setScaleX(1.0f);
404 toView.setScaleY(1.0f);
405 toView.setVisibility(View.VISIBLE);
406 toView.bringToFront();
407
408 // Show the content view
409 contentView.setVisibility(View.VISIBLE);
410
Winson Chung0f785722015-04-08 10:27:49 -0700411 if (hideSearchBar) {
412 mCb.onStateTransitionHideSearchBar();
413 }
Winson Chungb745afb2015-03-02 11:51:23 -0800414
415 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
416 dispatchOnLauncherTransitionStart(fromView, animated, false);
417 dispatchOnLauncherTransitionEnd(fromView, animated, false);
418 dispatchOnLauncherTransitionPrepare(toView, animated, false);
419 dispatchOnLauncherTransitionStart(toView, animated, false);
420 dispatchOnLauncherTransitionEnd(toView, animated, false);
421 }
422 }
423
424 /**
425 * Starts and animation to the workspace from the apps view.
426 */
427 private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700428 final Workspace.State toWorkspaceState, final int toWorkspacePage,
429 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700430 AllAppsContainerView appsView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800431 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
432 int[] mAllAppsToPanelDelta;
433
434 @Override
435 public void onRevealViewVisible(View revealView, View contentView,
436 View allAppsButtonView) {
437 // Get the y delta between the center of the page and the center of the all apps
438 // button
439 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
440 allAppsButtonView, null);
441 }
442 @Override
443 public float getMaterialRevealViewFinalXDrift(View revealView) {
444 return mAllAppsToPanelDelta[0];
445 }
446 @Override
447 public float getMaterialRevealViewFinalYDrift(View revealView) {
448 return mAllAppsToPanelDelta[1];
449 }
450 @Override
451 float getMaterialRevealViewFinalAlpha(View revealView) {
452 // No alpha anim from all apps
453 return 1f;
454 }
455 @Override
456 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700457 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800458 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 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700480 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
481 appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable,
482 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800483 }
484
485 /**
486 * Starts and animation to the workspace from the widgets view.
487 */
488 private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700489 final Workspace.State toWorkspaceState, final int toWorkspacePage,
490 final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700491 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
492 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800493 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
494 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700495 public void onRevealViewVisible(View revealView, View contentView,
496 View allAppsButtonView) {
497 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
498 }
499 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800500 public float getMaterialRevealViewFinalYDrift(View revealView) {
501 return revealView.getMeasuredHeight() / 2;
502 }
503 @Override
504 float getMaterialRevealViewFinalAlpha(View revealView) {
505 return 0.4f;
506 }
507 @Override
508 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
509 final View revealView, final View allAppsButtonView) {
510 return new AnimatorListenerAdapter() {
511 public void onAnimationEnd(Animator animation) {
512 // Hide the reveal view
513 revealView.setVisibility(View.INVISIBLE);
514 }
515 };
516 }
517 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700518 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
Winson Chung0f785722015-04-08 10:27:49 -0700519 widgetsView.getContentView(), widgetsView.getRevealView(), animated,
520 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800521 }
522
523 /**
524 * Creates and starts a new animation to the workspace.
525 */
526 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700527 final int toWorkspacePage, final View fromView, final View contentView,
528 final View revealView, final boolean animated, final Runnable onCompleteRunnable,
Winson Chung0f785722015-04-08 10:27:49 -0700529 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800530 final Resources res = mLauncher.getResources();
531 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700532 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800533 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700534 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800535
536 final View allAppsButtonView = mLauncher.getAllAppsButton();
537 final View toView = mLauncher.getWorkspace();
538
539 final HashMap<View, Integer> layerViews = new HashMap<>();
540
541 // If for some reason our views aren't initialized, don't animate
542 boolean initialized = allAppsButtonView != null;
543
544 // Cancel the current animation
545 cancelAnimation();
546
547 // Create the workspace animation.
548 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700549 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
550 toWorkspacePage, animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800551
552 if (animated && initialized) {
553 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
554
555 // Play the workspace animation
556 if (workspaceAnim != null) {
557 mStateAnimation.play(workspaceAnim);
558 }
559
560 // hideAppsCustomizeHelper is called in some cases when it is already hidden
561 // don't perform all these no-op animations. In particularly, this was causing
562 // the all-apps button to pop in and out.
563 if (fromView.getVisibility() == View.VISIBLE) {
564 int width = revealView.getMeasuredWidth();
565 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700566 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800567 revealView.setVisibility(View.VISIBLE);
568 revealView.setAlpha(1f);
569 revealView.setTranslationY(0);
570 layerViews.put(revealView, BUILD_AND_SET_LAYER);
571 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
572
573 // Calculate the final animation values
574 final float revealViewToXDrift;
575 final float revealViewToYDrift;
576 if (material) {
577 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
578 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
579 } else {
580 revealViewToYDrift = 2 * height / 3;
581 revealViewToXDrift = 0;
582 }
583
584 // The vertical motion of the apps panel should be delayed by one frame
585 // from the conceal animation in order to give the right feel. We correspondingly
586 // shorten the duration so that the slide and conceal end at the same time.
587 TimeInterpolator decelerateInterpolator = material ?
588 new LogDecelerateInterpolator(100, 0) :
589 new DecelerateInterpolator(1f);
590 ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
591 0, revealViewToYDrift);
592 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
593 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
594 panelDriftY.setInterpolator(decelerateInterpolator);
595 mStateAnimation.play(panelDriftY);
596
597 ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
598 0, revealViewToXDrift);
599 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
600 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
601 panelDriftX.setInterpolator(decelerateInterpolator);
602 mStateAnimation.play(panelDriftX);
603
604 // Setup animation for the reveal panel alpha
605 final float revealViewToAlpha = !material ? 0f :
606 pCb.getMaterialRevealViewFinalAlpha(revealView);
607 if (revealViewToAlpha != 1f) {
608 ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
609 1f, revealViewToAlpha);
610 panelAlpha.setDuration(material ? revealDuration : 150);
611 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
612 panelAlpha.setInterpolator(decelerateInterpolator);
613 mStateAnimation.play(panelAlpha);
614 }
615
616 // Setup the animation for the content view
617 layerViews.put(contentView, BUILD_AND_SET_LAYER);
618
619 // Create the individual animators
620 ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY",
621 0, revealViewToYDrift);
622 contentView.setTranslationY(0);
623 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
624 pageDrift.setInterpolator(decelerateInterpolator);
625 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
626 mStateAnimation.play(pageDrift);
627
628 contentView.setAlpha(1f);
629 ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f);
630 itemsAlpha.setDuration(100);
631 itemsAlpha.setInterpolator(decelerateInterpolator);
632 mStateAnimation.play(itemsAlpha);
633
Winson Chungb745afb2015-03-02 11:51:23 -0800634 if (material) {
635 // Animate the all apps button
636 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
637 AnimatorListenerAdapter listener =
638 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
639 Animator reveal =
640 LauncherAnimUtils.createCircularReveal(revealView, width / 2,
641 height / 2, revealRadius, finalRadius);
642 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
643 reveal.setDuration(revealDuration);
644 reveal.setStartDelay(itemsAlphaStagger);
645 if (listener != null) {
646 reveal.addListener(listener);
647 }
648 mStateAnimation.play(reveal);
649 }
650
651 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
652 dispatchOnLauncherTransitionPrepare(toView, animated, true);
653 }
654
655 mStateAnimation.addListener(new AnimatorListenerAdapter() {
656 @Override
657 public void onAnimationEnd(Animator animation) {
658 fromView.setVisibility(View.GONE);
659 dispatchOnLauncherTransitionEnd(fromView, animated, true);
660 dispatchOnLauncherTransitionEnd(toView, animated, true);
661
662 // Run any queued runnables
663 if (onCompleteRunnable != null) {
664 onCompleteRunnable.run();
665 }
666
667 // Animation complete callback
668 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
669
670 // Disable all necessary layers
671 for (View v : layerViews.keySet()) {
672 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
673 v.setLayerType(View.LAYER_TYPE_NONE, null);
674 }
675 }
676
677 // Reset page transforms
678 if (contentView != null) {
679 contentView.setTranslationX(0);
680 contentView.setTranslationY(0);
681 contentView.setAlpha(1);
682 }
683
684 // This can hold unnecessary references to views.
685 mStateAnimation = null;
686 }
687 });
688
689 final AnimatorSet stateAnimation = mStateAnimation;
690 final Runnable startAnimRunnable = new Runnable() {
691 public void run() {
692 // Check that mStateAnimation hasn't changed while
693 // we waited for a layout/draw pass
694 if (mStateAnimation != stateAnimation)
695 return;
696 dispatchOnLauncherTransitionStart(fromView, animated, false);
697 dispatchOnLauncherTransitionStart(toView, animated, false);
698
699 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700700 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800701 for (View v : layerViews.keySet()) {
702 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
703 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
704 }
Winson Chung11509ad2015-05-12 18:55:26 -0700705 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800706 v.buildLayer();
707 }
708 }
709 mStateAnimation.start();
710 }
711 };
712 fromView.post(startAnimRunnable);
713 } else {
714 fromView.setVisibility(View.GONE);
715 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
716 dispatchOnLauncherTransitionStart(fromView, animated, true);
717 dispatchOnLauncherTransitionEnd(fromView, animated, true);
718 dispatchOnLauncherTransitionPrepare(toView, animated, true);
719 dispatchOnLauncherTransitionStart(toView, animated, true);
720 dispatchOnLauncherTransitionEnd(toView, animated, true);
721
722 // Run any queued runnables
723 if (onCompleteRunnable != null) {
724 onCompleteRunnable.run();
725 }
726 }
727 }
728
729
730 /**
731 * Dispatches the prepare-transition event to suitable views.
732 */
733 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
734 if (v instanceof LauncherTransitionable) {
735 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
736 toWorkspace);
737 }
738 }
739
740 /**
741 * Dispatches the start-transition event to suitable views.
742 */
743 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
744 if (v instanceof LauncherTransitionable) {
745 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
746 toWorkspace);
747 }
748
749 // Update the workspace transition step as well
750 dispatchOnLauncherTransitionStep(v, 0f);
751 }
752
753 /**
754 * Dispatches the step-transition event to suitable views.
755 */
756 void dispatchOnLauncherTransitionStep(View v, float t) {
757 if (v instanceof LauncherTransitionable) {
758 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
759 }
760 }
761
762 /**
763 * Dispatches the end-transition event to suitable views.
764 */
765 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
766 if (v instanceof LauncherTransitionable) {
767 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
768 toWorkspace);
769 }
770
771 // Update the workspace transition step as well
772 dispatchOnLauncherTransitionStep(v, 1f);
773 }
774
775 /**
776 * Cancels the current animation.
777 */
778 private void cancelAnimation() {
779 if (mStateAnimation != null) {
780 mStateAnimation.setDuration(0);
781 mStateAnimation.cancel();
782 mStateAnimation = null;
783 }
784 }
785}