blob: 5a3f3dc7acb95dde46b34fa0633a5df229def98c [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 /**
Winson Chungb745afb2015-03-02 11:51:23 -080083 * Private callbacks made during transition setup.
84 */
85 static abstract class PrivateTransitionCallbacks {
Winson Chungb745afb2015-03-02 11:51:23 -080086 float getMaterialRevealViewFinalAlpha(View revealView) {
87 return 0;
88 }
Winson Chungb745afb2015-03-02 11:51:23 -080089 float getMaterialRevealViewStartFinalRadius() {
90 return 0;
91 }
92 AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(View revealView,
Hyunyoung Song98ff38a2015-07-10 17:50:13 -070093 View buttonView) {
Winson Chungb745afb2015-03-02 11:51:23 -080094 return null;
95 }
Winson Chung76648c52015-07-10 14:33:23 -070096 void onTransitionComplete() {}
Winson Chungb745afb2015-03-02 11:51:23 -080097 }
98
99 public static final String TAG = "LauncherStateTransitionAnimation";
100
101 // Flags to determine how to set the layers on views before the transition animation
102 public static final int BUILD_LAYER = 0;
103 public static final int BUILD_AND_SET_LAYER = 1;
104 public static final int SINGLE_FRAME_DELAY = 16;
105
Adam Cohen091440a2015-03-18 14:16:05 -0700106 @Thunk Launcher mLauncher;
Winson Chung006ee262015-08-03 14:40:11 -0700107 @Thunk AnimatorSet mCurrentAnimation;
Winson Chungb745afb2015-03-02 11:51:23 -0800108
Winson Chung006ee262015-08-03 14:40:11 -0700109 public LauncherStateTransitionAnimation(Launcher l) {
Winson Chungb745afb2015-03-02 11:51:23 -0800110 mLauncher = l;
Winson Chungb745afb2015-03-02 11:51:23 -0800111 }
112
113 /**
114 * Starts an animation to the apps view.
Winson Chung76648c52015-07-10 14:33:23 -0700115 *
116 * @param startSearchAfterTransition Immediately starts app search after the transition to
117 * All Apps is completed.
Winson Chungb745afb2015-03-02 11:51:23 -0800118 */
Winson Chung006ee262015-08-03 14:40:11 -0700119 public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
120 final boolean animated, final boolean startSearchAfterTransition) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700121 final AllAppsContainerView toView = mLauncher.getAppsView();
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700122 final View buttonView = mLauncher.getAllAppsButton();
Winson Chungb745afb2015-03-02 11:51:23 -0800123 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
Winson Chungb745afb2015-03-02 11:51:23 -0800124 @Override
125 public float getMaterialRevealViewFinalAlpha(View revealView) {
126 return 1f;
127 }
128 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800129 public float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700130 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800131 return allAppsButtonSize / 2;
132 }
133 @Override
134 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
135 final View revealView, final View allAppsButtonView) {
136 return new AnimatorListenerAdapter() {
137 public void onAnimationStart(Animator animation) {
138 allAppsButtonView.setVisibility(View.INVISIBLE);
139 }
140 public void onAnimationEnd(Animator animation) {
141 allAppsButtonView.setVisibility(View.VISIBLE);
142 }
143 };
144 }
Winson Chung76648c52015-07-10 14:33:23 -0700145 @Override
146 void onTransitionComplete() {
147 if (startSearchAfterTransition) {
148 toView.startAppsSearch();
149 }
150 }
Winson Chungb745afb2015-03-02 11:51:23 -0800151 };
Winson Chungef7f8742015-06-04 17:18:17 -0700152 // Only animate the search bar if animating from spring loaded mode back to all apps
Winson Chung006ee262015-08-03 14:40:11 -0700153 mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
154 Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
155 toView.getRevealView(), toView.getSearchBarView(), animated, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800156 }
157
158 /**
159 * Starts an animation to the widgets view.
160 */
Winson Chung006ee262015-08-03 14:40:11 -0700161 public void startAnimationToWidgets(final Workspace.State fromWorkspaceState,
162 final boolean animated) {
Hyunyoung Song3f471442015-04-08 19:01:34 -0700163 final WidgetsContainerView toView = mLauncher.getWidgetsView();
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700164 final View buttonView = mLauncher.getWidgetsButton();
165
Winson Chungb745afb2015-03-02 11:51:23 -0800166 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
167 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800168 public float getMaterialRevealViewFinalAlpha(View revealView) {
169 return 0.3f;
170 }
Winson Chungb745afb2015-03-02 11:51:23 -0800171 };
Winson Chung006ee262015-08-03 14:40:11 -0700172 mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
173 Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
174 toView.getRevealView(), null, animated, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800175 }
176
177 /**
178 * Starts and animation to the workspace from the current overlay view.
179 */
180 public void startAnimationToWorkspace(final Launcher.State fromState,
Winson Chung006ee262015-08-03 14:40:11 -0700181 final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
182 final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
Winson Chungb745afb2015-03-02 11:51:23 -0800183 if (toWorkspaceState != Workspace.State.NORMAL &&
184 toWorkspaceState != Workspace.State.SPRING_LOADED &&
185 toWorkspaceState != Workspace.State.OVERVIEW) {
186 Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
187 }
188
189 if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
Winson Chung006ee262015-08-03 14:40:11 -0700190 startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700191 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800192 } else {
Winson Chung006ee262015-08-03 14:40:11 -0700193 startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
Winson Chungdc61c4d2015-04-20 18:26:57 -0700194 animated, onCompleteRunnable);
Winson Chungb745afb2015-03-02 11:51:23 -0800195 }
196 }
197
198 /**
199 * Creates and starts a new animation to a particular overlay view.
200 */
Sunny Goyal70660032015-05-14 00:07:08 -0700201 @SuppressLint("NewApi")
Winson Chung006ee262015-08-03 14:40:11 -0700202 private AnimatorSet startAnimationToOverlay(final Workspace.State fromWorkspaceState,
203 final Workspace.State toWorkspaceState, final View buttonView, final View toView,
204 final View contentView, final View revealView, final View overlaySearchBarView,
205 final boolean animated, final PrivateTransitionCallbacks pCb) {
206 final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
Winson Chungb745afb2015-03-02 11:51:23 -0800207 final Resources res = mLauncher.getResources();
208 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700209 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800210 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700211 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800212
Winson Chungb745afb2015-03-02 11:51:23 -0800213 final View fromView = mLauncher.getWorkspace();
214
215 final HashMap<View, Integer> layerViews = new HashMap<>();
216
217 // If for some reason our views aren't initialized, don't animate
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700218 boolean initialized = buttonView != null;
Winson Chungb745afb2015-03-02 11:51:23 -0800219
220 // Cancel the current animation
221 cancelAnimation();
222
223 // Create the workspace animation.
224 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700225 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
Winson Chung006ee262015-08-03 14:40:11 -0700226 animated, layerViews);
227
228 // Animate the search bar
229 startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
230 animated ? revealDuration : 0, overlaySearchBarView);
Winson Chungb745afb2015-03-02 11:51:23 -0800231
232 if (animated && initialized) {
Winson Chungb745afb2015-03-02 11:51:23 -0800233 // Setup the reveal view animation
234 int width = revealView.getMeasuredWidth();
235 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700236 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800237 revealView.setVisibility(View.VISIBLE);
238 revealView.setAlpha(0f);
239 revealView.setTranslationY(0f);
240 revealView.setTranslationX(0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800241
242 // Calculate the final animation values
243 final float revealViewToAlpha;
244 final float revealViewToXDrift;
245 final float revealViewToYDrift;
246 if (material) {
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700247 int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
248 buttonView, null);
Winson Chungb745afb2015-03-02 11:51:23 -0800249 revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700250 revealViewToYDrift = buttonViewToPanelDelta[1];
251 revealViewToXDrift = buttonViewToPanelDelta[0];
Winson Chungb745afb2015-03-02 11:51:23 -0800252 } else {
253 revealViewToAlpha = 0f;
254 revealViewToYDrift = 2 * height / 3;
255 revealViewToXDrift = 0;
256 }
257
258 // Create the animators
259 PropertyValuesHolder panelAlpha =
260 PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
261 PropertyValuesHolder panelDriftY =
262 PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
263 PropertyValuesHolder panelDriftX =
264 PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
265 ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
266 panelAlpha, panelDriftY, panelDriftX);
267 panelAlphaAndDrift.setDuration(revealDuration);
268 panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
269
270 // Play the animation
271 layerViews.put(revealView, BUILD_AND_SET_LAYER);
Winson Chung006ee262015-08-03 14:40:11 -0700272 animation.play(panelAlphaAndDrift);
Winson Chungb745afb2015-03-02 11:51:23 -0800273
Winson Chungef7f8742015-06-04 17:18:17 -0700274 if (overlaySearchBarView != null) {
275 overlaySearchBarView.setAlpha(0f);
276 ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
277 searchBarAlpha.setDuration(100);
278 searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
279 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
Winson Chung006ee262015-08-03 14:40:11 -0700280 animation.play(searchBarAlpha);
Winson Chungef7f8742015-06-04 17:18:17 -0700281 }
282
Winson Chungb745afb2015-03-02 11:51:23 -0800283 // Setup the animation for the content view
284 contentView.setVisibility(View.VISIBLE);
285 contentView.setAlpha(0f);
286 contentView.setTranslationY(revealViewToYDrift);
287 layerViews.put(contentView, BUILD_AND_SET_LAYER);
288
289 // Create the individual animators
290 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
291 revealViewToYDrift, 0);
292 pageDrift.setDuration(revealDuration);
293 pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
294 pageDrift.setStartDelay(itemsAlphaStagger);
Winson Chung006ee262015-08-03 14:40:11 -0700295 animation.play(pageDrift);
Winson Chungb745afb2015-03-02 11:51:23 -0800296
297 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
298 itemsAlpha.setDuration(revealDuration);
299 itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
300 itemsAlpha.setStartDelay(itemsAlphaStagger);
Winson Chung006ee262015-08-03 14:40:11 -0700301 animation.play(itemsAlpha);
Winson Chungb745afb2015-03-02 11:51:23 -0800302
303 if (material) {
Winson Chungb745afb2015-03-02 11:51:23 -0800304 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
305 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700306 revealView, buttonView);
Adam Cohen15588932015-05-26 23:03:31 -0700307 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
Winson Chungb745afb2015-03-02 11:51:23 -0800308 height / 2, startRadius, revealRadius);
309 reveal.setDuration(revealDuration);
310 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
311 if (listener != null) {
312 reveal.addListener(listener);
313 }
Winson Chung006ee262015-08-03 14:40:11 -0700314 animation.play(reveal);
Winson Chungb745afb2015-03-02 11:51:23 -0800315 }
316
Winson Chung006ee262015-08-03 14:40:11 -0700317 animation.addListener(new AnimatorListenerAdapter() {
Winson Chungb745afb2015-03-02 11:51:23 -0800318 @Override
319 public void onAnimationEnd(Animator animation) {
320 dispatchOnLauncherTransitionEnd(fromView, animated, false);
321 dispatchOnLauncherTransitionEnd(toView, animated, false);
322
323 // Hide the reveal view
324 revealView.setVisibility(View.INVISIBLE);
Winson Chungb745afb2015-03-02 11:51:23 -0800325
326 // Disable all necessary layers
327 for (View v : layerViews.keySet()) {
328 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
329 v.setLayerType(View.LAYER_TYPE_NONE, null);
330 }
331 }
332
Winson Chungb745afb2015-03-02 11:51:23 -0800333 // This can hold unnecessary references to views.
Winson Chung006ee262015-08-03 14:40:11 -0700334 cleanupAnimation();
Winson Chung76648c52015-07-10 14:33:23 -0700335 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800336 }
337
338 });
339
340 // Play the workspace animation
341 if (workspaceAnim != null) {
Winson Chung006ee262015-08-03 14:40:11 -0700342 animation.play(workspaceAnim);
Winson Chungb745afb2015-03-02 11:51:23 -0800343 }
344
345 // Dispatch the prepare transition signal
346 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
347 dispatchOnLauncherTransitionPrepare(toView, animated, false);
348
349
Winson Chung006ee262015-08-03 14:40:11 -0700350 final AnimatorSet stateAnimation = animation;
Winson Chungb745afb2015-03-02 11:51:23 -0800351 final Runnable startAnimRunnable = new Runnable() {
352 public void run() {
Winson Chung006ee262015-08-03 14:40:11 -0700353 // Check that mCurrentAnimation hasn't changed while
Winson Chungb745afb2015-03-02 11:51:23 -0800354 // we waited for a layout/draw pass
Winson Chung006ee262015-08-03 14:40:11 -0700355 if (mCurrentAnimation != stateAnimation)
Winson Chungb745afb2015-03-02 11:51:23 -0800356 return;
357 dispatchOnLauncherTransitionStart(fromView, animated, false);
358 dispatchOnLauncherTransitionStart(toView, animated, false);
359
360 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700361 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800362 for (View v : layerViews.keySet()) {
363 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
364 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
365 }
Winson Chung11509ad2015-05-12 18:55:26 -0700366 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800367 v.buildLayer();
368 }
369 }
370
371 // Focus the new view
372 toView.requestFocus();
373
Winson Chung006ee262015-08-03 14:40:11 -0700374 stateAnimation.start();
Winson Chungb745afb2015-03-02 11:51:23 -0800375 }
376 };
Winson Chungb745afb2015-03-02 11:51:23 -0800377 toView.bringToFront();
378 toView.setVisibility(View.VISIBLE);
379 toView.post(startAnimRunnable);
Winson Chung006ee262015-08-03 14:40:11 -0700380
381 return animation;
Winson Chungb745afb2015-03-02 11:51:23 -0800382 } else {
383 toView.setTranslationX(0.0f);
384 toView.setTranslationY(0.0f);
385 toView.setScaleX(1.0f);
386 toView.setScaleY(1.0f);
387 toView.setVisibility(View.VISIBLE);
388 toView.bringToFront();
389
390 // Show the content view
391 contentView.setVisibility(View.VISIBLE);
392
Winson Chungb745afb2015-03-02 11:51:23 -0800393 dispatchOnLauncherTransitionPrepare(fromView, animated, false);
394 dispatchOnLauncherTransitionStart(fromView, animated, false);
395 dispatchOnLauncherTransitionEnd(fromView, animated, false);
396 dispatchOnLauncherTransitionPrepare(toView, animated, false);
397 dispatchOnLauncherTransitionStart(toView, animated, false);
398 dispatchOnLauncherTransitionEnd(toView, animated, false);
Winson Chung76648c52015-07-10 14:33:23 -0700399 pCb.onTransitionComplete();
Winson Chung006ee262015-08-03 14:40:11 -0700400
401 return null;
Winson Chungb745afb2015-03-02 11:51:23 -0800402 }
403 }
404
405 /**
406 * Starts and animation to the workspace from the apps view.
407 */
Winson Chung006ee262015-08-03 14:40:11 -0700408 private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
409 final Workspace.State toWorkspaceState, final int toWorkspacePage,
410 final boolean animated, final Runnable onCompleteRunnable) {
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700411 AllAppsContainerView appsView = mLauncher.getAppsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800412 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
Winson Chungb745afb2015-03-02 11:51:23 -0800413 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800414 float getMaterialRevealViewFinalAlpha(View revealView) {
415 // No alpha anim from all apps
416 return 1f;
417 }
418 @Override
419 float getMaterialRevealViewStartFinalRadius() {
Adam Cohen2e6da152015-05-06 11:42:25 -0700420 int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
Winson Chungb745afb2015-03-02 11:51:23 -0800421 return allAppsButtonSize / 2;
422 }
423 @Override
424 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
425 final View revealView, final View allAppsButtonView) {
426 return new AnimatorListenerAdapter() {
427 public void onAnimationStart(Animator animation) {
428 // We set the alpha instead of visibility to ensure that the focus does not
429 // get taken from the all apps view
430 allAppsButtonView.setVisibility(View.VISIBLE);
431 allAppsButtonView.setAlpha(0f);
432 }
433 public void onAnimationEnd(Animator animation) {
434 // Hide the reveal view
435 revealView.setVisibility(View.INVISIBLE);
436
437 // Show the all apps button, and focus it
438 allAppsButtonView.setAlpha(1f);
439 }
440 };
441 }
442 };
Winson Chungef7f8742015-06-04 17:18:17 -0700443 // Only animate the search bar if animating to spring loaded mode from all apps
Winson Chung006ee262015-08-03 14:40:11 -0700444 mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
445 toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700446 appsView.getRevealView(), appsView.getSearchBarView(), animated,
447 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800448 }
449
450 /**
451 * Starts and animation to the workspace from the widgets view.
452 */
Winson Chung006ee262015-08-03 14:40:11 -0700453 private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
454 final Workspace.State toWorkspaceState, final int toWorkspacePage,
455 final boolean animated, final Runnable onCompleteRunnable) {
Hyunyoung Song4cea4c82015-04-17 19:02:30 -0700456 final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
Winson Chungb745afb2015-03-02 11:51:23 -0800457 PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
458 @Override
Winson Chungb745afb2015-03-02 11:51:23 -0800459 float getMaterialRevealViewFinalAlpha(View revealView) {
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700460 return 0.3f;
Winson Chungb745afb2015-03-02 11:51:23 -0800461 }
462 @Override
463 public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700464 final View revealView, final View widgetsButtonView) {
Winson Chungb745afb2015-03-02 11:51:23 -0800465 return new AnimatorListenerAdapter() {
466 public void onAnimationEnd(Animator animation) {
467 // Hide the reveal view
468 revealView.setVisibility(View.INVISIBLE);
469 }
470 };
471 }
472 };
Winson Chung006ee262015-08-03 14:40:11 -0700473 mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
474 toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
475 widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
476 onCompleteRunnable, cb);
Winson Chungb745afb2015-03-02 11:51:23 -0800477 }
478
479 /**
480 * Creates and starts a new animation to the workspace.
481 */
Winson Chung006ee262015-08-03 14:40:11 -0700482 private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
483 final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView,
484 final View fromView, final View contentView, final View revealView,
485 final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable,
486 final PrivateTransitionCallbacks pCb) {
487 final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
Winson Chungb745afb2015-03-02 11:51:23 -0800488 final Resources res = mLauncher.getResources();
489 final boolean material = Utilities.isLmpOrAbove();
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700490 final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
Winson Chungb745afb2015-03-02 11:51:23 -0800491 final int itemsAlphaStagger =
Winson Chung5f4e0fd2015-05-22 11:12:27 -0700492 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
Winson Chungb745afb2015-03-02 11:51:23 -0800493
Winson Chungb745afb2015-03-02 11:51:23 -0800494 final View toView = mLauncher.getWorkspace();
495
496 final HashMap<View, Integer> layerViews = new HashMap<>();
497
498 // If for some reason our views aren't initialized, don't animate
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700499 boolean initialized = buttonView != null;
Winson Chungb745afb2015-03-02 11:51:23 -0800500
501 // Cancel the current animation
502 cancelAnimation();
503
504 // Create the workspace animation.
505 // NOTE: this call apparently also sets the state for the workspace if !animated
Winson Chungcd99cd32015-04-29 11:03:24 -0700506 Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
Winson Chung006ee262015-08-03 14:40:11 -0700507 toWorkspacePage, animated, layerViews);
508
509 // Animate the search bar
510 startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
511 animated ? revealDuration : 0, overlaySearchBarView);
Winson Chungb745afb2015-03-02 11:51:23 -0800512
513 if (animated && initialized) {
Winson Chungb745afb2015-03-02 11:51:23 -0800514 // Play the workspace animation
515 if (workspaceAnim != null) {
Winson Chung006ee262015-08-03 14:40:11 -0700516 animation.play(workspaceAnim);
Winson Chungb745afb2015-03-02 11:51:23 -0800517 }
518
519 // hideAppsCustomizeHelper is called in some cases when it is already hidden
520 // don't perform all these no-op animations. In particularly, this was causing
521 // the all-apps button to pop in and out.
522 if (fromView.getVisibility() == View.VISIBLE) {
523 int width = revealView.getMeasuredWidth();
524 int height = revealView.getMeasuredHeight();
Sunny Goyalf7a29e82015-04-24 15:20:43 -0700525 float revealRadius = (float) Math.hypot(width / 2, height / 2);
Winson Chungb745afb2015-03-02 11:51:23 -0800526 revealView.setVisibility(View.VISIBLE);
527 revealView.setAlpha(1f);
528 revealView.setTranslationY(0);
529 layerViews.put(revealView, BUILD_AND_SET_LAYER);
Winson Chungb745afb2015-03-02 11:51:23 -0800530
531 // Calculate the final animation values
532 final float revealViewToXDrift;
533 final float revealViewToYDrift;
534 if (material) {
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700535 int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
536 buttonView, null);
537 revealViewToYDrift = buttonViewToPanelDelta[1];
538 revealViewToXDrift = buttonViewToPanelDelta[0];
Winson Chungb745afb2015-03-02 11:51:23 -0800539 } else {
540 revealViewToYDrift = 2 * height / 3;
541 revealViewToXDrift = 0;
542 }
543
544 // The vertical motion of the apps panel should be delayed by one frame
545 // from the conceal animation in order to give the right feel. We correspondingly
546 // shorten the duration so that the slide and conceal end at the same time.
547 TimeInterpolator decelerateInterpolator = material ?
548 new LogDecelerateInterpolator(100, 0) :
549 new DecelerateInterpolator(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700550 ObjectAnimator panelDriftY = ObjectAnimator.ofFloat(revealView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800551 0, revealViewToYDrift);
552 panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
553 panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
554 panelDriftY.setInterpolator(decelerateInterpolator);
Winson Chung006ee262015-08-03 14:40:11 -0700555 animation.play(panelDriftY);
Winson Chungb745afb2015-03-02 11:51:23 -0800556
Adam Cohen15588932015-05-26 23:03:31 -0700557 ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
Winson Chungb745afb2015-03-02 11:51:23 -0800558 0, revealViewToXDrift);
559 panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
560 panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
561 panelDriftX.setInterpolator(decelerateInterpolator);
Winson Chung006ee262015-08-03 14:40:11 -0700562 animation.play(panelDriftX);
Winson Chungb745afb2015-03-02 11:51:23 -0800563
564 // Setup animation for the reveal panel alpha
565 final float revealViewToAlpha = !material ? 0f :
566 pCb.getMaterialRevealViewFinalAlpha(revealView);
567 if (revealViewToAlpha != 1f) {
Adam Cohen15588932015-05-26 23:03:31 -0700568 ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
Winson Chungb745afb2015-03-02 11:51:23 -0800569 1f, revealViewToAlpha);
570 panelAlpha.setDuration(material ? revealDuration : 150);
571 panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
572 panelAlpha.setInterpolator(decelerateInterpolator);
Winson Chung006ee262015-08-03 14:40:11 -0700573 animation.play(panelAlpha);
Winson Chungb745afb2015-03-02 11:51:23 -0800574 }
575
576 // Setup the animation for the content view
577 layerViews.put(contentView, BUILD_AND_SET_LAYER);
578
579 // Create the individual animators
Adam Cohen15588932015-05-26 23:03:31 -0700580 ObjectAnimator pageDrift = ObjectAnimator.ofFloat(contentView, "translationY",
Winson Chungb745afb2015-03-02 11:51:23 -0800581 0, revealViewToYDrift);
582 contentView.setTranslationY(0);
583 pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
584 pageDrift.setInterpolator(decelerateInterpolator);
585 pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
Winson Chung006ee262015-08-03 14:40:11 -0700586 animation.play(pageDrift);
Winson Chungb745afb2015-03-02 11:51:23 -0800587
588 contentView.setAlpha(1f);
Adam Cohen15588932015-05-26 23:03:31 -0700589 ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
Winson Chungb745afb2015-03-02 11:51:23 -0800590 itemsAlpha.setDuration(100);
591 itemsAlpha.setInterpolator(decelerateInterpolator);
Winson Chung006ee262015-08-03 14:40:11 -0700592 animation.play(itemsAlpha);
Winson Chungb745afb2015-03-02 11:51:23 -0800593
Winson Chungef7f8742015-06-04 17:18:17 -0700594 if (overlaySearchBarView != null) {
595 overlaySearchBarView.setAlpha(1f);
596 ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
597 searchAlpha.setDuration(material ? 100 : 150);
598 searchAlpha.setInterpolator(decelerateInterpolator);
599 searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
600 layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
Winson Chung006ee262015-08-03 14:40:11 -0700601 animation.play(searchAlpha);
Winson Chungef7f8742015-06-04 17:18:17 -0700602 }
603
Winson Chungb745afb2015-03-02 11:51:23 -0800604 if (material) {
605 // Animate the all apps button
606 float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
607 AnimatorListenerAdapter listener =
Hyunyoung Song98ff38a2015-07-10 17:50:13 -0700608 pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView);
Adam Cohen15588932015-05-26 23:03:31 -0700609 Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
610 height / 2, revealRadius, finalRadius);
Winson Chungb745afb2015-03-02 11:51:23 -0800611 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
612 reveal.setDuration(revealDuration);
613 reveal.setStartDelay(itemsAlphaStagger);
614 if (listener != null) {
615 reveal.addListener(listener);
616 }
Winson Chung006ee262015-08-03 14:40:11 -0700617 animation.play(reveal);
Winson Chungb745afb2015-03-02 11:51:23 -0800618 }
619
620 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
621 dispatchOnLauncherTransitionPrepare(toView, animated, true);
622 }
623
Winson Chung006ee262015-08-03 14:40:11 -0700624 animation.addListener(new AnimatorListenerAdapter() {
Winson Chungb745afb2015-03-02 11:51:23 -0800625 @Override
626 public void onAnimationEnd(Animator animation) {
627 fromView.setVisibility(View.GONE);
628 dispatchOnLauncherTransitionEnd(fromView, animated, true);
629 dispatchOnLauncherTransitionEnd(toView, animated, true);
630
631 // Run any queued runnables
632 if (onCompleteRunnable != null) {
633 onCompleteRunnable.run();
634 }
635
Winson Chungb745afb2015-03-02 11:51:23 -0800636 // Disable all necessary layers
637 for (View v : layerViews.keySet()) {
638 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
639 v.setLayerType(View.LAYER_TYPE_NONE, null);
640 }
641 }
642
643 // Reset page transforms
644 if (contentView != null) {
645 contentView.setTranslationX(0);
646 contentView.setTranslationY(0);
647 contentView.setAlpha(1);
648 }
Winson Chungef7f8742015-06-04 17:18:17 -0700649 if (overlaySearchBarView != null) {
650 overlaySearchBarView.setAlpha(1f);
651 }
Winson Chungb745afb2015-03-02 11:51:23 -0800652
653 // This can hold unnecessary references to views.
Winson Chung006ee262015-08-03 14:40:11 -0700654 cleanupAnimation();
Winson Chung76648c52015-07-10 14:33:23 -0700655 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800656 }
657 });
658
Winson Chung006ee262015-08-03 14:40:11 -0700659 final AnimatorSet stateAnimation = animation;
Winson Chungb745afb2015-03-02 11:51:23 -0800660 final Runnable startAnimRunnable = new Runnable() {
661 public void run() {
Winson Chung006ee262015-08-03 14:40:11 -0700662 // Check that mCurrentAnimation hasn't changed while
Winson Chungb745afb2015-03-02 11:51:23 -0800663 // we waited for a layout/draw pass
Winson Chung006ee262015-08-03 14:40:11 -0700664 if (mCurrentAnimation != stateAnimation)
Winson Chungb745afb2015-03-02 11:51:23 -0800665 return;
Winson Chung006ee262015-08-03 14:40:11 -0700666
Winson Chungb745afb2015-03-02 11:51:23 -0800667 dispatchOnLauncherTransitionStart(fromView, animated, false);
668 dispatchOnLauncherTransitionStart(toView, animated, false);
669
670 // Enable all necessary layers
Winson Chung11509ad2015-05-12 18:55:26 -0700671 boolean isLmpOrAbove = Utilities.isLmpOrAbove();
Winson Chungb745afb2015-03-02 11:51:23 -0800672 for (View v : layerViews.keySet()) {
673 if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
674 v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
675 }
Winson Chung11509ad2015-05-12 18:55:26 -0700676 if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
Winson Chungb745afb2015-03-02 11:51:23 -0800677 v.buildLayer();
678 }
679 }
Winson Chung006ee262015-08-03 14:40:11 -0700680 stateAnimation.start();
Winson Chungb745afb2015-03-02 11:51:23 -0800681 }
682 };
683 fromView.post(startAnimRunnable);
Winson Chung006ee262015-08-03 14:40:11 -0700684
685 return animation;
Winson Chungb745afb2015-03-02 11:51:23 -0800686 } else {
687 fromView.setVisibility(View.GONE);
688 dispatchOnLauncherTransitionPrepare(fromView, animated, true);
689 dispatchOnLauncherTransitionStart(fromView, animated, true);
690 dispatchOnLauncherTransitionEnd(fromView, animated, true);
691 dispatchOnLauncherTransitionPrepare(toView, animated, true);
692 dispatchOnLauncherTransitionStart(toView, animated, true);
693 dispatchOnLauncherTransitionEnd(toView, animated, true);
Winson Chung76648c52015-07-10 14:33:23 -0700694 pCb.onTransitionComplete();
Winson Chungb745afb2015-03-02 11:51:23 -0800695
696 // Run any queued runnables
697 if (onCompleteRunnable != null) {
698 onCompleteRunnable.run();
699 }
Winson Chung006ee262015-08-03 14:40:11 -0700700
701 return null;
Winson Chungb745afb2015-03-02 11:51:23 -0800702 }
703 }
704
Winson Chung006ee262015-08-03 14:40:11 -0700705 /**
706 * Coordinates the workspace search bar animation along with the launcher state animation.
707 */
708 private void startWorkspaceSearchBarAnimation(AnimatorSet animation,
709 final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, int duration,
710 View overlaySearchBar) {
711 final SearchDropTargetBar.State toSearchBarState =
712 toWorkspaceState.getSearchDropTargetBarState();
713
714 if (overlaySearchBar != null) {
715 if ((toWorkspaceState == Workspace.State.NORMAL) &&
716 (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN)) {
717 // If we are transitioning from the overlay to the workspace, then show the
718 // workspace search bar immediately and let the overlay search bar fade out on top
719 mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
720 } else if (fromWorkspaceState == Workspace.State.NORMAL) {
721 // If we are transitioning from the workspace to the overlay, then keep the
722 // workspace search bar visible until the overlay search bar fades in on top
723 animation.addListener(new AnimatorListenerAdapter() {
724 @Override
725 public void onAnimationEnd(Animator animation) {
726 mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
727 }
728 });
729 } else {
730 // Otherwise, then just animate the workspace search bar normally
731 mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
732 }
733 } else {
734 // If there is no overlay search bar, then just animate the workspace search bar
735 mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
736 }
737 }
Winson Chungb745afb2015-03-02 11:51:23 -0800738
739 /**
740 * Dispatches the prepare-transition event to suitable views.
741 */
742 void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
743 if (v instanceof LauncherTransitionable) {
744 ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
745 toWorkspace);
746 }
747 }
748
749 /**
750 * Dispatches the start-transition event to suitable views.
751 */
752 void dispatchOnLauncherTransitionStart(View v, boolean animated, boolean toWorkspace) {
753 if (v instanceof LauncherTransitionable) {
754 ((LauncherTransitionable) v).onLauncherTransitionStart(mLauncher, animated,
755 toWorkspace);
756 }
757
758 // Update the workspace transition step as well
759 dispatchOnLauncherTransitionStep(v, 0f);
760 }
761
762 /**
763 * Dispatches the step-transition event to suitable views.
764 */
765 void dispatchOnLauncherTransitionStep(View v, float t) {
766 if (v instanceof LauncherTransitionable) {
767 ((LauncherTransitionable) v).onLauncherTransitionStep(mLauncher, t);
768 }
769 }
770
771 /**
772 * Dispatches the end-transition event to suitable views.
773 */
774 void dispatchOnLauncherTransitionEnd(View v, boolean animated, boolean toWorkspace) {
775 if (v instanceof LauncherTransitionable) {
776 ((LauncherTransitionable) v).onLauncherTransitionEnd(mLauncher, animated,
777 toWorkspace);
778 }
779
780 // Update the workspace transition step as well
781 dispatchOnLauncherTransitionStep(v, 1f);
782 }
783
784 /**
785 * Cancels the current animation.
786 */
787 private void cancelAnimation() {
Winson Chung006ee262015-08-03 14:40:11 -0700788 if (mCurrentAnimation != null) {
789 mCurrentAnimation.setDuration(0);
790 mCurrentAnimation.cancel();
791 mCurrentAnimation = null;
Winson Chungb745afb2015-03-02 11:51:23 -0800792 }
793 }
Winson Chung006ee262015-08-03 14:40:11 -0700794
Sunny Goyald1ea63f2015-08-05 12:32:47 -0700795 @Thunk void cleanupAnimation() {
Winson Chung006ee262015-08-03 14:40:11 -0700796 mCurrentAnimation = null;
797 }
Adam Cohen15588932015-05-26 23:03:31 -0700798}