blob: d69b7432dec5cd8cfd07a6f60ef5fa32ee89c5ff [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 {
Winson Chungb745afb2015-03-02 11:51:23 -080093 float getMaterialRevealViewFinalAlpha(View revealView) {
94 return 0;
95 }
Winson Chungb745afb2015-03-02 11:51:23 -080096 float getMaterialRevealViewStartFinalRadius() {
97 return 0;
98 }
99 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700100 View buttonView) {
Winson Chungb745afb2015-03-02 11:51:23 -0800101 return null;
102 }
Winson Chung76648c52015-07-10 14:33:23 -0700103 void onTransitionComplete() {}
Winson Chungb745afb2015-03-02 11:51:23 -0800104 }
105
106 public static final String TAG = "LauncherStateTransitionAnimation";
107
108 // Flags to determine how to set the layers on views before the transition animation
109 public static final int BUILD_LAYER = 0;
110 public static final int BUILD_AND_SET_LAYER = 1;
111 public static final int SINGLE_FRAME_DELAY = 16;
112
Adam Cohen091440a2015-03-18 14:16:05 -0700113 @Thunk Launcher mLauncher;
114 @Thunk Callbacks mCb;
115 @Thunk AnimatorSet mStateAnimation;
Winson Chungb745afb2015-03-02 11:51:23 -0800116
117 public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
118 mLauncher = l;
119 mCb = cb;
120 }
121
122 /**
123 * Starts an animation to the apps view.
Winson Chung76648c52015-07-10 14:33:23 -0700124 *
125 * @param startSearchAfterTransition Immediately starts app search after the transition to
126 * All Apps is completed.
Winson Chungb745afb2015-03-02 11:51:23 -0800127 */
Winson Chung76648c52015-07-10 14:33:23 -0700128 public void startAnimationToAllApps(final boolean animated,
129 final boolean startSearchAfterTransition) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700130 final AllAppsContainerView toView = mLauncher.getAppsView();
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700131 final View buttonView = mLauncher.getAllAppsButton();
Winson Chungb745afb2015-03-02 11:51:23 -0800132 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
Winson Chungb745afb2015-03-02 11:51:23 -0800133 @Override
134 public float getMaterialRevealViewFinalAlpha(View revealView) {
135 return 1f;
136 }
137 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800138 public float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700139 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800140 return allAppsButtonSize / 2;
141 }
142 @Override
143 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
144 final View revealView, final View allAppsButtonView) {
145 return new AnimatorListenerAdapter() {
146 public void onAnimationStart(Animator animation) {
147 allAppsButtonView.setVisibility(View.INVISIBLE);
148 }
149 public void onAnimationEnd(Animator animation) {
150 allAppsButtonView.setVisibility(View.VISIBLE);
151 }
152 };
153 }
Winson Chung76648c52015-07-10 14:33:23 -0700154 @Override
155 void onTransitionComplete() {
156 if (startSearchAfterTransition) {
157 toView.startAppsSearch();
158 }
159 }
Winson Chungb745afb2015-03-02 11:51:23 -0800160 };
Winson Chungef7f8742015-06-04 17:18:17 -0700161 // Only animate the search bar if animating from spring loaded mode back to all apps
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700162 startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, buttonView, toView,
163 toView.getContentView(), toView.getRevealView(), toView.getSearchBarView(),
164 animated, true /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800165 }
166
167 /**
168 * Starts an animation to the widgets view.
169 */
170 public void startAnimationToWidgets(final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700171 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700172 final View buttonView = mLauncher.getWidgetsButton();
173
Winson Chungb745afb2015-03-02 11:51:23 -0800174 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
175 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800176 public float getMaterialRevealViewFinalAlpha(View revealView) {
177 return 0.3f;
178 }
Winson Chungb745afb2015-03-02 11:51:23 -0800179 };
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700180 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, buttonView, toView,
Winson Chungef7f8742015-06-04 17:18:17 -0700181 toView.getContentView(), toView.getRevealView(), null, animated,
182 true /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800183 }
184
185 /**
186 * Starts and animation to the workspace from the current overlay view.
187 */
188 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700189 final Workspace.State toWorkspaceState, final int toWorkspacePage,
190 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800191 if (toWorkspaceState != Workspace.State.NORMAL &&
192 toWorkspaceState != Workspace.State.SPRING_LOADED &&
193 toWorkspaceState != Workspace.State.OVERVIEW) {
194 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
195 }
196
197 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chungef7f8742015-06-04 17:18:17 -0700198 startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700199 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800200 } else {
Winson Chungef7f8742015-06-04 17:18:17 -0700201 startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700202 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800203 }
204 }
205
206 /**
207 * Creates and starts a new animation to a particular overlay view.
208 */
Sunny Goyal70660032015-05-14 00:07:08 -0700209 @SuppressLint("NewApi")
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700210 private void startAnimationToOverlay(final Workspace.State toWorkspaceState,
211 final View buttonView, final View toView, final View contentView, final View revealView,
212 final View overlaySearchBarView, final boolean animated, final boolean hideSearchBar,
213 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800214 final Resources res = mLauncher.getResources();
215 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700216 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800217 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700218 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800219
Winson Chungb745afb2015-03-02 11:51:23 -0800220 final View fromView = mLauncher.getWorkspace();
221
222 final HashMap<View, Integer> layerViews = new HashMap<>();
223
224 // If for some reason our views aren't initialized, don't animate
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700225 boolean initialized = buttonView != null;
Winson Chungb745afb2015-03-02 11:51:23 -0800226
227 // Cancel the current animation
228 cancelAnimation();
229
230 // Create the workspace animation.
231 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700232 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
Winson Chungef7f8742015-06-04 17:18:17 -0700233 animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800234
235 if (animated && initialized) {
236 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
237
238 // Setup the reveal view animation
239 int width = revealView.getMeasuredWidth();
240 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700241 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800242 revealView.setVisibility(View.VISIBLE);
243 revealView.setAlpha(0f);
244 revealView.setTranslationY(0f);
245 revealView.setTranslationX(0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800246
247 // Calculate the final animation values
248 final float revealViewToAlpha;
249 final float revealViewToXDrift;
250 final float revealViewToYDrift;
251 if (material) {
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700252 int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
253 buttonView, null);
Winson Chungb745afb2015-03-02 11:51:23 -0800254 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700255 revealViewToYDrift = buttonViewToPanelDelta[1];
256 revealViewToXDrift = buttonViewToPanelDelta[0];
Winson Chungb745afb2015-03-02 11:51:23 -0800257 } else {
258 revealViewToAlpha = 0f;
259 revealViewToYDrift = 2 * height / 3;
260 revealViewToXDrift = 0;
261 }
262
263 // Create the animators
264 PropertyValuesHolder panelAlpha =
265 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
266 PropertyValuesHolder panelDriftY =
267 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
268 PropertyValuesHolder panelDriftX =
269 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
270 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
271 panelAlpha, panelDriftY, panelDriftX);
272 panelAlphaAndDrift.setDuration(revealDuration);
273 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
274
275 // Play the animation
276 layerViews.put(revealView, BUILD_AND_SET_LAYER);
277 mStateAnimation.play(panelAlphaAndDrift);
278
Winson Chungef7f8742015-06-04 17:18:17 -0700279 if (overlaySearchBarView != null) {
280 overlaySearchBarView.setAlpha(0f);
281 ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
282 searchBarAlpha.setDuration(100);
283 searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
284 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
285 mStateAnimation.play(searchBarAlpha);
286 }
287
Winson Chungb745afb2015-03-02 11:51:23 -0800288 // Setup the animation for the content view
289 contentView.setVisibility(View.VISIBLE);
290 contentView.setAlpha(0f);
291 contentView.setTranslationY(revealViewToYDrift);
292 layerViews.put(contentView, BUILD_AND_SET_LAYER);
293
294 // Create the individual animators
295 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
296 revealViewToYDrift, 0);
297 pageDrift.setDuration(revealDuration);
298 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
299 pageDrift.setStartDelay(itemsAlphaStagger);
300 mStateAnimation.play(pageDrift);
301
302 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
303 itemsAlpha.setDuration(revealDuration);
304 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
305 itemsAlpha.setStartDelay(itemsAlphaStagger);
306 mStateAnimation.play(itemsAlpha);
307
308 if (material) {
Winson Chungb745afb2015-03-02 11:51:23 -0800309 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
310 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700311 revealView, buttonView);
Adam Cohen15588932015-05-26 23:03:31 -0700312 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
Winson Chungb745afb2015-03-02 11:51:23 -0800313 height / 2, startRadius, revealRadius);
314 reveal.setDuration(revealDuration);
315 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
316 if (listener != null) {
317 reveal.addListener(listener);
318 }
319 mStateAnimation.play(reveal);
320 }
321
322 mStateAnimation.addListener(new AnimatorListenerAdapter() {
323 @Override
324 public void onAnimationEnd(Animator animation) {
325 dispatchOnLauncherTransitionEnd(fromView, animated, false);
326 dispatchOnLauncherTransitionEnd(toView, animated, false);
327
328 // Hide the reveal view
329 revealView.setVisibility(View.INVISIBLE);
Winson Chungb745afb2015-03-02 11:51:23 -0800330
331 // Disable all necessary layers
332 for (View v : layerViews.keySet()) {
333 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
334 v.setLayerType(View.LAYER_TYPE_NONE, null);
335 }
336 }
337
Winson Chung0f785722015-04-08 10:27:49 -0700338 if (hideSearchBar) {
339 mCb.onStateTransitionHideSearchBar();
340 }
Winson Chungb745afb2015-03-02 11:51:23 -0800341
342 // This can hold unnecessary references to views.
343 mStateAnimation = null;
Winson Chung76648c52015-07-10 14:33:23 -0700344 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800345 }
346
347 });
348
349 // Play the workspace animation
350 if (workspaceAnim != null) {
351 mStateAnimation.play(workspaceAnim);
352 }
353
354 // Dispatch the prepare transition signal
355 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
356 dispatchOnLauncherTransitionPrepare(toView, animated, false);
357
358
359 final AnimatorSet stateAnimation = mStateAnimation;
360 final Runnable startAnimRunnable = new Runnable() {
361 public void run() {
362 // Check that mStateAnimation hasn't changed while
363 // we waited for a layout/draw pass
364 if (mStateAnimation != stateAnimation)
365 return;
366 dispatchOnLauncherTransitionStart(fromView, animated, false);
367 dispatchOnLauncherTransitionStart(toView, animated, false);
368
369 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700370 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800371 for (View v : layerViews.keySet()) {
372 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
373 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
374 }
Winson Chung11509ad2015-05-12 18:55:26 -0700375 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800376 v.buildLayer();
377 }
378 }
379
380 // Focus the new view
381 toView.requestFocus();
382
383 mStateAnimation.start();
384 }
385 };
Winson Chungb745afb2015-03-02 11:51:23 -0800386 toView.bringToFront();
387 toView.setVisibility(View.VISIBLE);
388 toView.post(startAnimRunnable);
389 } else {
390 toView.setTranslationX(0.0f);
391 toView.setTranslationY(0.0f);
392 toView.setScaleX(1.0f);
393 toView.setScaleY(1.0f);
394 toView.setVisibility(View.VISIBLE);
395 toView.bringToFront();
396
397 // Show the content view
398 contentView.setVisibility(View.VISIBLE);
399
Winson Chung0f785722015-04-08 10:27:49 -0700400 if (hideSearchBar) {
401 mCb.onStateTransitionHideSearchBar();
402 }
Winson Chungb745afb2015-03-02 11:51:23 -0800403
404 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
405 dispatchOnLauncherTransitionStart(fromView, animated, false);
406 dispatchOnLauncherTransitionEnd(fromView, animated, false);
407 dispatchOnLauncherTransitionPrepare(toView, animated, false);
408 dispatchOnLauncherTransitionStart(toView, animated, false);
409 dispatchOnLauncherTransitionEnd(toView, animated, false);
Winson Chung76648c52015-07-10 14:33:23 -0700410 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800411 }
412 }
413
414 /**
415 * Starts and animation to the workspace from the apps view.
416 */
Winson Chungef7f8742015-06-04 17:18:17 -0700417 private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
418 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700419 AllAppsContainerView appsView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800420 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
421 int[] mAllAppsToPanelDelta;
422
423 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800424 float getMaterialRevealViewFinalAlpha(View revealView) {
425 // No alpha anim from all apps
426 return 1f;
427 }
428 @Override
429 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700430 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800431 return allAppsButtonSize / 2;
432 }
433 @Override
434 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
435 final View revealView, final View allAppsButtonView) {
436 return new AnimatorListenerAdapter() {
437 public void onAnimationStart(Animator animation) {
438 // We set the alpha instead of visibility to ensure that the focus does not
439 // get taken from the all apps view
440 allAppsButtonView.setVisibility(View.VISIBLE);
441 allAppsButtonView.setAlpha(0f);
442 }
443 public void onAnimationEnd(Animator animation) {
444 // Hide the reveal view
445 revealView.setVisibility(View.INVISIBLE);
446
447 // Show the all apps button, and focus it
448 allAppsButtonView.setAlpha(1f);
449 }
450 };
451 }
452 };
Winson Chungef7f8742015-06-04 17:18:17 -0700453 // Only animate the search bar if animating to spring loaded mode from all apps
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700454 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage,
455 mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
456 appsView.getRevealView(), appsView.getSearchBarView(), animated,
457 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800458 }
459
460 /**
461 * Starts and animation to the workspace from the widgets view.
462 */
Winson Chungef7f8742015-06-04 17:18:17 -0700463 private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
464 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700465 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800466 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
467 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800468 float getMaterialRevealViewFinalAlpha(View revealView) {
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700469 return 0.3f;
Winson Chungb745afb2015-03-02 11:51:23 -0800470 }
471 @Override
472 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700473 final View revealView, final View widgetsButtonView) {
Winson Chungb745afb2015-03-02 11:51:23 -0800474 return new AnimatorListenerAdapter() {
475 public void onAnimationEnd(Animator animation) {
476 // Hide the reveal view
477 revealView.setVisibility(View.INVISIBLE);
478 }
479 };
480 }
481 };
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700482 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage,
483 mLauncher.getWidgetsButton(), widgetsView, widgetsView.getContentView(),
484 widgetsView.getRevealView(), null, animated, onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800485 }
486
487 /**
488 * Creates and starts a new animation to the workspace.
489 */
490 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700491 final int toWorkspacePage, final View buttonView, final View fromView,
492 final View contentView, final View revealView, final View overlaySearchBarView,
493 final boolean animated, final Runnable onCompleteRunnable,
494 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800495 final Resources res = mLauncher.getResources();
496 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700497 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800498 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700499 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800500
Winson Chungb745afb2015-03-02 11:51:23 -0800501 final View toView = mLauncher.getWorkspace();
502
503 final HashMap<View, Integer> layerViews = new HashMap<>();
504
505 // If for some reason our views aren't initialized, don't animate
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700506 boolean initialized = buttonView != null;
Winson Chungb745afb2015-03-02 11:51:23 -0800507
508 // Cancel the current animation
509 cancelAnimation();
510
511 // Create the workspace animation.
512 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700513 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
Winson Chungef7f8742015-06-04 17:18:17 -0700514 toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
515 layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800516
517 if (animated && initialized) {
518 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
519
520 // Play the workspace animation
521 if (workspaceAnim != null) {
522 mStateAnimation.play(workspaceAnim);
523 }
524
525 // hideAppsCustomizeHelper is called in some cases when it is already hidden
526 // don't perform all these no-op animations. In particularly, this was causing
527 // the all-apps button to pop in and out.
528 if (fromView.getVisibility() == View.VISIBLE) {
529 int width = revealView.getMeasuredWidth();
530 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700531 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800532 revealView.setVisibility(View.VISIBLE);
533 revealView.setAlpha(1f);
534 revealView.setTranslationY(0);
535 layerViews.put(revealView, BUILD_AND_SET_LAYER);
Winson Chungb745afb2015-03-02 11:51:23 -0800536
537 // Calculate the final animation values
538 final float revealViewToXDrift;
539 final float revealViewToYDrift;
540 if (material) {
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700541 int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
542 buttonView, null);
543 revealViewToYDrift = buttonViewToPanelDelta[1];
544 revealViewToXDrift = buttonViewToPanelDelta[0];
Winson Chungb745afb2015-03-02 11:51:23 -0800545 } else {
546 revealViewToYDrift = 2 * height / 3;
547 revealViewToXDrift = 0;
548 }
549
550 // The vertical motion of the apps panel should be delayed by one frame
551 // from the conceal animation in order to give the right feel. We correspondingly
552 // shorten the duration so that the slide and conceal end at the same time.
553 TimeInterpolator decelerateInterpolator = material ?
554 new LogDecelerateInterpolator(100, 0) :
555 new DecelerateInterpolator(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700556 ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800557 0, revealViewToYDrift);
558 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
559 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
560 panelDriftY.setInterpolator(decelerateInterpolator);
561 mStateAnimation.play(panelDriftY);
562
Adam Cohen15588932015-05-26 23:03:31 -0700563 ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
Winson Chungb745afb2015-03-02 11:51:23 -0800564 0, revealViewToXDrift);
565 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
566 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
567 panelDriftX.setInterpolator(decelerateInterpolator);
568 mStateAnimation.play(panelDriftX);
569
570 // Setup animation for the reveal panel alpha
571 final float revealViewToAlpha = !material ? 0f :
572 pCb.getMaterialRevealViewFinalAlpha(revealView);
573 if (revealViewToAlpha != 1f) {
Adam Cohen15588932015-05-26 23:03:31 -0700574 ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
Winson Chungb745afb2015-03-02 11:51:23 -0800575 1f, revealViewToAlpha);
576 panelAlpha.setDuration(material ? revealDuration : 150);
577 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
578 panelAlpha.setInterpolator(decelerateInterpolator);
579 mStateAnimation.play(panelAlpha);
580 }
581
582 // Setup the animation for the content view
583 layerViews.put(contentView, BUILD_AND_SET_LAYER);
584
585 // Create the individual animators
Adam Cohen15588932015-05-26 23:03:31 -0700586 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800587 0, revealViewToYDrift);
588 contentView.setTranslationY(0);
589 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
590 pageDrift.setInterpolator(decelerateInterpolator);
591 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
592 mStateAnimation.play(pageDrift);
593
594 contentView.setAlpha(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700595 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800596 itemsAlpha.setDuration(100);
597 itemsAlpha.setInterpolator(decelerateInterpolator);
598 mStateAnimation.play(itemsAlpha);
599
Winson Chungef7f8742015-06-04 17:18:17 -0700600 if (overlaySearchBarView != null) {
601 overlaySearchBarView.setAlpha(1f);
602 ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
603 searchAlpha.setDuration(material ? 100 : 150);
604 searchAlpha.setInterpolator(decelerateInterpolator);
605 searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
606 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
607 mStateAnimation.play(searchAlpha);
608 }
609
Winson Chungb745afb2015-03-02 11:51:23 -0800610 if (material) {
611 // Animate the all apps button
612 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
613 AnimatorListenerAdapter listener =
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700614 pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView);
Adam Cohen15588932015-05-26 23:03:31 -0700615 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
616 height / 2, revealRadius, finalRadius);
Winson Chungb745afb2015-03-02 11:51:23 -0800617 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
618 reveal.setDuration(revealDuration);
619 reveal.setStartDelay(itemsAlphaStagger);
620 if (listener != null) {
621 reveal.addListener(listener);
622 }
623 mStateAnimation.play(reveal);
624 }
625
626 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
627 dispatchOnLauncherTransitionPrepare(toView, animated, true);
628 }
629
630 mStateAnimation.addListener(new AnimatorListenerAdapter() {
631 @Override
632 public void onAnimationEnd(Animator animation) {
633 fromView.setVisibility(View.GONE);
634 dispatchOnLauncherTransitionEnd(fromView, animated, true);
635 dispatchOnLauncherTransitionEnd(toView, animated, true);
636
637 // Run any queued runnables
638 if (onCompleteRunnable != null) {
639 onCompleteRunnable.run();
640 }
641
Winson Chungb745afb2015-03-02 11:51:23 -0800642 // Disable all necessary layers
643 for (View v : layerViews.keySet()) {
644 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
645 v.setLayerType(View.LAYER_TYPE_NONE, null);
646 }
647 }
648
649 // Reset page transforms
650 if (contentView != null) {
651 contentView.setTranslationX(0);
652 contentView.setTranslationY(0);
653 contentView.setAlpha(1);
654 }
Winson Chungef7f8742015-06-04 17:18:17 -0700655 if (overlaySearchBarView != null) {
656 overlaySearchBarView.setAlpha(1f);
657 }
Winson Chungb745afb2015-03-02 11:51:23 -0800658
659 // This can hold unnecessary references to views.
660 mStateAnimation = null;
Winson Chung76648c52015-07-10 14:33:23 -0700661 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800662 }
663 });
664
665 final AnimatorSet stateAnimation = mStateAnimation;
666 final Runnable startAnimRunnable = new Runnable() {
667 public void run() {
668 // Check that mStateAnimation hasn't changed while
669 // we waited for a layout/draw pass
670 if (mStateAnimation != stateAnimation)
671 return;
672 dispatchOnLauncherTransitionStart(fromView, animated, false);
673 dispatchOnLauncherTransitionStart(toView, animated, false);
674
675 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700676 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800677 for (View v : layerViews.keySet()) {
678 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
679 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
680 }
Winson Chung11509ad2015-05-12 18:55:26 -0700681 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800682 v.buildLayer();
683 }
684 }
685 mStateAnimation.start();
686 }
687 };
688 fromView.post(startAnimRunnable);
689 } else {
690 fromView.setVisibility(View.GONE);
691 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
692 dispatchOnLauncherTransitionStart(fromView, animated, true);
693 dispatchOnLauncherTransitionEnd(fromView, animated, true);
694 dispatchOnLauncherTransitionPrepare(toView, animated, true);
695 dispatchOnLauncherTransitionStart(toView, animated, true);
696 dispatchOnLauncherTransitionEnd(toView, animated, true);
Winson Chung76648c52015-07-10 14:33:23 -0700697 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800698
699 // Run any queued runnables
700 if (onCompleteRunnable != null) {
701 onCompleteRunnable.run();
702 }
703 }
704 }
705
706
707 /**
708 * Dispatches the prepare-transition event to suitable views.
709 */
710 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
711 if (v instanceof LauncherTransitionable) {
712 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
713 toWorkspace);
714 }
715 }
716
717 /**
718 * Dispatches the start-transition event to suitable views.
719 */
720 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
721 if (v instanceof LauncherTransitionable) {
722 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
723 toWorkspace);
724 }
725
726 // Update the workspace transition step as well
727 dispatchOnLauncherTransitionStep(v, 0f);
728 }
729
730 /**
731 * Dispatches the step-transition event to suitable views.
732 */
733 void dispatchOnLauncherTransitionStep(View v, float t) {
734 if (v instanceof LauncherTransitionable) {
735 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
736 }
737 }
738
739 /**
740 * Dispatches the end-transition event to suitable views.
741 */
742 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
743 if (v instanceof LauncherTransitionable) {
744 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
745 toWorkspace);
746 }
747
748 // Update the workspace transition step as well
749 dispatchOnLauncherTransitionStep(v, 1f);
750 }
751
752 /**
753 * Cancels the current animation.
754 */
755 private void cancelAnimation() {
756 if (mStateAnimation != null) {
757 mStateAnimation.setDuration(0);
758 mStateAnimation.cancel();
759 mStateAnimation = null;
760 }
761 }
Adam Cohen15588932015-05-26 23:03:31 -0700762}