blob: 324375444487e88e6c99a8f9b5ee26e22bbafa69 [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;
26import android.annotation.TargetApi;
Winson Chungb745afb2015-03-02 11:51:23 -080027import android.content.res.Resources;
Sunny Goyal70660032015-05-14 00:07:08 -070028import android.os.Build;
Winson Chungb745afb2015-03-02 11:51:23 -080029import android.util.Log;
30import android.view.View;
31import android.view.ViewAnimationUtils;
32import android.view.animation.AccelerateInterpolator;
33import android.view.animation.DecelerateInterpolator;
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;
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 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();
237 final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
238 final int itemsAlphaStagger =
239 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
240
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);
325 Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
326 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 Chungb745afb2015-03-02 11:51:23 -0800432 AppsContainerView appsView = mLauncher.getAppsView();
433 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() {
459 int allAppsButtonSize = LauncherAppState.getInstance().
460 getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
461 return allAppsButtonSize / 2;
462 }
463 @Override
464 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
465 final View revealView, final View allAppsButtonView) {
466 return new AnimatorListenerAdapter() {
467 public void onAnimationStart(Animator animation) {
468 // We set the alpha instead of visibility to ensure that the focus does not
469 // get taken from the all apps view
470 allAppsButtonView.setVisibility(View.VISIBLE);
471 allAppsButtonView.setAlpha(0f);
472 }
473 public void onAnimationEnd(Animator animation) {
474 // Hide the reveal view
475 revealView.setVisibility(View.INVISIBLE);
476
477 // Show the all apps button, and focus it
478 allAppsButtonView.setAlpha(1f);
479 }
480 };
481 }
482 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700483 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
484 appsView.getContentView(), appsView.getRevealView(), animated, onCompleteRunnable,
485 cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800486 }
487
488 /**
489 * Starts and animation to the workspace from the widgets view.
490 */
491 private void startAnimationToWorkspaceFromWidgets(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700492 final Workspace.State toWorkspaceState, final int toWorkspacePage,
493 final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700494 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
495 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800496 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
497 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700498 public void onRevealViewVisible(View revealView, View contentView,
499 View allAppsButtonView) {
500 revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
501 }
502 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800503 public float getMaterialRevealViewFinalYDrift(View revealView) {
504 return revealView.getMeasuredHeight() / 2;
505 }
506 @Override
507 float getMaterialRevealViewFinalAlpha(View revealView) {
508 return 0.4f;
509 }
510 @Override
511 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
512 final View revealView, final View allAppsButtonView) {
513 return new AnimatorListenerAdapter() {
514 public void onAnimationEnd(Animator animation) {
515 // Hide the reveal view
516 revealView.setVisibility(View.INVISIBLE);
517 }
518 };
519 }
520 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700521 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
Winson Chung0f785722015-04-08 10:27:49 -0700522 widgetsView.getContentView(), widgetsView.getRevealView(), animated,
523 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800524 }
525
526 /**
527 * Creates and starts a new animation to the workspace.
528 */
529 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700530 final int toWorkspacePage, final View fromView, final View contentView,
531 final View revealView, final boolean animated, final Runnable onCompleteRunnable,
Winson Chung0f785722015-04-08 10:27:49 -0700532 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800533 final Resources res = mLauncher.getResources();
534 final boolean material = Utilities.isLmpOrAbove();
535 final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
536 final int itemsAlphaStagger =
537 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
538
539 final View allAppsButtonView = mLauncher.getAllAppsButton();
540 final View toView = mLauncher.getWorkspace();
541
542 final HashMap<View, Integer> layerViews = new HashMap<>();
543
544 // If for some reason our views aren't initialized, don't animate
545 boolean initialized = allAppsButtonView != null;
546
547 // Cancel the current animation
548 cancelAnimation();
549
550 // Create the workspace animation.
551 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700552 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
553 toWorkspacePage, animated, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800554
555 if (animated && initialized) {
556 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
557
558 // Play the workspace animation
559 if (workspaceAnim != null) {
560 mStateAnimation.play(workspaceAnim);
561 }
562
563 // hideAppsCustomizeHelper is called in some cases when it is already hidden
564 // don't perform all these no-op animations. In particularly, this was causing
565 // the all-apps button to pop in and out.
566 if (fromView.getVisibility() == View.VISIBLE) {
567 int width = revealView.getMeasuredWidth();
568 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700569 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800570 revealView.setVisibility(View.VISIBLE);
571 revealView.setAlpha(1f);
572 revealView.setTranslationY(0);
573 layerViews.put(revealView, BUILD_AND_SET_LAYER);
574 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
575
576 // Calculate the final animation values
577 final float revealViewToXDrift;
578 final float revealViewToYDrift;
579 if (material) {
580 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
581 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
582 } else {
583 revealViewToYDrift = 2 * height / 3;
584 revealViewToXDrift = 0;
585 }
586
587 // The vertical motion of the apps panel should be delayed by one frame
588 // from the conceal animation in order to give the right feel. We correspondingly
589 // shorten the duration so that the slide and conceal end at the same time.
590 TimeInterpolator decelerateInterpolator = material ?
591 new LogDecelerateInterpolator(100, 0) :
592 new DecelerateInterpolator(1f);
593 ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
594 0, revealViewToYDrift);
595 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
596 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
597 panelDriftY.setInterpolator(decelerateInterpolator);
598 mStateAnimation.play(panelDriftY);
599
600 ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
601 0, revealViewToXDrift);
602 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
603 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
604 panelDriftX.setInterpolator(decelerateInterpolator);
605 mStateAnimation.play(panelDriftX);
606
607 // Setup animation for the reveal panel alpha
608 final float revealViewToAlpha = !material ? 0f :
609 pCb.getMaterialRevealViewFinalAlpha(revealView);
610 if (revealViewToAlpha != 1f) {
611 ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
612 1f, revealViewToAlpha);
613 panelAlpha.setDuration(material ? revealDuration : 150);
614 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
615 panelAlpha.setInterpolator(decelerateInterpolator);
616 mStateAnimation.play(panelAlpha);
617 }
618
619 // Setup the animation for the content view
620 layerViews.put(contentView, BUILD_AND_SET_LAYER);
621
622 // Create the individual animators
623 ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(contentView, "translationY",
624 0, revealViewToYDrift);
625 contentView.setTranslationY(0);
626 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
627 pageDrift.setInterpolator(decelerateInterpolator);
628 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
629 mStateAnimation.play(pageDrift);
630
631 contentView.setAlpha(1f);
632 ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(contentView, "alpha", 1f, 0f);
633 itemsAlpha.setDuration(100);
634 itemsAlpha.setInterpolator(decelerateInterpolator);
635 mStateAnimation.play(itemsAlpha);
636
Winson Chungb745afb2015-03-02 11:51:23 -0800637 if (material) {
638 // Animate the all apps button
639 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
640 AnimatorListenerAdapter listener =
641 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
642 Animator reveal =
643 LauncherAnimUtils.createCircularReveal(revealView, width / 2,
644 height / 2, revealRadius, finalRadius);
645 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
646 reveal.setDuration(revealDuration);
647 reveal.setStartDelay(itemsAlphaStagger);
648 if (listener != null) {
649 reveal.addListener(listener);
650 }
651 mStateAnimation.play(reveal);
652 }
653
654 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
655 dispatchOnLauncherTransitionPrepare(toView, animated, true);
656 }
657
658 mStateAnimation.addListener(new AnimatorListenerAdapter() {
659 @Override
660 public void onAnimationEnd(Animator animation) {
661 fromView.setVisibility(View.GONE);
662 dispatchOnLauncherTransitionEnd(fromView, animated, true);
663 dispatchOnLauncherTransitionEnd(toView, animated, true);
664
665 // Run any queued runnables
666 if (onCompleteRunnable != null) {
667 onCompleteRunnable.run();
668 }
669
670 // Animation complete callback
671 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
672
673 // Disable all necessary layers
674 for (View v : layerViews.keySet()) {
675 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
676 v.setLayerType(View.LAYER_TYPE_NONE, null);
677 }
678 }
679
680 // Reset page transforms
681 if (contentView != null) {
682 contentView.setTranslationX(0);
683 contentView.setTranslationY(0);
684 contentView.setAlpha(1);
685 }
686
687 // This can hold unnecessary references to views.
688 mStateAnimation = null;
689 }
690 });
691
692 final AnimatorSet stateAnimation = mStateAnimation;
693 final Runnable startAnimRunnable = new Runnable() {
694 public void run() {
695 // Check that mStateAnimation hasn't changed while
696 // we waited for a layout/draw pass
697 if (mStateAnimation != stateAnimation)
698 return;
699 dispatchOnLauncherTransitionStart(fromView, animated, false);
700 dispatchOnLauncherTransitionStart(toView, animated, false);
701
702 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700703 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800704 for (View v : layerViews.keySet()) {
705 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
706 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
707 }
Winson Chung11509ad2015-05-12 18:55:26 -0700708 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800709 v.buildLayer();
710 }
711 }
712 mStateAnimation.start();
713 }
714 };
715 fromView.post(startAnimRunnable);
716 } else {
717 fromView.setVisibility(View.GONE);
718 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
719 dispatchOnLauncherTransitionStart(fromView, animated, true);
720 dispatchOnLauncherTransitionEnd(fromView, animated, true);
721 dispatchOnLauncherTransitionPrepare(toView, animated, true);
722 dispatchOnLauncherTransitionStart(toView, animated, true);
723 dispatchOnLauncherTransitionEnd(toView, animated, true);
724
725 // Run any queued runnables
726 if (onCompleteRunnable != null) {
727 onCompleteRunnable.run();
728 }
729 }
730 }
731
732
733 /**
734 * Dispatches the prepare-transition event to suitable views.
735 */
736 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
737 if (v instanceof LauncherTransitionable) {
738 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
739 toWorkspace);
740 }
741 }
742
743 /**
744 * Dispatches the start-transition event to suitable views.
745 */
746 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
747 if (v instanceof LauncherTransitionable) {
748 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
749 toWorkspace);
750 }
751
752 // Update the workspace transition step as well
753 dispatchOnLauncherTransitionStep(v, 0f);
754 }
755
756 /**
757 * Dispatches the step-transition event to suitable views.
758 */
759 void dispatchOnLauncherTransitionStep(View v, float t) {
760 if (v instanceof LauncherTransitionable) {
761 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
762 }
763 }
764
765 /**
766 * Dispatches the end-transition event to suitable views.
767 */
768 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
769 if (v instanceof LauncherTransitionable) {
770 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
771 toWorkspace);
772 }
773
774 // Update the workspace transition step as well
775 dispatchOnLauncherTransitionStep(v, 1f);
776 }
777
778 /**
779 * Cancels the current animation.
780 */
781 private void cancelAnimation() {
782 if (mStateAnimation != null) {
783 mStateAnimation.setDuration(0);
784 mStateAnimation.cancel();
785 mStateAnimation = null;
786 }
787 }
788}