blob: a006d141b9d641e2101078729023e788aad78635 [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;
Winson Chungb745afb2015-03-02 11:51:23 -080029import android.view.animation.AccelerateInterpolator;
30import android.view.animation.DecelerateInterpolator;
Adam Cohen15588932015-05-26 23:03:31 -070031
Winson Chung5f4e0fd2015-05-22 11:12:27 -070032import com.android.launcher3.allapps.AllAppsContainerView;
Adam Cohen15588932015-05-26 23:03:31 -070033import com.android.launcher3.util.UiThreadCircularReveal;
Adam Cohen091440a2015-03-18 14:16:05 -070034import com.android.launcher3.util.Thunk;
Hyunyoung Song3f471442015-04-08 19:01:34 -070035import com.android.launcher3.widget.WidgetsContainerView;
Adam Cohen15588932015-05-26 23:03:31 -070036
Winson Chungb745afb2015-03-02 11:51:23 -080037import java.util.HashMap;
38
39/**
40 * TODO: figure out what kind of tests we can write for this
41 *
42 * Things to test when changing the following class.
43 * - Home from workspace
44 * - from center screen
45 * - from other screens
46 * - Home from all apps
47 * - from center screen
48 * - from other screens
49 * - Back from all apps
50 * - from center screen
51 * - from other screens
52 * - Launch app from workspace and quit
53 * - with back
54 * - with home
55 * - Launch app from all apps and quit
56 * - with back
57 * - with home
58 * - Go to a screen that's not the default, then all
59 * apps, and launch and app, and go back
60 * - with back
61 * -with home
62 * - On workspace, long press power and go back
63 * - with back
64 * - with home
65 * - On all apps, long press power and go back
66 * - with back
67 * - with home
68 * - On workspace, power off
69 * - On all apps, power off
70 * - Launch an app and turn off the screen while in that app
71 * - Go back with home key
72 * - Go back with back key TODO: make this not go to workspace
73 * - From all apps
74 * - From workspace
75 * - Enter and exit car mode (becuase it causes an extra configuration changed)
76 * - From all apps
77 * - From the center workspace
78 * - From another workspace
79 */
80public class LauncherStateTransitionAnimation {
81
82 /**
83 * Callbacks made during the state transition
84 */
85 interface Callbacks {
86 public void onStateTransitionHideSearchBar();
87 }
88
89 /**
90 * Private callbacks made during transition setup.
91 */
92 static abstract class PrivateTransitionCallbacks {
93 void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
94 void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {}
95 float getMaterialRevealViewFinalAlpha(View revealView) {
96 return 0;
97 }
98 float getMaterialRevealViewFinalXDrift(View revealView) {
99 return 0;
100 }
101 float getMaterialRevealViewFinalYDrift(View revealView) {
102 return 0;
103 }
104 float getMaterialRevealViewStartFinalRadius() {
105 return 0;
106 }
107 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
108 View allAppsButtonView) {
109 return null;
110 }
111 }
112
113 public static final String TAG = "LauncherStateTransitionAnimation";
114
115 // Flags to determine how to set the layers on views before the transition animation
116 public static final int BUILD_LAYER = 0;
117 public static final int BUILD_AND_SET_LAYER = 1;
118 public static final int SINGLE_FRAME_DELAY = 16;
119
Adam Cohen091440a2015-03-18 14:16:05 -0700120 @Thunk Launcher mLauncher;
121 @Thunk Callbacks mCb;
122 @Thunk AnimatorSet mStateAnimation;
Winson Chungb745afb2015-03-02 11:51:23 -0800123
124 public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
125 mLauncher = l;
126 mCb = cb;
127 }
128
129 /**
130 * Starts an animation to the apps view.
131 */
132 public void startAnimationToAllApps(final boolean animated) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700133 final AllAppsContainerView toView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800134 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
135 private int[] mAllAppsToPanelDelta;
136
137 @Override
138 public void onRevealViewVisible(View revealView, View contentView,
139 View allAppsButtonView) {
140 toView.setBackground(null);
141 // Get the y delta between the center of the page and the center of the all apps
142 // button
143 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
144 allAppsButtonView, null);
145 }
146 @Override
147 public float getMaterialRevealViewFinalAlpha(View revealView) {
148 return 1f;
149 }
150 @Override
151 public float getMaterialRevealViewFinalXDrift(View revealView) {
152 return mAllAppsToPanelDelta[0];
153 }
154 @Override
155 public float getMaterialRevealViewFinalYDrift(View revealView) {
156 return mAllAppsToPanelDelta[1];
157 }
158 @Override
159 public float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700160 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800161 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 Chungdc61c4d2015-04-20 18:26:57 -0700177 toView.getRevealView(), animated,
178 !mLauncher.isAllAppsSearchOverridden() /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800179 }
180
181 /**
182 * Starts an animation to the widgets view.
183 */
184 public void startAnimationToWidgets(final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700185 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700186 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800187 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
188 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700189 public void onRevealViewVisible(View revealView, View contentView,
190 View allAppsButtonView) {
191 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
192 }
193 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800194 public float getMaterialRevealViewFinalAlpha(View revealView) {
195 return 0.3f;
196 }
197 @Override
198 public float getMaterialRevealViewFinalYDrift(View revealView) {
199 return revealView.getMeasuredHeight() / 2;
200 }
201 };
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700202 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
203 toView.getContentView(), toView.getRevealView(), animated, true /* hideSearchBar */,
204 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800205 }
206
207 /**
208 * Starts and animation to the workspace from the current overlay view.
209 */
210 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700211 final Workspace.State toWorkspaceState, final int toWorkspacePage,
212 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800213 if (toWorkspaceState != Workspace.State.NORMAL &&
214 toWorkspaceState != Workspace.State.SPRING_LOADED &&
215 toWorkspaceState != Workspace.State.OVERVIEW) {
216 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
217 }
218
219 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chungdc61c4d2015-04-20 18:26:57 -0700220 startAnimationToWorkspaceFromAllApps(fromState, toWorkspaceState, toWorkspacePage,
221 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800222 } else {
Winson Chungdc61c4d2015-04-20 18:26:57 -0700223 startAnimationToWorkspaceFromWidgets(fromState, toWorkspaceState, toWorkspacePage,
224 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800225 }
226 }
227
228 /**
229 * Creates and starts a new animation to a particular overlay view.
230 */
Sunny Goyal70660032015-05-14 00:07:08 -0700231 @SuppressLint("NewApi")
Winson Chungb745afb2015-03-02 11:51:23 -0800232 private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
Winson Chung0f785722015-04-08 10:27:49 -0700233 final View contentView, final View revealView, final boolean animated,
234 final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800235 final Resources res = mLauncher.getResources();
236 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700237 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800238 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700239 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800240
241 final View allAppsButtonView = mLauncher.getAllAppsButton();
242 final View fromView = mLauncher.getWorkspace();
243
244 final HashMap<View, Integer> layerViews = new HashMap<>();
245
246 // If for some reason our views aren't initialized, don't animate
247 boolean initialized = allAppsButtonView != null;
248
249 // Cancel the current animation
250 cancelAnimation();
251
252 // Create the workspace animation.
253 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700254 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
255 animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800256
257 if (animated && initialized) {
258 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
259
260 // Setup the reveal view animation
261 int width = revealView.getMeasuredWidth();
262 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700263 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800264 revealView.setVisibility(View.VISIBLE);
265 revealView.setAlpha(0f);
266 revealView.setTranslationY(0f);
267 revealView.setTranslationX(0f);
268 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
269
270 // Calculate the final animation values
271 final float revealViewToAlpha;
272 final float revealViewToXDrift;
273 final float revealViewToYDrift;
274 if (material) {
275 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
276 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
277 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
278 } else {
279 revealViewToAlpha = 0f;
280 revealViewToYDrift = 2 * height / 3;
281 revealViewToXDrift = 0;
282 }
283
284 // Create the animators
285 PropertyValuesHolder panelAlpha =
286 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
287 PropertyValuesHolder panelDriftY =
288 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
289 PropertyValuesHolder panelDriftX =
290 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
291 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
292 panelAlpha, panelDriftY, panelDriftX);
293 panelAlphaAndDrift.setDuration(revealDuration);
294 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
295
296 // Play the animation
297 layerViews.put(revealView, BUILD_AND_SET_LAYER);
298 mStateAnimation.play(panelAlphaAndDrift);
299
Winson Chungb745afb2015-03-02 11:51:23 -0800300 // Setup the animation for the content view
301 contentView.setVisibility(View.VISIBLE);
302 contentView.setAlpha(0f);
303 contentView.setTranslationY(revealViewToYDrift);
304 layerViews.put(contentView, BUILD_AND_SET_LAYER);
305
306 // Create the individual animators
307 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
308 revealViewToYDrift, 0);
309 pageDrift.setDuration(revealDuration);
310 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
311 pageDrift.setStartDelay(itemsAlphaStagger);
312 mStateAnimation.play(pageDrift);
313
314 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
315 itemsAlpha.setDuration(revealDuration);
316 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
317 itemsAlpha.setStartDelay(itemsAlphaStagger);
318 mStateAnimation.play(itemsAlpha);
319
320 if (material) {
321 // Animate the all apps button
322 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
323 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
324 revealView, allAppsButtonView);
Adam Cohen15588932015-05-26 23:03:31 -0700325 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
Winson Chungb745afb2015-03-02 11:51:23 -0800326 height / 2, startRadius, revealRadius);
327 reveal.setDuration(revealDuration);
328 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
329 if (listener != null) {
330 reveal.addListener(listener);
331 }
332 mStateAnimation.play(reveal);
333 }
334
335 mStateAnimation.addListener(new AnimatorListenerAdapter() {
336 @Override
337 public void onAnimationEnd(Animator animation) {
338 dispatchOnLauncherTransitionEnd(fromView, animated, false);
339 dispatchOnLauncherTransitionEnd(toView, animated, false);
340
341 // Hide the reveal view
342 revealView.setVisibility(View.INVISIBLE);
343 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
344
345 // Disable all necessary layers
346 for (View v : layerViews.keySet()) {
347 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
348 v.setLayerType(View.LAYER_TYPE_NONE, null);
349 }
350 }
351
Winson Chung0f785722015-04-08 10:27:49 -0700352 if (hideSearchBar) {
353 mCb.onStateTransitionHideSearchBar();
354 }
Winson Chungb745afb2015-03-02 11:51:23 -0800355
356 // This can hold unnecessary references to views.
357 mStateAnimation = null;
358 }
359
360 });
361
362 // Play the workspace animation
363 if (workspaceAnim != null) {
364 mStateAnimation.play(workspaceAnim);
365 }
366
367 // Dispatch the prepare transition signal
368 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
369 dispatchOnLauncherTransitionPrepare(toView, animated, false);
370
371
372 final AnimatorSet stateAnimation = mStateAnimation;
373 final Runnable startAnimRunnable = new Runnable() {
374 public void run() {
375 // Check that mStateAnimation hasn't changed while
376 // we waited for a layout/draw pass
377 if (mStateAnimation != stateAnimation)
378 return;
379 dispatchOnLauncherTransitionStart(fromView, animated, false);
380 dispatchOnLauncherTransitionStart(toView, animated, false);
381
382 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700383 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800384 for (View v : layerViews.keySet()) {
385 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
386 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
387 }
Winson Chung11509ad2015-05-12 18:55:26 -0700388 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800389 v.buildLayer();
390 }
391 }
392
393 // Focus the new view
394 toView.requestFocus();
395
396 mStateAnimation.start();
397 }
398 };
Winson Chungb745afb2015-03-02 11:51:23 -0800399 toView.bringToFront();
400 toView.setVisibility(View.VISIBLE);
401 toView.post(startAnimRunnable);
402 } else {
403 toView.setTranslationX(0.0f);
404 toView.setTranslationY(0.0f);
405 toView.setScaleX(1.0f);
406 toView.setScaleY(1.0f);
407 toView.setVisibility(View.VISIBLE);
408 toView.bringToFront();
409
410 // Show the content view
411 contentView.setVisibility(View.VISIBLE);
412
Winson Chung0f785722015-04-08 10:27:49 -0700413 if (hideSearchBar) {
414 mCb.onStateTransitionHideSearchBar();
415 }
Winson Chungb745afb2015-03-02 11:51:23 -0800416
417 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
418 dispatchOnLauncherTransitionStart(fromView, animated, false);
419 dispatchOnLauncherTransitionEnd(fromView, animated, false);
420 dispatchOnLauncherTransitionPrepare(toView, animated, false);
421 dispatchOnLauncherTransitionStart(toView, animated, false);
422 dispatchOnLauncherTransitionEnd(toView, animated, false);
423 }
424 }
425
426 /**
427 * Starts and animation to the workspace from the apps view.
428 */
429 private void startAnimationToWorkspaceFromAllApps(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700430 final Workspace.State toWorkspaceState, final int toWorkspacePage,
431 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700432 AllAppsContainerView appsView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800433 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
434 int[] mAllAppsToPanelDelta;
435
436 @Override
437 public void onRevealViewVisible(View revealView, View contentView,
438 View allAppsButtonView) {
439 // Get the y delta between the center of the page and the center of the all apps
440 // button
441 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
442 allAppsButtonView, null);
443 }
444 @Override
445 public float getMaterialRevealViewFinalXDrift(View revealView) {
446 return mAllAppsToPanelDelta[0];
447 }
448 @Override
449 public float getMaterialRevealViewFinalYDrift(View revealView) {
450 return mAllAppsToPanelDelta[1];
451 }
452 @Override
453 float getMaterialRevealViewFinalAlpha(View revealView) {
454 // No alpha anim from all apps
455 return 1f;
456 }
457 @Override
458 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700459 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800460 return allAppsButtonSize / 2;
461 }
462 @Override
463 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
464 final View revealView, final View allAppsButtonView) {
465 return new AnimatorListenerAdapter() {
466 public void onAnimationStart(Animator animation) {
467 // We set the alpha instead of visibility to ensure that the focus does not
468 // get taken from the all apps view
469 allAppsButtonView.setVisibility(View.VISIBLE);
470 allAppsButtonView.setAlpha(0f);
471 }
472 public void onAnimationEnd(Animator animation) {
473 // Hide the reveal view
474 revealView.setVisibility(View.INVISIBLE);
475
476 // Show the all apps button, and focus it
477 allAppsButtonView.setAlpha(1f);
478 }
479 };
480 }
481 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700482 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
483 appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable,
484 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800485 }
486
487 /**
488 * Starts and animation to the workspace from the widgets view.
489 */
490 private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700491 final Workspace.State toWorkspaceState, final int toWorkspacePage,
492 final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700493 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
494 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800495 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
496 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700497 public void onRevealViewVisible(View revealView, View contentView,
498 View allAppsButtonView) {
499 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
500 }
501 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800502 public float getMaterialRevealViewFinalYDrift(View revealView) {
503 return revealView.getMeasuredHeight() / 2;
504 }
505 @Override
506 float getMaterialRevealViewFinalAlpha(View revealView) {
507 return 0.4f;
508 }
509 @Override
510 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
511 final View revealView, final View allAppsButtonView) {
512 return new AnimatorListenerAdapter() {
513 public void onAnimationEnd(Animator animation) {
514 // Hide the reveal view
515 revealView.setVisibility(View.INVISIBLE);
516 }
517 };
518 }
519 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700520 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
Winson Chung0f785722015-04-08 10:27:49 -0700521 widgetsView.getContentView(), widgetsView.getRevealView(), animated,
522 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800523 }
524
525 /**
526 * Creates and starts a new animation to the workspace.
527 */
528 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700529 final int toWorkspacePage, final View fromView, final View contentView,
530 final View revealView, final boolean animated, final Runnable onCompleteRunnable,
Winson Chung0f785722015-04-08 10:27:49 -0700531 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800532 final Resources res = mLauncher.getResources();
533 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700534 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800535 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700536 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800537
538 final View allAppsButtonView = mLauncher.getAllAppsButton();
539 final View toView = mLauncher.getWorkspace();
540
541 final HashMap<View, Integer> layerViews = new HashMap<>();
542
543 // If for some reason our views aren't initialized, don't animate
544 boolean initialized = allAppsButtonView != null;
545
546 // Cancel the current animation
547 cancelAnimation();
548
549 // Create the workspace animation.
550 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700551 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
552 toWorkspacePage, animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800553
554 if (animated && initialized) {
555 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
556
557 // Play the workspace animation
558 if (workspaceAnim != null) {
559 mStateAnimation.play(workspaceAnim);
560 }
561
562 // hideAppsCustomizeHelper is called in some cases when it is already hidden
563 // don't perform all these no-op animations. In particularly, this was causing
564 // the all-apps button to pop in and out.
565 if (fromView.getVisibility() == View.VISIBLE) {
566 int width = revealView.getMeasuredWidth();
567 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700568 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800569 revealView.setVisibility(View.VISIBLE);
570 revealView.setAlpha(1f);
571 revealView.setTranslationY(0);
572 layerViews.put(revealView, BUILD_AND_SET_LAYER);
573 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
574
575 // Calculate the final animation values
576 final float revealViewToXDrift;
577 final float revealViewToYDrift;
578 if (material) {
579 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
580 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
581 } else {
582 revealViewToYDrift = 2 * height / 3;
583 revealViewToXDrift = 0;
584 }
585
586 // The vertical motion of the apps panel should be delayed by one frame
587 // from the conceal animation in order to give the right feel. We correspondingly
588 // shorten the duration so that the slide and conceal end at the same time.
589 TimeInterpolator decelerateInterpolator = material ?
590 new LogDecelerateInterpolator(100, 0) :
591 new DecelerateInterpolator(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700592 ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800593 0, revealViewToYDrift);
594 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
595 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
596 panelDriftY.setInterpolator(decelerateInterpolator);
597 mStateAnimation.play(panelDriftY);
598
Adam Cohen15588932015-05-26 23:03:31 -0700599 ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
Winson Chungb745afb2015-03-02 11:51:23 -0800600 0, revealViewToXDrift);
601 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
602 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
603 panelDriftX.setInterpolator(decelerateInterpolator);
604 mStateAnimation.play(panelDriftX);
605
606 // Setup animation for the reveal panel alpha
607 final float revealViewToAlpha = !material ? 0f :
608 pCb.getMaterialRevealViewFinalAlpha(revealView);
609 if (revealViewToAlpha != 1f) {
Adam Cohen15588932015-05-26 23:03:31 -0700610 ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
Winson Chungb745afb2015-03-02 11:51:23 -0800611 1f, revealViewToAlpha);
612 panelAlpha.setDuration(material ? revealDuration : 150);
613 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
614 panelAlpha.setInterpolator(decelerateInterpolator);
615 mStateAnimation.play(panelAlpha);
616 }
617
618 // Setup the animation for the content view
619 layerViews.put(contentView, BUILD_AND_SET_LAYER);
620
621 // Create the individual animators
Adam Cohen15588932015-05-26 23:03:31 -0700622 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800623 0, revealViewToYDrift);
624 contentView.setTranslationY(0);
625 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
626 pageDrift.setInterpolator(decelerateInterpolator);
627 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
628 mStateAnimation.play(pageDrift);
629
630 contentView.setAlpha(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700631 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800632 itemsAlpha.setDuration(100);
633 itemsAlpha.setInterpolator(decelerateInterpolator);
634 mStateAnimation.play(itemsAlpha);
635
Winson Chungb745afb2015-03-02 11:51:23 -0800636 if (material) {
637 // Animate the all apps button
638 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
639 AnimatorListenerAdapter listener =
640 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
Adam Cohen15588932015-05-26 23:03:31 -0700641 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
642 height / 2, revealRadius, finalRadius);
Winson Chungb745afb2015-03-02 11:51:23 -0800643 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
644 reveal.setDuration(revealDuration);
645 reveal.setStartDelay(itemsAlphaStagger);
646 if (listener != null) {
647 reveal.addListener(listener);
648 }
649 mStateAnimation.play(reveal);
650 }
651
652 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
653 dispatchOnLauncherTransitionPrepare(toView, animated, true);
654 }
655
656 mStateAnimation.addListener(new AnimatorListenerAdapter() {
657 @Override
658 public void onAnimationEnd(Animator animation) {
659 fromView.setVisibility(View.GONE);
660 dispatchOnLauncherTransitionEnd(fromView, animated, true);
661 dispatchOnLauncherTransitionEnd(toView, animated, true);
662
663 // Run any queued runnables
664 if (onCompleteRunnable != null) {
665 onCompleteRunnable.run();
666 }
667
668 // Animation complete callback
669 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
670
671 // Disable all necessary layers
672 for (View v : layerViews.keySet()) {
673 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
674 v.setLayerType(View.LAYER_TYPE_NONE, null);
675 }
676 }
677
678 // Reset page transforms
679 if (contentView != null) {
680 contentView.setTranslationX(0);
681 contentView.setTranslationY(0);
682 contentView.setAlpha(1);
683 }
684
685 // This can hold unnecessary references to views.
686 mStateAnimation = null;
687 }
688 });
689
690 final AnimatorSet stateAnimation = mStateAnimation;
691 final Runnable startAnimRunnable = new Runnable() {
692 public void run() {
693 // Check that mStateAnimation hasn't changed while
694 // we waited for a layout/draw pass
695 if (mStateAnimation != stateAnimation)
696 return;
697 dispatchOnLauncherTransitionStart(fromView, animated, false);
698 dispatchOnLauncherTransitionStart(toView, animated, false);
699
700 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700701 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800702 for (View v : layerViews.keySet()) {
703 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
704 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
705 }
Winson Chung11509ad2015-05-12 18:55:26 -0700706 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800707 v.buildLayer();
708 }
709 }
710 mStateAnimation.start();
711 }
712 };
713 fromView.post(startAnimRunnable);
714 } else {
715 fromView.setVisibility(View.GONE);
716 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
717 dispatchOnLauncherTransitionStart(fromView, animated, true);
718 dispatchOnLauncherTransitionEnd(fromView, animated, true);
719 dispatchOnLauncherTransitionPrepare(toView, animated, true);
720 dispatchOnLauncherTransitionStart(toView, animated, true);
721 dispatchOnLauncherTransitionEnd(toView, animated, true);
722
723 // Run any queued runnables
724 if (onCompleteRunnable != null) {
725 onCompleteRunnable.run();
726 }
727 }
728 }
729
730
731 /**
732 * Dispatches the prepare-transition event to suitable views.
733 */
734 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
735 if (v instanceof LauncherTransitionable) {
736 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
737 toWorkspace);
738 }
739 }
740
741 /**
742 * Dispatches the start-transition event to suitable views.
743 */
744 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
745 if (v instanceof LauncherTransitionable) {
746 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
747 toWorkspace);
748 }
749
750 // Update the workspace transition step as well
751 dispatchOnLauncherTransitionStep(v, 0f);
752 }
753
754 /**
755 * Dispatches the step-transition event to suitable views.
756 */
757 void dispatchOnLauncherTransitionStep(View v, float t) {
758 if (v instanceof LauncherTransitionable) {
759 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
760 }
761 }
762
763 /**
764 * Dispatches the end-transition event to suitable views.
765 */
766 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
767 if (v instanceof LauncherTransitionable) {
768 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
769 toWorkspace);
770 }
771
772 // Update the workspace transition step as well
773 dispatchOnLauncherTransitionStep(v, 1f);
774 }
775
776 /**
777 * Cancels the current animation.
778 */
779 private void cancelAnimation() {
780 if (mStateAnimation != null) {
781 mStateAnimation.setDuration(0);
782 mStateAnimation.cancel();
783 mStateAnimation = null;
784 }
785 }
Adam Cohen15588932015-05-26 23:03:31 -0700786}