blob: 0ea9d8bd072a13b6e425d519ce36d9524efa412a [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) {}
Winson Chungb745afb2015-03-02 11:51:23 -080094 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 }
Winson Chung76648c52015-07-10 14:33:23 -0700110 void onTransitionComplete() {}
Winson Chungb745afb2015-03-02 11:51:23 -0800111 }
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.
Winson Chung76648c52015-07-10 14:33:23 -0700131 *
132 * @param startSearchAfterTransition Immediately starts app search after the transition to
133 * All Apps is completed.
Winson Chungb745afb2015-03-02 11:51:23 -0800134 */
Winson Chung76648c52015-07-10 14:33:23 -0700135 public void startAnimationToAllApps(final boolean animated,
136 final boolean startSearchAfterTransition) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700137 final AllAppsContainerView toView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800138 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
139 private int[] mAllAppsToPanelDelta;
140
141 @Override
142 public void onRevealViewVisible(View revealView, View contentView,
143 View allAppsButtonView) {
Winson Chungb745afb2015-03-02 11:51:23 -0800144 // Get the y delta between the center of the page and the center of the all apps
145 // button
146 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
147 allAppsButtonView, null);
148 }
149 @Override
150 public float getMaterialRevealViewFinalAlpha(View revealView) {
151 return 1f;
152 }
153 @Override
154 public float getMaterialRevealViewFinalXDrift(View revealView) {
155 return mAllAppsToPanelDelta[0];
156 }
157 @Override
158 public float getMaterialRevealViewFinalYDrift(View revealView) {
159 return mAllAppsToPanelDelta[1];
160 }
161 @Override
162 public float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700163 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800164 return allAppsButtonSize / 2;
165 }
166 @Override
167 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
168 final View revealView, final View allAppsButtonView) {
169 return new AnimatorListenerAdapter() {
170 public void onAnimationStart(Animator animation) {
171 allAppsButtonView.setVisibility(View.INVISIBLE);
172 }
173 public void onAnimationEnd(Animator animation) {
174 allAppsButtonView.setVisibility(View.VISIBLE);
175 }
176 };
177 }
Winson Chung76648c52015-07-10 14:33:23 -0700178 @Override
179 void onTransitionComplete() {
180 if (startSearchAfterTransition) {
181 toView.startAppsSearch();
182 }
183 }
Winson Chungb745afb2015-03-02 11:51:23 -0800184 };
Winson Chungef7f8742015-06-04 17:18:17 -0700185 // Only animate the search bar if animating from spring loaded mode back to all apps
Winson Chungb745afb2015-03-02 11:51:23 -0800186 startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
Winson Chung76648c52015-07-10 14:33:23 -0700187 toView.getRevealView(), toView.getSearchBarView(), animated,
188 true /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800189 }
190
191 /**
192 * Starts an animation to the widgets view.
193 */
194 public void startAnimationToWidgets(final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700195 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700196 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800197 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
198 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700199 public void onRevealViewVisible(View revealView, View contentView,
200 View allAppsButtonView) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700201 }
202 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800203 public float getMaterialRevealViewFinalAlpha(View revealView) {
204 return 0.3f;
205 }
206 @Override
207 public float getMaterialRevealViewFinalYDrift(View revealView) {
208 return revealView.getMeasuredHeight() / 2;
209 }
210 };
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700211 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
Winson Chungef7f8742015-06-04 17:18:17 -0700212 toView.getContentView(), toView.getRevealView(), null, animated,
213 true /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800214 }
215
216 /**
217 * Starts and animation to the workspace from the current overlay view.
218 */
219 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700220 final Workspace.State toWorkspaceState, final int toWorkspacePage,
221 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800222 if (toWorkspaceState != Workspace.State.NORMAL &&
223 toWorkspaceState != Workspace.State.SPRING_LOADED &&
224 toWorkspaceState != Workspace.State.OVERVIEW) {
225 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
226 }
227
228 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chungef7f8742015-06-04 17:18:17 -0700229 startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700230 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800231 } else {
Winson Chungef7f8742015-06-04 17:18:17 -0700232 startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700233 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800234 }
235 }
236
237 /**
238 * Creates and starts a new animation to a particular overlay view.
239 */
Sunny Goyal70660032015-05-14 00:07:08 -0700240 @SuppressLint("NewApi")
Winson Chungb745afb2015-03-02 11:51:23 -0800241 private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
Winson Chungef7f8742015-06-04 17:18:17 -0700242 final View contentView, final View revealView, final View overlaySearchBarView,
243 final boolean animated, final boolean hideSearchBar,
244 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800245 final Resources res = mLauncher.getResources();
246 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700247 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800248 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700249 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800250
251 final View allAppsButtonView = mLauncher.getAllAppsButton();
252 final View fromView = mLauncher.getWorkspace();
253
254 final HashMap<View, Integer> layerViews = new HashMap<>();
255
256 // If for some reason our views aren't initialized, don't animate
257 boolean initialized = allAppsButtonView != null;
258
259 // Cancel the current animation
260 cancelAnimation();
261
262 // Create the workspace animation.
263 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700264 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
Winson Chungef7f8742015-06-04 17:18:17 -0700265 animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800266
267 if (animated && initialized) {
268 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
269
270 // Setup the reveal view animation
271 int width = revealView.getMeasuredWidth();
272 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700273 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800274 revealView.setVisibility(View.VISIBLE);
275 revealView.setAlpha(0f);
276 revealView.setTranslationY(0f);
277 revealView.setTranslationX(0f);
278 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
279
280 // Calculate the final animation values
281 final float revealViewToAlpha;
282 final float revealViewToXDrift;
283 final float revealViewToYDrift;
284 if (material) {
285 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
286 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
287 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
288 } else {
289 revealViewToAlpha = 0f;
290 revealViewToYDrift = 2 * height / 3;
291 revealViewToXDrift = 0;
292 }
293
294 // Create the animators
295 PropertyValuesHolder panelAlpha =
296 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
297 PropertyValuesHolder panelDriftY =
298 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
299 PropertyValuesHolder panelDriftX =
300 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
301 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
302 panelAlpha, panelDriftY, panelDriftX);
303 panelAlphaAndDrift.setDuration(revealDuration);
304 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
305
306 // Play the animation
307 layerViews.put(revealView, BUILD_AND_SET_LAYER);
308 mStateAnimation.play(panelAlphaAndDrift);
309
Winson Chungef7f8742015-06-04 17:18:17 -0700310 if (overlaySearchBarView != null) {
311 overlaySearchBarView.setAlpha(0f);
312 ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
313 searchBarAlpha.setDuration(100);
314 searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
315 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
316 mStateAnimation.play(searchBarAlpha);
317 }
318
Winson Chungb745afb2015-03-02 11:51:23 -0800319 // Setup the animation for the content view
320 contentView.setVisibility(View.VISIBLE);
321 contentView.setAlpha(0f);
322 contentView.setTranslationY(revealViewToYDrift);
323 layerViews.put(contentView, BUILD_AND_SET_LAYER);
324
325 // Create the individual animators
326 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
327 revealViewToYDrift, 0);
328 pageDrift.setDuration(revealDuration);
329 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
330 pageDrift.setStartDelay(itemsAlphaStagger);
331 mStateAnimation.play(pageDrift);
332
333 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
334 itemsAlpha.setDuration(revealDuration);
335 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
336 itemsAlpha.setStartDelay(itemsAlphaStagger);
337 mStateAnimation.play(itemsAlpha);
338
339 if (material) {
340 // Animate the all apps button
341 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
342 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
343 revealView, allAppsButtonView);
Adam Cohen15588932015-05-26 23:03:31 -0700344 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
Winson Chungb745afb2015-03-02 11:51:23 -0800345 height / 2, startRadius, revealRadius);
346 reveal.setDuration(revealDuration);
347 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
348 if (listener != null) {
349 reveal.addListener(listener);
350 }
351 mStateAnimation.play(reveal);
352 }
353
354 mStateAnimation.addListener(new AnimatorListenerAdapter() {
355 @Override
356 public void onAnimationEnd(Animator animation) {
357 dispatchOnLauncherTransitionEnd(fromView, animated, false);
358 dispatchOnLauncherTransitionEnd(toView, animated, false);
359
360 // Hide the reveal view
361 revealView.setVisibility(View.INVISIBLE);
Winson Chungb745afb2015-03-02 11:51:23 -0800362
363 // Disable all necessary layers
364 for (View v : layerViews.keySet()) {
365 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
366 v.setLayerType(View.LAYER_TYPE_NONE, null);
367 }
368 }
369
Winson Chung0f785722015-04-08 10:27:49 -0700370 if (hideSearchBar) {
371 mCb.onStateTransitionHideSearchBar();
372 }
Winson Chungb745afb2015-03-02 11:51:23 -0800373
374 // This can hold unnecessary references to views.
375 mStateAnimation = null;
Winson Chung76648c52015-07-10 14:33:23 -0700376 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800377 }
378
379 });
380
381 // Play the workspace animation
382 if (workspaceAnim != null) {
383 mStateAnimation.play(workspaceAnim);
384 }
385
386 // Dispatch the prepare transition signal
387 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
388 dispatchOnLauncherTransitionPrepare(toView, animated, false);
389
390
391 final AnimatorSet stateAnimation = mStateAnimation;
392 final Runnable startAnimRunnable = new Runnable() {
393 public void run() {
394 // Check that mStateAnimation hasn't changed while
395 // we waited for a layout/draw pass
396 if (mStateAnimation != stateAnimation)
397 return;
398 dispatchOnLauncherTransitionStart(fromView, animated, false);
399 dispatchOnLauncherTransitionStart(toView, animated, false);
400
401 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700402 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800403 for (View v : layerViews.keySet()) {
404 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
405 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
406 }
Winson Chung11509ad2015-05-12 18:55:26 -0700407 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800408 v.buildLayer();
409 }
410 }
411
412 // Focus the new view
413 toView.requestFocus();
414
415 mStateAnimation.start();
416 }
417 };
Winson Chungb745afb2015-03-02 11:51:23 -0800418 toView.bringToFront();
419 toView.setVisibility(View.VISIBLE);
420 toView.post(startAnimRunnable);
421 } else {
422 toView.setTranslationX(0.0f);
423 toView.setTranslationY(0.0f);
424 toView.setScaleX(1.0f);
425 toView.setScaleY(1.0f);
426 toView.setVisibility(View.VISIBLE);
427 toView.bringToFront();
428
429 // Show the content view
430 contentView.setVisibility(View.VISIBLE);
431
Winson Chung0f785722015-04-08 10:27:49 -0700432 if (hideSearchBar) {
433 mCb.onStateTransitionHideSearchBar();
434 }
Winson Chungb745afb2015-03-02 11:51:23 -0800435
436 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
437 dispatchOnLauncherTransitionStart(fromView, animated, false);
438 dispatchOnLauncherTransitionEnd(fromView, animated, false);
439 dispatchOnLauncherTransitionPrepare(toView, animated, false);
440 dispatchOnLauncherTransitionStart(toView, animated, false);
441 dispatchOnLauncherTransitionEnd(toView, animated, false);
Winson Chung76648c52015-07-10 14:33:23 -0700442 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800443 }
444 }
445
446 /**
447 * Starts and animation to the workspace from the apps view.
448 */
Winson Chungef7f8742015-06-04 17:18:17 -0700449 private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
450 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700451 AllAppsContainerView appsView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800452 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
453 int[] mAllAppsToPanelDelta;
454
455 @Override
456 public void onRevealViewVisible(View revealView, View contentView,
457 View allAppsButtonView) {
458 // Get the y delta between the center of the page and the center of the all apps
459 // button
460 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
461 allAppsButtonView, null);
462 }
463 @Override
464 public float getMaterialRevealViewFinalXDrift(View revealView) {
465 return mAllAppsToPanelDelta[0];
466 }
467 @Override
468 public float getMaterialRevealViewFinalYDrift(View revealView) {
469 return mAllAppsToPanelDelta[1];
470 }
471 @Override
472 float getMaterialRevealViewFinalAlpha(View revealView) {
473 // No alpha anim from all apps
474 return 1f;
475 }
476 @Override
477 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700478 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800479 return allAppsButtonSize / 2;
480 }
481 @Override
482 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
483 final View revealView, final View allAppsButtonView) {
484 return new AnimatorListenerAdapter() {
485 public void onAnimationStart(Animator animation) {
486 // We set the alpha instead of visibility to ensure that the focus does not
487 // get taken from the all apps view
488 allAppsButtonView.setVisibility(View.VISIBLE);
489 allAppsButtonView.setAlpha(0f);
490 }
491 public void onAnimationEnd(Animator animation) {
492 // Hide the reveal view
493 revealView.setVisibility(View.INVISIBLE);
494
495 // Show the all apps button, and focus it
496 allAppsButtonView.setAlpha(1f);
497 }
498 };
499 }
500 };
Winson Chungef7f8742015-06-04 17:18:17 -0700501 // Only animate the search bar if animating to spring loaded mode from all apps
Winson Chungdc61c4d2015-04-20 18:26:57 -0700502 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
Winson Chungef7f8742015-06-04 17:18:17 -0700503 appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(),
504 animated, onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800505 }
506
507 /**
508 * Starts and animation to the workspace from the widgets view.
509 */
Winson Chungef7f8742015-06-04 17:18:17 -0700510 private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
511 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700512 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
513 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800514 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
515 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700516 public void onRevealViewVisible(View revealView, View contentView,
517 View allAppsButtonView) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700518 }
519 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800520 public float getMaterialRevealViewFinalYDrift(View revealView) {
521 return revealView.getMeasuredHeight() / 2;
522 }
523 @Override
524 float getMaterialRevealViewFinalAlpha(View revealView) {
525 return 0.4f;
526 }
527 @Override
528 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
529 final View revealView, final View allAppsButtonView) {
530 return new AnimatorListenerAdapter() {
531 public void onAnimationEnd(Animator animation) {
532 // Hide the reveal view
533 revealView.setVisibility(View.INVISIBLE);
534 }
535 };
536 }
537 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700538 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
Winson Chungef7f8742015-06-04 17:18:17 -0700539 widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
Winson Chung0f785722015-04-08 10:27:49 -0700540 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800541 }
542
543 /**
544 * Creates and starts a new animation to the workspace.
545 */
546 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700547 final int toWorkspacePage, final View fromView, final View contentView,
Winson Chungef7f8742015-06-04 17:18:17 -0700548 final View revealView, final View overlaySearchBarView, final boolean animated,
549 final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800550 final Resources res = mLauncher.getResources();
551 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700552 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800553 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700554 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800555
556 final View allAppsButtonView = mLauncher.getAllAppsButton();
557 final View toView = mLauncher.getWorkspace();
558
559 final HashMap<View, Integer> layerViews = new HashMap<>();
560
561 // If for some reason our views aren't initialized, don't animate
562 boolean initialized = allAppsButtonView != null;
563
564 // Cancel the current animation
565 cancelAnimation();
566
567 // Create the workspace animation.
568 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700569 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
Winson Chungef7f8742015-06-04 17:18:17 -0700570 toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
571 layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800572
573 if (animated && initialized) {
574 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
575
576 // Play the workspace animation
577 if (workspaceAnim != null) {
578 mStateAnimation.play(workspaceAnim);
579 }
580
581 // hideAppsCustomizeHelper is called in some cases when it is already hidden
582 // don't perform all these no-op animations. In particularly, this was causing
583 // the all-apps button to pop in and out.
584 if (fromView.getVisibility() == View.VISIBLE) {
585 int width = revealView.getMeasuredWidth();
586 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700587 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800588 revealView.setVisibility(View.VISIBLE);
589 revealView.setAlpha(1f);
590 revealView.setTranslationY(0);
591 layerViews.put(revealView, BUILD_AND_SET_LAYER);
592 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
593
594 // Calculate the final animation values
595 final float revealViewToXDrift;
596 final float revealViewToYDrift;
597 if (material) {
598 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
599 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
600 } else {
601 revealViewToYDrift = 2 * height / 3;
602 revealViewToXDrift = 0;
603 }
604
605 // The vertical motion of the apps panel should be delayed by one frame
606 // from the conceal animation in order to give the right feel. We correspondingly
607 // shorten the duration so that the slide and conceal end at the same time.
608 TimeInterpolator decelerateInterpolator = material ?
609 new LogDecelerateInterpolator(100, 0) :
610 new DecelerateInterpolator(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700611 ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800612 0, revealViewToYDrift);
613 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
614 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
615 panelDriftY.setInterpolator(decelerateInterpolator);
616 mStateAnimation.play(panelDriftY);
617
Adam Cohen15588932015-05-26 23:03:31 -0700618 ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
Winson Chungb745afb2015-03-02 11:51:23 -0800619 0, revealViewToXDrift);
620 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
621 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
622 panelDriftX.setInterpolator(decelerateInterpolator);
623 mStateAnimation.play(panelDriftX);
624
625 // Setup animation for the reveal panel alpha
626 final float revealViewToAlpha = !material ? 0f :
627 pCb.getMaterialRevealViewFinalAlpha(revealView);
628 if (revealViewToAlpha != 1f) {
Adam Cohen15588932015-05-26 23:03:31 -0700629 ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
Winson Chungb745afb2015-03-02 11:51:23 -0800630 1f, revealViewToAlpha);
631 panelAlpha.setDuration(material ? revealDuration : 150);
632 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
633 panelAlpha.setInterpolator(decelerateInterpolator);
634 mStateAnimation.play(panelAlpha);
635 }
636
637 // Setup the animation for the content view
638 layerViews.put(contentView, BUILD_AND_SET_LAYER);
639
640 // Create the individual animators
Adam Cohen15588932015-05-26 23:03:31 -0700641 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800642 0, revealViewToYDrift);
643 contentView.setTranslationY(0);
644 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
645 pageDrift.setInterpolator(decelerateInterpolator);
646 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
647 mStateAnimation.play(pageDrift);
648
649 contentView.setAlpha(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700650 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800651 itemsAlpha.setDuration(100);
652 itemsAlpha.setInterpolator(decelerateInterpolator);
653 mStateAnimation.play(itemsAlpha);
654
Winson Chungef7f8742015-06-04 17:18:17 -0700655 if (overlaySearchBarView != null) {
656 overlaySearchBarView.setAlpha(1f);
657 ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
658 searchAlpha.setDuration(material ? 100 : 150);
659 searchAlpha.setInterpolator(decelerateInterpolator);
660 searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
661 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
662 mStateAnimation.play(searchAlpha);
663 }
664
Winson Chungb745afb2015-03-02 11:51:23 -0800665 if (material) {
666 // Animate the all apps button
667 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
668 AnimatorListenerAdapter listener =
669 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
Adam Cohen15588932015-05-26 23:03:31 -0700670 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
671 height / 2, revealRadius, finalRadius);
Winson Chungb745afb2015-03-02 11:51:23 -0800672 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
673 reveal.setDuration(revealDuration);
674 reveal.setStartDelay(itemsAlphaStagger);
675 if (listener != null) {
676 reveal.addListener(listener);
677 }
678 mStateAnimation.play(reveal);
679 }
680
681 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
682 dispatchOnLauncherTransitionPrepare(toView, animated, true);
683 }
684
685 mStateAnimation.addListener(new AnimatorListenerAdapter() {
686 @Override
687 public void onAnimationEnd(Animator animation) {
688 fromView.setVisibility(View.GONE);
689 dispatchOnLauncherTransitionEnd(fromView, animated, true);
690 dispatchOnLauncherTransitionEnd(toView, animated, true);
691
692 // Run any queued runnables
693 if (onCompleteRunnable != null) {
694 onCompleteRunnable.run();
695 }
696
Winson Chungb745afb2015-03-02 11:51:23 -0800697 // Disable all necessary layers
698 for (View v : layerViews.keySet()) {
699 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
700 v.setLayerType(View.LAYER_TYPE_NONE, null);
701 }
702 }
703
704 // Reset page transforms
705 if (contentView != null) {
706 contentView.setTranslationX(0);
707 contentView.setTranslationY(0);
708 contentView.setAlpha(1);
709 }
Winson Chungef7f8742015-06-04 17:18:17 -0700710 if (overlaySearchBarView != null) {
711 overlaySearchBarView.setAlpha(1f);
712 }
Winson Chungb745afb2015-03-02 11:51:23 -0800713
714 // This can hold unnecessary references to views.
715 mStateAnimation = null;
Winson Chung76648c52015-07-10 14:33:23 -0700716 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800717 }
718 });
719
720 final AnimatorSet stateAnimation = mStateAnimation;
721 final Runnable startAnimRunnable = new Runnable() {
722 public void run() {
723 // Check that mStateAnimation hasn't changed while
724 // we waited for a layout/draw pass
725 if (mStateAnimation != stateAnimation)
726 return;
727 dispatchOnLauncherTransitionStart(fromView, animated, false);
728 dispatchOnLauncherTransitionStart(toView, animated, false);
729
730 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700731 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800732 for (View v : layerViews.keySet()) {
733 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
734 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
735 }
Winson Chung11509ad2015-05-12 18:55:26 -0700736 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800737 v.buildLayer();
738 }
739 }
740 mStateAnimation.start();
741 }
742 };
743 fromView.post(startAnimRunnable);
744 } else {
745 fromView.setVisibility(View.GONE);
746 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
747 dispatchOnLauncherTransitionStart(fromView, animated, true);
748 dispatchOnLauncherTransitionEnd(fromView, animated, true);
749 dispatchOnLauncherTransitionPrepare(toView, animated, true);
750 dispatchOnLauncherTransitionStart(toView, animated, true);
751 dispatchOnLauncherTransitionEnd(toView, animated, true);
Winson Chung76648c52015-07-10 14:33:23 -0700752 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800753
754 // Run any queued runnables
755 if (onCompleteRunnable != null) {
756 onCompleteRunnable.run();
757 }
758 }
759 }
760
761
762 /**
763 * Dispatches the prepare-transition event to suitable views.
764 */
765 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
766 if (v instanceof LauncherTransitionable) {
767 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
768 toWorkspace);
769 }
770 }
771
772 /**
773 * Dispatches the start-transition event to suitable views.
774 */
775 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
776 if (v instanceof LauncherTransitionable) {
777 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
778 toWorkspace);
779 }
780
781 // Update the workspace transition step as well
782 dispatchOnLauncherTransitionStep(v, 0f);
783 }
784
785 /**
786 * Dispatches the step-transition event to suitable views.
787 */
788 void dispatchOnLauncherTransitionStep(View v, float t) {
789 if (v instanceof LauncherTransitionable) {
790 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
791 }
792 }
793
794 /**
795 * Dispatches the end-transition event to suitable views.
796 */
797 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
798 if (v instanceof LauncherTransitionable) {
799 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
800 toWorkspace);
801 }
802
803 // Update the workspace transition step as well
804 dispatchOnLauncherTransitionStep(v, 1f);
805 }
806
807 /**
808 * Cancels the current animation.
809 */
810 private void cancelAnimation() {
811 if (mStateAnimation != null) {
812 mStateAnimation.setDuration(0);
813 mStateAnimation.cancel();
814 mStateAnimation = null;
815 }
816 }
Adam Cohen15588932015-05-26 23:03:31 -0700817}