blob: e94a2ac0b2e4552061b3d705ae5fdd99dfcd1f3a [file] [log] [blame]
Winson Chungb745afb2015-03-02 11:51:23 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.launcher3;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.AnimatorSet;
22import android.animation.ObjectAnimator;
23import android.animation.PropertyValuesHolder;
24import android.animation.TimeInterpolator;
Sunny Goyal70660032015-05-14 00:07:08 -070025import android.annotation.SuppressLint;
Winson Chungb745afb2015-03-02 11:51:23 -080026import android.content.res.Resources;
27import android.util.Log;
28import android.view.View;
Winson Chungb745afb2015-03-02 11:51:23 -080029import android.view.animation.AccelerateInterpolator;
30import android.view.animation.DecelerateInterpolator;
Adam Cohen15588932015-05-26 23:03:31 -070031
Winson Chung5f4e0fd2015-05-22 11:12:27 -070032import com.android.launcher3.allapps.AllAppsContainerView;
Adam Cohen15588932015-05-26 23:03:31 -070033import com.android.launcher3.util.UiThreadCircularReveal;
Adam Cohen091440a2015-03-18 14:16:05 -070034import com.android.launcher3.util.Thunk;
Hyunyoung Song3f471442015-04-08 19:01:34 -070035import com.android.launcher3.widget.WidgetsContainerView;
Adam Cohen15588932015-05-26 23:03:31 -070036
Winson Chungb745afb2015-03-02 11:51:23 -080037import java.util.HashMap;
38
39/**
40 * TODO: figure out what kind of tests we can write for this
41 *
42 * Things to test when changing the following class.
43 * - Home from workspace
44 * - from center screen
45 * - from other screens
46 * - Home from all apps
47 * - from center screen
48 * - from other screens
49 * - Back from all apps
50 * - from center screen
51 * - from other screens
52 * - Launch app from workspace and quit
53 * - with back
54 * - with home
55 * - Launch app from all apps and quit
56 * - with back
57 * - with home
58 * - Go to a screen that's not the default, then all
59 * apps, and launch and app, and go back
60 * - with back
61 * -with home
62 * - On workspace, long press power and go back
63 * - with back
64 * - with home
65 * - On all apps, long press power and go back
66 * - with back
67 * - with home
68 * - On workspace, power off
69 * - On all apps, power off
70 * - Launch an app and turn off the screen while in that app
71 * - Go back with home key
72 * - Go back with back key TODO: make this not go to workspace
73 * - From all apps
74 * - From workspace
75 * - Enter and exit car mode (becuase it causes an extra configuration changed)
76 * - From all apps
77 * - From the center workspace
78 * - From another workspace
79 */
80public class LauncherStateTransitionAnimation {
81
82 /**
83 * Callbacks made during the state transition
84 */
85 interface Callbacks {
86 public void onStateTransitionHideSearchBar();
87 }
88
89 /**
90 * Private callbacks made during transition setup.
91 */
92 static abstract class PrivateTransitionCallbacks {
93 void onRevealViewVisible(View revealView, View contentView, View allAppsButtonView) {}
94 void onAnimationComplete(View revealView, View contentView, View allAppsButtonView) {}
95 float getMaterialRevealViewFinalAlpha(View revealView) {
96 return 0;
97 }
98 float getMaterialRevealViewFinalXDrift(View revealView) {
99 return 0;
100 }
101 float getMaterialRevealViewFinalYDrift(View revealView) {
102 return 0;
103 }
104 float getMaterialRevealViewStartFinalRadius() {
105 return 0;
106 }
107 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
108 View allAppsButtonView) {
109 return null;
110 }
111 }
112
113 public static final String TAG = "LauncherStateTransitionAnimation";
114
115 // Flags to determine how to set the layers on views before the transition animation
116 public static final int BUILD_LAYER = 0;
117 public static final int BUILD_AND_SET_LAYER = 1;
118 public static final int SINGLE_FRAME_DELAY = 16;
119
Adam Cohen091440a2015-03-18 14:16:05 -0700120 @Thunk Launcher mLauncher;
121 @Thunk Callbacks mCb;
122 @Thunk AnimatorSet mStateAnimation;
Winson Chungb745afb2015-03-02 11:51:23 -0800123
124 public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
125 mLauncher = l;
126 mCb = cb;
127 }
128
129 /**
130 * Starts an animation to the apps view.
131 */
Winson Chungef7f8742015-06-04 17:18:17 -0700132 public void startAnimationToAllApps(final Launcher.State fromState, final boolean animated) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700133 final AllAppsContainerView toView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800134 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
135 private int[] mAllAppsToPanelDelta;
136
137 @Override
138 public void onRevealViewVisible(View revealView, View contentView,
139 View allAppsButtonView) {
Winson Chungb745afb2015-03-02 11:51:23 -0800140 // 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() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700159 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800160 return allAppsButtonSize / 2;
161 }
162 @Override
163 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
164 final View revealView, final View allAppsButtonView) {
165 return new AnimatorListenerAdapter() {
166 public void onAnimationStart(Animator animation) {
167 allAppsButtonView.setVisibility(View.INVISIBLE);
168 }
169 public void onAnimationEnd(Animator animation) {
170 allAppsButtonView.setVisibility(View.VISIBLE);
171 }
172 };
173 }
174 };
Winson Chungef7f8742015-06-04 17:18:17 -0700175 // Only animate the search bar if animating from spring loaded mode back to all apps
Winson Chungb745afb2015-03-02 11:51:23 -0800176 startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, toView, toView.getContentView(),
Winson Chungef7f8742015-06-04 17:18:17 -0700177 toView.getRevealView(), toView.getSearchBarView(), animated, true, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800178 }
179
180 /**
181 * Starts an animation to the widgets view.
182 */
183 public void startAnimationToWidgets(final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700184 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700185 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800186 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
187 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700188 public void onRevealViewVisible(View revealView, View contentView,
189 View allAppsButtonView) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700190 }
191 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800192 public float getMaterialRevealViewFinalAlpha(View revealView) {
193 return 0.3f;
194 }
195 @Override
196 public float getMaterialRevealViewFinalYDrift(View revealView) {
197 return revealView.getMeasuredHeight() / 2;
198 }
199 };
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700200 startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, toView,
Winson Chungef7f8742015-06-04 17:18:17 -0700201 toView.getContentView(), toView.getRevealView(), null, animated,
202 true /* hideSearchBar */, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800203 }
204
205 /**
206 * Starts and animation to the workspace from the current overlay view.
207 */
208 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700209 final Workspace.State toWorkspaceState, final int toWorkspacePage,
210 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800211 if (toWorkspaceState != Workspace.State.NORMAL &&
212 toWorkspaceState != Workspace.State.SPRING_LOADED &&
213 toWorkspaceState != Workspace.State.OVERVIEW) {
214 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
215 }
216
217 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chungef7f8742015-06-04 17:18:17 -0700218 startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700219 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800220 } else {
Winson Chungef7f8742015-06-04 17:18:17 -0700221 startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700222 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800223 }
224 }
225
226 /**
227 * Creates and starts a new animation to a particular overlay view.
228 */
Sunny Goyal70660032015-05-14 00:07:08 -0700229 @SuppressLint("NewApi")
Winson Chungb745afb2015-03-02 11:51:23 -0800230 private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
Winson Chungef7f8742015-06-04 17:18:17 -0700231 final View contentView, final View revealView, final View overlaySearchBarView,
232 final boolean animated, final boolean hideSearchBar,
233 final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800234 final Resources res = mLauncher.getResources();
235 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700236 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800237 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700238 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800239
240 final View allAppsButtonView = mLauncher.getAllAppsButton();
241 final View fromView = mLauncher.getWorkspace();
242
243 final HashMap<View, Integer> layerViews = new HashMap<>();
244
245 // If for some reason our views aren't initialized, don't animate
246 boolean initialized = allAppsButtonView != null;
247
248 // Cancel the current animation
249 cancelAnimation();
250
251 // Create the workspace animation.
252 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700253 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
Winson Chungef7f8742015-06-04 17:18:17 -0700254 animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800255
256 if (animated && initialized) {
257 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
258
259 // Setup the reveal view animation
260 int width = revealView.getMeasuredWidth();
261 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700262 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800263 revealView.setVisibility(View.VISIBLE);
264 revealView.setAlpha(0f);
265 revealView.setTranslationY(0f);
266 revealView.setTranslationX(0f);
267 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
268
269 // Calculate the final animation values
270 final float revealViewToAlpha;
271 final float revealViewToXDrift;
272 final float revealViewToYDrift;
273 if (material) {
274 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
275 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
276 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
277 } else {
278 revealViewToAlpha = 0f;
279 revealViewToYDrift = 2 * height / 3;
280 revealViewToXDrift = 0;
281 }
282
283 // Create the animators
284 PropertyValuesHolder panelAlpha =
285 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
286 PropertyValuesHolder panelDriftY =
287 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
288 PropertyValuesHolder panelDriftX =
289 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
290 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
291 panelAlpha, panelDriftY, panelDriftX);
292 panelAlphaAndDrift.setDuration(revealDuration);
293 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
294
295 // Play the animation
296 layerViews.put(revealView, BUILD_AND_SET_LAYER);
297 mStateAnimation.play(panelAlphaAndDrift);
298
Winson Chungef7f8742015-06-04 17:18:17 -0700299 if (overlaySearchBarView != null) {
300 overlaySearchBarView.setAlpha(0f);
301 ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
302 searchBarAlpha.setDuration(100);
303 searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
304 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
305 mStateAnimation.play(searchBarAlpha);
306 }
307
Winson Chungb745afb2015-03-02 11:51:23 -0800308 // Setup the animation for the content view
309 contentView.setVisibility(View.VISIBLE);
310 contentView.setAlpha(0f);
311 contentView.setTranslationY(revealViewToYDrift);
312 layerViews.put(contentView, BUILD_AND_SET_LAYER);
313
314 // Create the individual animators
315 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
316 revealViewToYDrift, 0);
317 pageDrift.setDuration(revealDuration);
318 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
319 pageDrift.setStartDelay(itemsAlphaStagger);
320 mStateAnimation.play(pageDrift);
321
322 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
323 itemsAlpha.setDuration(revealDuration);
324 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
325 itemsAlpha.setStartDelay(itemsAlphaStagger);
326 mStateAnimation.play(itemsAlpha);
327
328 if (material) {
329 // Animate the all apps button
330 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
331 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
332 revealView, allAppsButtonView);
Adam Cohen15588932015-05-26 23:03:31 -0700333 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
Winson Chungb745afb2015-03-02 11:51:23 -0800334 height / 2, startRadius, revealRadius);
335 reveal.setDuration(revealDuration);
336 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
337 if (listener != null) {
338 reveal.addListener(listener);
339 }
340 mStateAnimation.play(reveal);
341 }
342
343 mStateAnimation.addListener(new AnimatorListenerAdapter() {
344 @Override
345 public void onAnimationEnd(Animator animation) {
346 dispatchOnLauncherTransitionEnd(fromView, animated, false);
347 dispatchOnLauncherTransitionEnd(toView, animated, false);
348
349 // Hide the reveal view
350 revealView.setVisibility(View.INVISIBLE);
351 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
352
353 // Disable all necessary layers
354 for (View v : layerViews.keySet()) {
355 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
356 v.setLayerType(View.LAYER_TYPE_NONE, null);
357 }
358 }
359
Winson Chung0f785722015-04-08 10:27:49 -0700360 if (hideSearchBar) {
361 mCb.onStateTransitionHideSearchBar();
362 }
Winson Chungb745afb2015-03-02 11:51:23 -0800363
364 // This can hold unnecessary references to views.
365 mStateAnimation = null;
366 }
367
368 });
369
370 // Play the workspace animation
371 if (workspaceAnim != null) {
372 mStateAnimation.play(workspaceAnim);
373 }
374
375 // Dispatch the prepare transition signal
376 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
377 dispatchOnLauncherTransitionPrepare(toView, animated, false);
378
379
380 final AnimatorSet stateAnimation = mStateAnimation;
381 final Runnable startAnimRunnable = new Runnable() {
382 public void run() {
383 // Check that mStateAnimation hasn't changed while
384 // we waited for a layout/draw pass
385 if (mStateAnimation != stateAnimation)
386 return;
387 dispatchOnLauncherTransitionStart(fromView, animated, false);
388 dispatchOnLauncherTransitionStart(toView, animated, false);
389
390 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700391 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800392 for (View v : layerViews.keySet()) {
393 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
394 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
395 }
Winson Chung11509ad2015-05-12 18:55:26 -0700396 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800397 v.buildLayer();
398 }
399 }
400
401 // Focus the new view
402 toView.requestFocus();
403
404 mStateAnimation.start();
405 }
406 };
Winson Chungb745afb2015-03-02 11:51:23 -0800407 toView.bringToFront();
408 toView.setVisibility(View.VISIBLE);
409 toView.post(startAnimRunnable);
410 } else {
411 toView.setTranslationX(0.0f);
412 toView.setTranslationY(0.0f);
413 toView.setScaleX(1.0f);
414 toView.setScaleY(1.0f);
415 toView.setVisibility(View.VISIBLE);
416 toView.bringToFront();
417
418 // Show the content view
419 contentView.setVisibility(View.VISIBLE);
420
Winson Chung0f785722015-04-08 10:27:49 -0700421 if (hideSearchBar) {
422 mCb.onStateTransitionHideSearchBar();
423 }
Winson Chungb745afb2015-03-02 11:51:23 -0800424
425 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
426 dispatchOnLauncherTransitionStart(fromView, animated, false);
427 dispatchOnLauncherTransitionEnd(fromView, animated, false);
428 dispatchOnLauncherTransitionPrepare(toView, animated, false);
429 dispatchOnLauncherTransitionStart(toView, animated, false);
430 dispatchOnLauncherTransitionEnd(toView, animated, false);
431 }
432 }
433
434 /**
435 * Starts and animation to the workspace from the apps view.
436 */
Winson Chungef7f8742015-06-04 17:18:17 -0700437 private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
438 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700439 AllAppsContainerView appsView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800440 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
441 int[] mAllAppsToPanelDelta;
442
443 @Override
444 public void onRevealViewVisible(View revealView, View contentView,
445 View allAppsButtonView) {
446 // Get the y delta between the center of the page and the center of the all apps
447 // button
448 mAllAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
449 allAppsButtonView, null);
450 }
451 @Override
452 public float getMaterialRevealViewFinalXDrift(View revealView) {
453 return mAllAppsToPanelDelta[0];
454 }
455 @Override
456 public float getMaterialRevealViewFinalYDrift(View revealView) {
457 return mAllAppsToPanelDelta[1];
458 }
459 @Override
460 float getMaterialRevealViewFinalAlpha(View revealView) {
461 // No alpha anim from all apps
462 return 1f;
463 }
464 @Override
465 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700466 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800467 return allAppsButtonSize / 2;
468 }
469 @Override
470 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
471 final View revealView, final View allAppsButtonView) {
472 return new AnimatorListenerAdapter() {
473 public void onAnimationStart(Animator animation) {
474 // We set the alpha instead of visibility to ensure that the focus does not
475 // get taken from the all apps view
476 allAppsButtonView.setVisibility(View.VISIBLE);
477 allAppsButtonView.setAlpha(0f);
478 }
479 public void onAnimationEnd(Animator animation) {
480 // Hide the reveal view
481 revealView.setVisibility(View.INVISIBLE);
482
483 // Show the all apps button, and focus it
484 allAppsButtonView.setAlpha(1f);
485 }
486 };
487 }
488 };
Winson Chungef7f8742015-06-04 17:18:17 -0700489 // Only animate the search bar if animating to spring loaded mode from all apps
Winson Chungdc61c4d2015-04-20 18:26:57 -0700490 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, appsView,
Winson Chungef7f8742015-06-04 17:18:17 -0700491 appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(),
492 animated, onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800493 }
494
495 /**
496 * Starts and animation to the workspace from the widgets view.
497 */
Winson Chungef7f8742015-06-04 17:18:17 -0700498 private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
499 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700500 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
501 final Resources res = mLauncher.getResources();
Winson Chungb745afb2015-03-02 11:51:23 -0800502 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
503 @Override
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700504 public void onRevealViewVisible(View revealView, View contentView,
505 View allAppsButtonView) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700506 }
507 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800508 public float getMaterialRevealViewFinalYDrift(View revealView) {
509 return revealView.getMeasuredHeight() / 2;
510 }
511 @Override
512 float getMaterialRevealViewFinalAlpha(View revealView) {
513 return 0.4f;
514 }
515 @Override
516 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
517 final View revealView, final View allAppsButtonView) {
518 return new AnimatorListenerAdapter() {
519 public void onAnimationEnd(Animator animation) {
520 // Hide the reveal view
521 revealView.setVisibility(View.INVISIBLE);
522 }
523 };
524 }
525 };
Winson Chungdc61c4d2015-04-20 18:26:57 -0700526 startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, widgetsView,
Winson Chungef7f8742015-06-04 17:18:17 -0700527 widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
Winson Chung0f785722015-04-08 10:27:49 -0700528 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800529 }
530
531 /**
532 * Creates and starts a new animation to the workspace.
533 */
534 private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700535 final int toWorkspacePage, final View fromView, final View contentView,
Winson Chungef7f8742015-06-04 17:18:17 -0700536 final View revealView, final View overlaySearchBarView, final boolean animated,
537 final Runnable onCompleteRunnable, final PrivateTransitionCallbacks pCb) {
Winson Chungb745afb2015-03-02 11:51:23 -0800538 final Resources res = mLauncher.getResources();
539 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700540 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800541 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700542 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800543
544 final View allAppsButtonView = mLauncher.getAllAppsButton();
545 final View toView = mLauncher.getWorkspace();
546
547 final HashMap<View, Integer> layerViews = new HashMap<>();
548
549 // If for some reason our views aren't initialized, don't animate
550 boolean initialized = allAppsButtonView != null;
551
552 // Cancel the current animation
553 cancelAnimation();
554
555 // Create the workspace animation.
556 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700557 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
Winson Chungef7f8742015-06-04 17:18:17 -0700558 toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
559 layerViews);
Winson Chungb745afb2015-03-02 11:51:23 -0800560
561 if (animated && initialized) {
562 mStateAnimation = LauncherAnimUtils.createAnimatorSet();
563
564 // Play the workspace animation
565 if (workspaceAnim != null) {
566 mStateAnimation.play(workspaceAnim);
567 }
568
569 // hideAppsCustomizeHelper is called in some cases when it is already hidden
570 // don't perform all these no-op animations. In particularly, this was causing
571 // the all-apps button to pop in and out.
572 if (fromView.getVisibility() == View.VISIBLE) {
573 int width = revealView.getMeasuredWidth();
574 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700575 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800576 revealView.setVisibility(View.VISIBLE);
577 revealView.setAlpha(1f);
578 revealView.setTranslationY(0);
579 layerViews.put(revealView, BUILD_AND_SET_LAYER);
580 pCb.onRevealViewVisible(revealView, contentView, allAppsButtonView);
581
582 // Calculate the final animation values
583 final float revealViewToXDrift;
584 final float revealViewToYDrift;
585 if (material) {
586 revealViewToYDrift = pCb.getMaterialRevealViewFinalYDrift(revealView);
587 revealViewToXDrift = pCb.getMaterialRevealViewFinalXDrift(revealView);
588 } else {
589 revealViewToYDrift = 2 * height / 3;
590 revealViewToXDrift = 0;
591 }
592
593 // The vertical motion of the apps panel should be delayed by one frame
594 // from the conceal animation in order to give the right feel. We correspondingly
595 // shorten the duration so that the slide and conceal end at the same time.
596 TimeInterpolator decelerateInterpolator = material ?
597 new LogDecelerateInterpolator(100, 0) :
598 new DecelerateInterpolator(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700599 ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800600 0, revealViewToYDrift);
601 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
602 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
603 panelDriftY.setInterpolator(decelerateInterpolator);
604 mStateAnimation.play(panelDriftY);
605
Adam Cohen15588932015-05-26 23:03:31 -0700606 ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
Winson Chungb745afb2015-03-02 11:51:23 -0800607 0, revealViewToXDrift);
608 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
609 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
610 panelDriftX.setInterpolator(decelerateInterpolator);
611 mStateAnimation.play(panelDriftX);
612
613 // Setup animation for the reveal panel alpha
614 final float revealViewToAlpha = !material ? 0f :
615 pCb.getMaterialRevealViewFinalAlpha(revealView);
616 if (revealViewToAlpha != 1f) {
Adam Cohen15588932015-05-26 23:03:31 -0700617 ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
Winson Chungb745afb2015-03-02 11:51:23 -0800618 1f, revealViewToAlpha);
619 panelAlpha.setDuration(material ? revealDuration : 150);
620 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
621 panelAlpha.setInterpolator(decelerateInterpolator);
622 mStateAnimation.play(panelAlpha);
623 }
624
625 // Setup the animation for the content view
626 layerViews.put(contentView, BUILD_AND_SET_LAYER);
627
628 // Create the individual animators
Adam Cohen15588932015-05-26 23:03:31 -0700629 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800630 0, revealViewToYDrift);
631 contentView.setTranslationY(0);
632 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
633 pageDrift.setInterpolator(decelerateInterpolator);
634 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
635 mStateAnimation.play(pageDrift);
636
637 contentView.setAlpha(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700638 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800639 itemsAlpha.setDuration(100);
640 itemsAlpha.setInterpolator(decelerateInterpolator);
641 mStateAnimation.play(itemsAlpha);
642
Winson Chungef7f8742015-06-04 17:18:17 -0700643 if (overlaySearchBarView != null) {
644 overlaySearchBarView.setAlpha(1f);
645 ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
646 searchAlpha.setDuration(material ? 100 : 150);
647 searchAlpha.setInterpolator(decelerateInterpolator);
648 searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
649 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
650 mStateAnimation.play(searchAlpha);
651 }
652
Winson Chungb745afb2015-03-02 11:51:23 -0800653 if (material) {
654 // Animate the all apps button
655 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
656 AnimatorListenerAdapter listener =
657 pCb.getMaterialRevealViewAnimatorListener(revealView, allAppsButtonView);
Adam Cohen15588932015-05-26 23:03:31 -0700658 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
659 height / 2, revealRadius, finalRadius);
Winson Chungb745afb2015-03-02 11:51:23 -0800660 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
661 reveal.setDuration(revealDuration);
662 reveal.setStartDelay(itemsAlphaStagger);
663 if (listener != null) {
664 reveal.addListener(listener);
665 }
666 mStateAnimation.play(reveal);
667 }
668
669 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
670 dispatchOnLauncherTransitionPrepare(toView, animated, true);
671 }
672
673 mStateAnimation.addListener(new AnimatorListenerAdapter() {
674 @Override
675 public void onAnimationEnd(Animator animation) {
676 fromView.setVisibility(View.GONE);
677 dispatchOnLauncherTransitionEnd(fromView, animated, true);
678 dispatchOnLauncherTransitionEnd(toView, animated, true);
679
680 // Run any queued runnables
681 if (onCompleteRunnable != null) {
682 onCompleteRunnable.run();
683 }
684
685 // Animation complete callback
686 pCb.onAnimationComplete(revealView, contentView, allAppsButtonView);
687
688 // Disable all necessary layers
689 for (View v : layerViews.keySet()) {
690 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
691 v.setLayerType(View.LAYER_TYPE_NONE, null);
692 }
693 }
694
695 // Reset page transforms
696 if (contentView != null) {
697 contentView.setTranslationX(0);
698 contentView.setTranslationY(0);
699 contentView.setAlpha(1);
700 }
Winson Chungef7f8742015-06-04 17:18:17 -0700701 if (overlaySearchBarView != null) {
702 overlaySearchBarView.setAlpha(1f);
703 }
Winson Chungb745afb2015-03-02 11:51:23 -0800704
705 // This can hold unnecessary references to views.
706 mStateAnimation = null;
707 }
708 });
709
710 final AnimatorSet stateAnimation = mStateAnimation;
711 final Runnable startAnimRunnable = new Runnable() {
712 public void run() {
713 // Check that mStateAnimation hasn't changed while
714 // we waited for a layout/draw pass
715 if (mStateAnimation != stateAnimation)
716 return;
717 dispatchOnLauncherTransitionStart(fromView, animated, false);
718 dispatchOnLauncherTransitionStart(toView, animated, false);
719
720 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700721 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800722 for (View v : layerViews.keySet()) {
723 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
724 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
725 }
Winson Chung11509ad2015-05-12 18:55:26 -0700726 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800727 v.buildLayer();
728 }
729 }
730 mStateAnimation.start();
731 }
732 };
733 fromView.post(startAnimRunnable);
734 } else {
735 fromView.setVisibility(View.GONE);
736 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
737 dispatchOnLauncherTransitionStart(fromView, animated, true);
738 dispatchOnLauncherTransitionEnd(fromView, animated, true);
739 dispatchOnLauncherTransitionPrepare(toView, animated, true);
740 dispatchOnLauncherTransitionStart(toView, animated, true);
741 dispatchOnLauncherTransitionEnd(toView, animated, true);
742
743 // Run any queued runnables
744 if (onCompleteRunnable != null) {
745 onCompleteRunnable.run();
746 }
747 }
748 }
749
750
751 /**
752 * Dispatches the prepare-transition event to suitable views.
753 */
754 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
755 if (v instanceof LauncherTransitionable) {
756 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
757 toWorkspace);
758 }
759 }
760
761 /**
762 * Dispatches the start-transition event to suitable views.
763 */
764 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
765 if (v instanceof LauncherTransitionable) {
766 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
767 toWorkspace);
768 }
769
770 // Update the workspace transition step as well
771 dispatchOnLauncherTransitionStep(v, 0f);
772 }
773
774 /**
775 * Dispatches the step-transition event to suitable views.
776 */
777 void dispatchOnLauncherTransitionStep(View v, float t) {
778 if (v instanceof LauncherTransitionable) {
779 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
780 }
781 }
782
783 /**
784 * Dispatches the end-transition event to suitable views.
785 */
786 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
787 if (v instanceof LauncherTransitionable) {
788 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
789 toWorkspace);
790 }
791
792 // Update the workspace transition step as well
793 dispatchOnLauncherTransitionStep(v, 1f);
794 }
795
796 /**
797 * Cancels the current animation.
798 */
799 private void cancelAnimation() {
800 if (mStateAnimation != null) {
801 mStateAnimation.setDuration(0);
802 mStateAnimation.cancel();
803 mStateAnimation = null;
804 }
805 }
Adam Cohen15588932015-05-26 23:03:31 -0700806}