blob: 9f25729602d116e0e4a2e61874ede92b16071715 [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
Sunny Goyalea609262017-10-25 15:47:38 -070019import static com.android.launcher3.LauncherState.NORMAL;
Sunny Goyal7185dd62018-03-14 17:51:49 -070020import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
Sunny Goyalea609262017-10-25 15:47:38 -070021
Winson Chungb745afb2015-03-02 11:51:23 -080022import android.animation.Animator;
23import android.animation.AnimatorListenerAdapter;
24import android.animation.AnimatorSet;
Sunny Goyalbe93f262017-10-20 17:05:27 -070025import android.os.Handler;
26import android.os.Looper;
Sunny Goyal210e1742019-10-17 12:05:38 -070027
28import androidx.annotation.IntDef;
Adam Cohen15588932015-05-26 23:03:31 -070029
Sunny Goyalbe93f262017-10-20 17:05:27 -070030import com.android.launcher3.anim.AnimationSuccessListener;
Sunny Goyal03102202017-10-27 11:05:26 -070031import com.android.launcher3.anim.AnimatorPlaybackController;
Sunny Goyale3c6d582017-12-07 12:45:49 -080032import com.android.launcher3.anim.AnimatorSetBuilder;
Sunny Goyal7185dd62018-03-14 17:51:49 -070033import com.android.launcher3.anim.PropertySetter;
34import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
vadimt8f9cf2f2019-06-10 18:01:58 -070035import com.android.launcher3.compat.AccessibilityManagerCompat;
Adam Cohen15588932015-05-26 23:03:31 -070036
Hyunyoung Songa310a802019-04-25 13:57:16 -070037import java.io.PrintWriter;
Tony Wickham6becf7c2018-04-19 11:39:34 -070038import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
Sunny Goyal7185dd62018-03-14 17:51:49 -070040import java.util.ArrayList;
41
Winson Chungb745afb2015-03-02 11:51:23 -080042/**
43 * TODO: figure out what kind of tests we can write for this
44 *
45 * Things to test when changing the following class.
46 * - Home from workspace
47 * - from center screen
48 * - from other screens
49 * - Home from all apps
50 * - from center screen
51 * - from other screens
52 * - Back from all apps
53 * - from center screen
54 * - from other screens
55 * - Launch app from workspace and quit
56 * - with back
57 * - with home
58 * - Launch app from all apps and quit
59 * - with back
60 * - with home
61 * - Go to a screen that's not the default, then all
62 * apps, and launch and app, and go back
63 * - with back
64 * -with home
65 * - On workspace, long press power and go back
66 * - with back
67 * - with home
68 * - On all apps, long press power and go back
69 * - with back
70 * - with home
71 * - On workspace, power off
72 * - On all apps, power off
73 * - Launch an app and turn off the screen while in that app
74 * - Go back with home key
75 * - Go back with back key TODO: make this not go to workspace
76 * - From all apps
77 * - From workspace
Sunny Goyal49bcf342018-01-11 13:59:53 -080078 * - Enter and exit car mode (becase it causes an extra configuration changed)
Winson Chungb745afb2015-03-02 11:51:23 -080079 * - From all apps
80 * - From the center workspace
81 * - From another workspace
82 */
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070083public class LauncherStateManager {
Winson Chungb745afb2015-03-02 11:51:23 -080084
Sunny Goyal3e3f44c2017-10-23 17:14:52 -070085 public static final String TAG = "StateManager";
Winson Chungb745afb2015-03-02 11:51:23 -080086
Tony Wickham6becf7c2018-04-19 11:39:34 -070087 // We separate the state animations into "atomic" and "non-atomic" components. The atomic
88 // components may be run atomically - that is, all at once, instead of user-controlled. However,
89 // atomic components are not restricted to this purpose; they can be user-controlled alongside
Tony1787ee92019-03-20 12:38:35 -050090 // non atomic components as well. Note that each gesture model has exactly one atomic component,
91 // ATOMIC_OVERVIEW_SCALE_COMPONENT *or* ATOMIC_OVERVIEW_PEEK_COMPONENT.
Tony Wickham6becf7c2018-04-19 11:39:34 -070092 @IntDef(flag = true, value = {
93 NON_ATOMIC_COMPONENT,
Tony1787ee92019-03-20 12:38:35 -050094 ATOMIC_OVERVIEW_SCALE_COMPONENT,
95 ATOMIC_OVERVIEW_PEEK_COMPONENT,
Tony Wickham6becf7c2018-04-19 11:39:34 -070096 })
97 @Retention(RetentionPolicy.SOURCE)
98 public @interface AnimationComponents {}
99 public static final int NON_ATOMIC_COMPONENT = 1 << 0;
Tony1787ee92019-03-20 12:38:35 -0500100 public static final int ATOMIC_OVERVIEW_SCALE_COMPONENT = 1 << 1;
101 public static final int ATOMIC_OVERVIEW_PEEK_COMPONENT = 1 << 2;
Tony Wickham6becf7c2018-04-19 11:39:34 -0700102
Tony1787ee92019-03-20 12:38:35 -0500103 public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_OVERVIEW_SCALE_COMPONENT
104 | ATOMIC_OVERVIEW_PEEK_COMPONENT;
Tony Wickham6becf7c2018-04-19 11:39:34 -0700105
Sunny Goyalaeb16432017-10-16 11:46:41 -0700106 private final AnimationConfig mConfig = new AnimationConfig();
Sunny Goyalbe93f262017-10-20 17:05:27 -0700107 private final Handler mUiHandler;
108 private final Launcher mLauncher;
Sunny Goyal7185dd62018-03-14 17:51:49 -0700109 private final ArrayList<StateListener> mListeners = new ArrayList<>();
Winson Chungb745afb2015-03-02 11:51:23 -0800110
Sunny Goyalc4bb3732019-06-20 11:34:14 -0700111 // Animators which are run on properties also controlled by state animations.
112 private Animator[] mStateElementAnimators;
113
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800114 private StateHandler[] mStateHandlers;
Sunny Goyalea609262017-10-25 15:47:38 -0700115 private LauncherState mState = NORMAL;
116
Sunny Goyal49bcf342018-01-11 13:59:53 -0800117 private LauncherState mLastStableState = NORMAL;
118 private LauncherState mCurrentStableState = NORMAL;
119
Sunny Goyalaf161fd2018-02-05 16:34:57 -0800120 private LauncherState mRestState;
121
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800122 public LauncherStateManager(Launcher l) {
Sunny Goyalbe93f262017-10-20 17:05:27 -0700123 mUiHandler = new Handler(Looper.getMainLooper());
Winson Chungb745afb2015-03-02 11:51:23 -0800124 mLauncher = l;
Winson Chungb745afb2015-03-02 11:51:23 -0800125 }
126
Sunny Goyalea609262017-10-25 15:47:38 -0700127 public LauncherState getState() {
128 return mState;
129 }
130
vadimta42dc532019-04-15 16:50:12 -0700131 public LauncherState getCurrentStableState() {
132 return mCurrentStableState;
133 }
134
Hyunyoung Songa310a802019-04-25 13:57:16 -0700135 public void dump(String prefix, PrintWriter writer) {
Winson Chungef528762019-09-06 12:05:52 -0700136 writer.println(prefix + "LauncherState:");
Hyunyoung Songa310a802019-04-25 13:57:16 -0700137 writer.println(prefix + "\tmLastStableState:" + mLastStableState);
138 writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
139 writer.println(prefix + "\tmState:" + mState);
140 writer.println(prefix + "\tmRestState:" + mRestState);
141 writer.println(prefix + "\tisInTransition:" + (mConfig.mCurrentAnimation != null));
142 }
143
Sunny Goyalde5535a2017-12-08 13:39:39 -0800144 public StateHandler[] getStateHandlers() {
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800145 if (mStateHandlers == null) {
Sunny Goyal210e1742019-10-17 12:05:38 -0700146 mStateHandlers = mLauncher.createStateHandlers();
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800147 }
148 return mStateHandlers;
149 }
150
Sunny Goyal7185dd62018-03-14 17:51:49 -0700151 public void addStateListener(StateListener listener) {
152 mListeners.add(listener);
153 }
154
155 public void removeStateListener(StateListener listener) {
156 mListeners.remove(listener);
Jon Miranda758d5042017-11-08 14:38:23 -0800157 }
158
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700159 /**
Sunny Goyalb52ff222019-02-14 13:49:36 -0800160 * Returns true if the state changes should be animated.
161 */
162 public boolean shouldAnimateStateChange() {
163 return !mLauncher.isForceInvisible() && mLauncher.isStarted();
164 }
165
166 /**
Riddle Hsu7a7468f2019-11-21 01:32:58 +0800167 * @return {@code true} if the state matches the current state and there is no active
168 * transition to different state.
169 */
170 public boolean isInStableState(LauncherState state) {
171 return mState == state && mCurrentStableState == state
172 && (mConfig.mTargetState == null || mConfig.mTargetState == state);
173 }
174
175 /**
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700176 * @see #goToState(LauncherState, boolean, Runnable)
177 */
178 public void goToState(LauncherState state) {
Sunny Goyalb52ff222019-02-14 13:49:36 -0800179 goToState(state, shouldAnimateStateChange());
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700180 }
181
182 /**
183 * @see #goToState(LauncherState, boolean, Runnable)
184 */
185 public void goToState(LauncherState state, boolean animated) {
186 goToState(state, animated, 0, null);
187 }
188
189 /**
190 * Changes the Launcher state to the provided state.
191 *
192 * @param animated false if the state should change immediately without any animation,
193 * true otherwise
194 * @paras onCompleteRunnable any action to perform at the end of the transition, of null.
195 */
Sunny Goyalbe93f262017-10-20 17:05:27 -0700196 public void goToState(LauncherState state, boolean animated, Runnable onCompleteRunnable) {
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700197 goToState(state, animated, 0, onCompleteRunnable);
198 }
199
200 /**
201 * Changes the Launcher state to the provided state after the given delay.
202 */
203 public void goToState(LauncherState state, long delay, Runnable onCompleteRunnable) {
204 goToState(state, true, delay, onCompleteRunnable);
205 }
206
207 /**
208 * Changes the Launcher state to the provided state after the given delay.
209 */
210 public void goToState(LauncherState state, long delay) {
211 goToState(state, true, delay, null);
212 }
213
Sunny Goyalce8809a2018-01-18 14:02:47 -0800214 public void reapplyState() {
Sunny Goyaled2d2bc2018-04-19 12:34:43 -0700215 reapplyState(false);
216 }
217
218 public void reapplyState(boolean cancelCurrentAnimation) {
Sunny Goyal8be50402019-06-18 12:02:42 -0700219 boolean wasInAnimation = mConfig.mCurrentAnimation != null;
Sunny Goyaled2d2bc2018-04-19 12:34:43 -0700220 if (cancelCurrentAnimation) {
Sunny Goyalc4bb3732019-06-20 11:34:14 -0700221 cancelAllStateElementAnimation();
Sunny Goyaled2d2bc2018-04-19 12:34:43 -0700222 cancelAnimation();
223 }
Sunny Goyalce8809a2018-01-18 14:02:47 -0800224 if (mConfig.mCurrentAnimation == null) {
225 for (StateHandler handler : getStateHandlers()) {
226 handler.setState(mState);
227 }
Sunny Goyal8be50402019-06-18 12:02:42 -0700228 if (wasInAnimation) {
229 onStateTransitionEnd(mState);
230 }
Sunny Goyalce8809a2018-01-18 14:02:47 -0800231 }
232 }
233
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700234 private void goToState(LauncherState state, boolean animated, long delay,
Sunny Goyalf58e0df2018-03-01 10:24:20 -0800235 final Runnable onCompleteRunnable) {
Sunny Goyal7368fa42019-04-22 09:58:14 -0700236 animated &= Utilities.areAnimationsEnabled(mLauncher);
Sunny Goyalf58e0df2018-03-01 10:24:20 -0800237 if (mLauncher.isInState(state)) {
238 if (mConfig.mCurrentAnimation == null) {
239 // Run any queued runnable
240 if (onCompleteRunnable != null) {
241 onCompleteRunnable.run();
242 }
243 return;
Sunny Goyald754dcd2018-04-12 14:16:42 -0700244 } else if (!mConfig.userControlled && animated && mConfig.mTargetState == state) {
Sunny Goyalf58e0df2018-03-01 10:24:20 -0800245 // We are running the same animation as requested
246 if (onCompleteRunnable != null) {
247 mConfig.mCurrentAnimation.addListener(new AnimationSuccessListener() {
248 @Override
249 public void onAnimationSuccess(Animator animator) {
250 onCompleteRunnable.run();
251 }
252 });
253 }
254 return;
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700255 }
Sunny Goyal3e3f44c2017-10-23 17:14:52 -0700256 }
257
Tony67f9cad2018-05-29 17:33:16 -0700258 // Cancel the current animation. This will reset mState to mCurrentStableState, so store it.
259 LauncherState fromState = mState;
Sunny Goyalaeb16432017-10-16 11:46:41 -0700260 mConfig.reset();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700261
Sunny Goyalaeb16432017-10-16 11:46:41 -0700262 if (!animated) {
Sunny Goyalc4bb3732019-06-20 11:34:14 -0700263 cancelAllStateElementAnimation();
Sunny Goyalbc683e92017-12-06 10:25:07 -0800264 onStateTransitionStart(state);
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800265 for (StateHandler handler : getStateHandlers()) {
266 handler.setState(state);
267 }
Sunny Goyal7185dd62018-03-14 17:51:49 -0700268
Sunny Goyalbc683e92017-12-06 10:25:07 -0800269 onStateTransitionEnd(state);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700270
Sunny Goyalbe93f262017-10-20 17:05:27 -0700271 // Run any queued runnable
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700272 if (onCompleteRunnable != null) {
273 onCompleteRunnable.run();
274 }
275 return;
276 }
277
Sunny Goyal2db53422019-03-01 16:06:12 -0800278 if (delay > 0) {
279 // Create the animation after the delay as some properties can change between preparing
280 // the animation and running the animation.
281 int startChangeId = mConfig.mChangeId;
282 mUiHandler.postDelayed(() -> {
283 if (mConfig.mChangeId == startChangeId) {
284 goToStateAnimated(state, fromState, onCompleteRunnable);
285 }
286 }, delay);
287 } else {
288 goToStateAnimated(state, fromState, onCompleteRunnable);
289 }
290 }
291
292 private void goToStateAnimated(LauncherState state, LauncherState fromState,
293 Runnable onCompleteRunnable) {
Sunny Goyal03102202017-10-27 11:05:26 -0700294 // Since state NORMAL can be reached from multiple states, just assume that the
295 // transition plays in reverse and use the same duration as previous state.
Tony Wickhamc7203ad2020-02-03 18:36:06 -0800296 mConfig.duration = state == NORMAL
297 ? fromState.getTransitionDuration(mLauncher)
298 : state.getTransitionDuration(mLauncher);
Sunny Goyal03102202017-10-27 11:05:26 -0700299
Tony Wickhamb2ddf102018-05-16 12:23:12 -0700300 AnimatorSetBuilder builder = new AnimatorSetBuilder();
Tony67f9cad2018-05-29 17:33:16 -0700301 prepareForAtomicAnimation(fromState, state, builder);
Sunny Goyale3c6d582017-12-07 12:45:49 -0800302 AnimatorSet animation = createAnimationToNewWorkspaceInternal(
Tony Wickhamb2ddf102018-05-16 12:23:12 -0700303 state, builder, onCompleteRunnable);
Sunny Goyal2db53422019-03-01 16:06:12 -0800304 mUiHandler.post(new StartAnimRunnable(animation));
Winson Chungb745afb2015-03-02 11:51:23 -0800305 }
306
Sunny Goyal03102202017-10-27 11:05:26 -0700307 /**
Tony Wickhamb2ddf102018-05-16 12:23:12 -0700308 * Prepares for a non-user controlled animation from fromState to toState. Preparations include:
309 * - Setting interpolators for various animations included in the state transition.
310 * - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
311 */
312 public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
313 AnimatorSetBuilder builder) {
Tony Wickham59c69012019-06-18 16:34:37 -0700314 toState.prepareForAtomicAnimation(mLauncher, fromState, builder);
Tony Wickhamb2ddf102018-05-16 12:23:12 -0700315 }
316
Tony1787ee92019-03-20 12:38:35 -0500317 public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
318 AnimatorSetBuilder builder, @AnimationComponents int atomicComponent, long duration) {
319 prepareForAtomicAnimation(fromState, toState, builder);
320 AnimationConfig config = new AnimationConfig();
321 config.animComponents = atomicComponent;
322 config.duration = duration;
323 for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
324 handler.setStateWithAnimation(toState, builder, config);
325 }
326 return builder.build();
327 }
328
Tony Wickhamb2ddf102018-05-16 12:23:12 -0700329 /**
Sunny Goyal03102202017-10-27 11:05:26 -0700330 * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
Sunny Goyald936f6a2018-06-01 12:18:19 -0700331 * state transition. The UI is force-set to fromState before creating the controller.
332 * @param fromState the initial state for the transition.
333 * @param state the final state for the transition.
334 * @param duration intended duration for normal playback. Use higher duration for better
335 * accuracy.
336 */
337 public AnimatorPlaybackController createAnimationToNewWorkspace(
338 LauncherState fromState, LauncherState state, long duration) {
Sunny Goyal379e8e02018-07-12 14:59:09 -0700339 // Since we are creating a state animation to a different state, temporarily prevent state
340 // change as part of config reset.
341 LauncherState originalRestState = mRestState;
342 mRestState = state;
Sunny Goyald936f6a2018-06-01 12:18:19 -0700343 mConfig.reset();
Sunny Goyal379e8e02018-07-12 14:59:09 -0700344 mRestState = originalRestState;
345
Sunny Goyald936f6a2018-06-01 12:18:19 -0700346 for (StateHandler handler : getStateHandlers()) {
347 handler.setState(fromState);
348 }
349
350 return createAnimationToNewWorkspace(state, duration);
351 }
352
353 /**
354 * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
Sunny Goyal03102202017-10-27 11:05:26 -0700355 * state transition.
356 * @param state the final state for the transition.
357 * @param duration intended duration for normal playback. Use higher duration for better
358 * accuracy.
359 */
Sunny Goyal85525172017-11-06 13:00:42 -0800360 public AnimatorPlaybackController createAnimationToNewWorkspace(
Sunny Goyal03102202017-10-27 11:05:26 -0700361 LauncherState state, long duration) {
Tony Wickham6becf7c2018-04-19 11:39:34 -0700362 return createAnimationToNewWorkspace(state, duration, LauncherStateManager.ANIM_ALL);
363 }
364
365 public AnimatorPlaybackController createAnimationToNewWorkspace(
366 LauncherState state, long duration, @AnimationComponents int animComponents) {
367 return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null,
368 animComponents);
Sunny Goyale3c6d582017-12-07 12:45:49 -0800369 }
370
Tony31fbd4c2018-04-27 12:33:24 -0500371 public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
Tony Wickham6becf7c2018-04-19 11:39:34 -0700372 AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable,
373 @AnimationComponents int animComponents) {
Sunny Goyalaeb16432017-10-16 11:46:41 -0700374 mConfig.reset();
Sunny Goyal03102202017-10-27 11:05:26 -0700375 mConfig.userControlled = true;
Tony Wickham6becf7c2018-04-19 11:39:34 -0700376 mConfig.animComponents = animComponents;
Sunny Goyal03102202017-10-27 11:05:26 -0700377 mConfig.duration = duration;
Tony31fbd4c2018-04-27 12:33:24 -0500378 mConfig.playbackController = AnimatorPlaybackController.wrap(
379 createAnimationToNewWorkspaceInternal(state, builder, null), duration,
380 onCancelRunnable);
381 return mConfig.playbackController;
Sunny Goyal03102202017-10-27 11:05:26 -0700382 }
383
384 protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
Sunny Goyale3c6d582017-12-07 12:45:49 -0800385 AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
Jon Miranda8b08a1f2018-02-15 11:32:25 -0800386
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800387 for (StateHandler handler : getStateHandlers()) {
Sunny Goyal3c585dd2017-12-07 11:42:33 -0800388 handler.setStateWithAnimation(state, builder, mConfig);
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800389 }
Sunny Goyale3c6d582017-12-07 12:45:49 -0800390
391 final AnimatorSet animation = builder.build();
Sunny Goyalbe93f262017-10-20 17:05:27 -0700392 animation.addListener(new AnimationSuccessListener() {
Sunny Goyalea609262017-10-25 15:47:38 -0700393
394 @Override
395 public void onAnimationStart(Animator animation) {
396 // Change the internal state only when the transition actually starts
Sunny Goyalbc683e92017-12-06 10:25:07 -0800397 onStateTransitionStart(state);
Jon Miranda758d5042017-11-08 14:38:23 -0800398 }
399
400 @Override
Sunny Goyalbe93f262017-10-20 17:05:27 -0700401 public void onAnimationSuccess(Animator animator) {
Sunny Goyalaeb16432017-10-16 11:46:41 -0700402 // Run any queued runnables
403 if (onCompleteRunnable != null) {
404 onCompleteRunnable.run();
405 }
Sunny Goyalbc683e92017-12-06 10:25:07 -0800406 onStateTransitionEnd(state);
Sunny Goyalaeb16432017-10-16 11:46:41 -0700407 }
408 });
Sunny Goyald754dcd2018-04-12 14:16:42 -0700409 mConfig.setAnimation(animation, state);
Sunny Goyalbe93f262017-10-20 17:05:27 -0700410 return mConfig.mCurrentAnimation;
Tony Wickham6d1bbe32015-09-16 12:06:38 -0700411 }
412
Sunny Goyalbc683e92017-12-06 10:25:07 -0800413 private void onStateTransitionStart(LauncherState state) {
Matthew Ngf8fafa22018-06-13 16:36:29 -0700414 if (mState != state) {
415 mState.onStateDisabled(mLauncher);
416 }
Sunny Goyalea609262017-10-25 15:47:38 -0700417 mState = state;
418 mState.onStateEnabled(mLauncher);
Winson Chungef528762019-09-06 12:05:52 -0700419 mLauncher.onStateSetStart(mState);
Sunny Goyalbc683e92017-12-06 10:25:07 -0800420
421 if (state.disablePageClipping) {
422 // Only disable clipping if needed, otherwise leave it as previous value.
423 mLauncher.getWorkspace().setClipChildren(false);
424 }
Sunny Goyal7f455142019-01-28 11:56:13 -0800425
426 for (int i = mListeners.size() - 1; i >= 0; i--) {
427 mListeners.get(i).onStateTransitionStart(state);
428 }
Sunny Goyalbc683e92017-12-06 10:25:07 -0800429 }
430
431 private void onStateTransitionEnd(LauncherState state) {
Sunny Goyal49bcf342018-01-11 13:59:53 -0800432 // Only change the stable states after the transitions have finished
433 if (state != mCurrentStableState) {
434 mLastStableState = state.getHistoryForState(mCurrentStableState);
435 mCurrentStableState = state;
436 }
437
Sunny Goyal75c59ac2018-01-15 14:23:11 -0800438 state.onStateTransitionEnd(mLauncher);
Winson Chungef528762019-09-06 12:05:52 -0700439 mLauncher.onStateSetEnd(state);
Sunny Goyalaf161fd2018-02-05 16:34:57 -0800440
441 if (state == NORMAL) {
442 setRestState(null);
443 }
Winson Chungd7823942018-02-16 03:23:47 +0000444
Sunny Goyal7f455142019-01-28 11:56:13 -0800445 for (int i = mListeners.size() - 1; i >= 0; i--) {
446 mListeners.get(i).onStateTransitionComplete(state);
447 }
vadimt8f9cf2f2019-06-10 18:01:58 -0700448
449 AccessibilityManagerCompat.sendStateEventToTest(mLauncher, state.ordinal);
Winson Chungd7823942018-02-16 03:23:47 +0000450 }
451
Sunny Goyal49bcf342018-01-11 13:59:53 -0800452 public LauncherState getLastState() {
453 return mLastStableState;
454 }
455
Sunny Goyalaf161fd2018-02-05 16:34:57 -0800456 public void moveToRestState() {
Sunny Goyal65860622018-03-12 13:40:58 -0700457 if (mConfig.mCurrentAnimation != null && mConfig.userControlled) {
458 // The user is doing something. Lets not mess it up
459 return;
460 }
Sunny Goyalaf161fd2018-02-05 16:34:57 -0800461 if (mState.disableRestore) {
462 goToState(getRestState());
463 // Reset history
464 mLastStableState = NORMAL;
465 }
466 }
467
468 public LauncherState getRestState() {
469 return mRestState == null ? NORMAL : mRestState;
470 }
471
472 public void setRestState(LauncherState restState) {
473 mRestState = restState;
474 }
475
Tony Wickham6d1bbe32015-09-16 12:06:38 -0700476 /**
Winson Chungb745afb2015-03-02 11:51:23 -0800477 * Cancels the current animation.
478 */
Sunny Goyalbe93f262017-10-20 17:05:27 -0700479 public void cancelAnimation() {
480 mConfig.reset();
Winson Chung006ee262015-08-03 14:40:11 -0700481 }
Sunny Goyaldb364372016-10-26 19:12:47 -0700482
Tony31fbd4c2018-04-27 12:33:24 -0500483 public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
Sunny Goyald6692ce2018-06-06 18:41:32 +0000484 clearCurrentAnimation();
Tony31fbd4c2018-04-27 12:33:24 -0500485 setCurrentAnimation(controller.getTarget());
486 mConfig.userControlled = true;
487 mConfig.playbackController = controller;
488 }
489
Sunny Goyala2467272018-03-16 14:53:05 -0700490 /**
491 * Sets the animation as the current state animation, i.e., canceled when
492 * starting another animation and may block some launcher interactions while running.
Sunny Goyalb35f50c2018-04-09 16:30:51 -0700493 *
494 * @param childAnimations Set of animations with the new target is controlling.
Sunny Goyala2467272018-03-16 14:53:05 -0700495 */
Sunny Goyalb35f50c2018-04-09 16:30:51 -0700496 public void setCurrentAnimation(AnimatorSet anim, Animator... childAnimations) {
497 for (Animator childAnim : childAnimations) {
Sunny Goyala6616de2018-05-04 10:09:41 -0700498 if (childAnim == null) {
499 continue;
500 }
501 if (mConfig.playbackController != null
502 && mConfig.playbackController.getTarget() == childAnim) {
Tony Wickhamd4ece9a2018-05-10 09:55:07 -0700503 clearCurrentAnimation();
Sunny Goyala6616de2018-05-04 10:09:41 -0700504 break;
505 } else if (mConfig.mCurrentAnimation == childAnim) {
Tony Wickhamd4ece9a2018-05-10 09:55:07 -0700506 clearCurrentAnimation();
Sunny Goyalb35f50c2018-04-09 16:30:51 -0700507 break;
508 }
509 }
Sunny Goyal1c6d5662018-04-02 15:27:28 -0700510 boolean reapplyNeeded = mConfig.mCurrentAnimation != null;
Sunny Goyala2467272018-03-16 14:53:05 -0700511 cancelAnimation();
Sunny Goyal1c6d5662018-04-02 15:27:28 -0700512 if (reapplyNeeded) {
513 reapplyState();
Sunny Goyal9cc1b2f2019-05-03 11:53:12 -0700514 // Dispatch on transition end, so that any transient property is cleared.
515 onStateTransitionEnd(mState);
Sunny Goyal1c6d5662018-04-02 15:27:28 -0700516 }
Sunny Goyald754dcd2018-04-12 14:16:42 -0700517 mConfig.setAnimation(anim, null);
Sunny Goyala2467272018-03-16 14:53:05 -0700518 }
519
Sunny Goyalc4bb3732019-06-20 11:34:14 -0700520 private void cancelAllStateElementAnimation() {
521 if (mStateElementAnimators == null) {
522 return;
523 }
524
525 for (Animator animator : mStateElementAnimators) {
526 if (animator != null) {
527 animator.cancel();
528 }
529 }
530 }
531
532 /**
533 * Cancels a currently running gesture animation
534 */
535 public void cancelStateElementAnimation(int index) {
536 if (mStateElementAnimators == null) {
537 return;
538 }
539 if (mStateElementAnimators[index] != null) {
540 mStateElementAnimators[index].cancel();
541 }
542 }
543
544 public Animator createStateElementAnimation(int index, float... values) {
545 cancelStateElementAnimation(index);
546 LauncherAppTransitionManager latm = mLauncher.getAppTransitionManager();
547 if (mStateElementAnimators == null) {
548 mStateElementAnimators = new Animator[latm.getStateElementAnimationsCount()];
549 }
550 Animator anim = latm.createStateElementAnimation(index, values);
551 mStateElementAnimators[index] = anim;
552 anim.addListener(new AnimatorListenerAdapter() {
553 @Override
554 public void onAnimationEnd(Animator animation) {
555 mStateElementAnimators[index] = null;
556 }
557 });
558 return anim;
559 }
560
Tony Wickhamd4ece9a2018-05-10 09:55:07 -0700561 private void clearCurrentAnimation() {
562 if (mConfig.mCurrentAnimation != null) {
563 mConfig.mCurrentAnimation.removeListener(mConfig);
564 mConfig.mCurrentAnimation = null;
565 }
566 mConfig.playbackController = null;
567 }
568
Sunny Goyaldb364372016-10-26 19:12:47 -0700569 private class StartAnimRunnable implements Runnable {
570
571 private final AnimatorSet mAnim;
Sunny Goyaldb364372016-10-26 19:12:47 -0700572
Vadim Tryshev1b0c5032018-05-23 12:54:27 -0700573 public StartAnimRunnable(AnimatorSet anim) {
Sunny Goyaldb364372016-10-26 19:12:47 -0700574 mAnim = anim;
Sunny Goyaldb364372016-10-26 19:12:47 -0700575 }
576
577 @Override
578 public void run() {
Sunny Goyalbe93f262017-10-20 17:05:27 -0700579 if (mConfig.mCurrentAnimation != mAnim) {
Sunny Goyaldb364372016-10-26 19:12:47 -0700580 return;
581 }
Sunny Goyaldb364372016-10-26 19:12:47 -0700582 mAnim.start();
583 }
584 }
Sunny Goyalaeb16432017-10-16 11:46:41 -0700585
Sunny Goyalbe93f262017-10-20 17:05:27 -0700586 public static class AnimationConfig extends AnimatorListenerAdapter {
Sunny Goyalea609262017-10-25 15:47:38 -0700587 public long duration;
Sunny Goyal03102202017-10-27 11:05:26 -0700588 public boolean userControlled;
Tony31fbd4c2018-04-27 12:33:24 -0500589 public AnimatorPlaybackController playbackController;
Tony Wickham6becf7c2018-04-19 11:39:34 -0700590 public @AnimationComponents int animComponents = ANIM_ALL;
Tony31fbd4c2018-04-27 12:33:24 -0500591 private PropertySetter mPropertySetter;
Sunny Goyalaeb16432017-10-16 11:46:41 -0700592
Sunny Goyalbe93f262017-10-20 17:05:27 -0700593 private AnimatorSet mCurrentAnimation;
Sunny Goyald754dcd2018-04-12 14:16:42 -0700594 private LauncherState mTargetState;
Sunny Goyal2db53422019-03-01 16:06:12 -0800595 // Id to keep track of config changes, to tie an animation with the corresponding request
596 private int mChangeId = 0;
Sunny Goyalaeb16432017-10-16 11:46:41 -0700597
Tony31fbd4c2018-04-27 12:33:24 -0500598 /**
599 * Cancels the current animation and resets config variables.
600 */
Sunny Goyalaeb16432017-10-16 11:46:41 -0700601 public void reset() {
Sunny Goyalea609262017-10-25 15:47:38 -0700602 duration = 0;
Sunny Goyal03102202017-10-27 11:05:26 -0700603 userControlled = false;
Tony Wickham6becf7c2018-04-19 11:39:34 -0700604 animComponents = ANIM_ALL;
Tony31fbd4c2018-04-27 12:33:24 -0500605 mPropertySetter = null;
Sunny Goyald754dcd2018-04-12 14:16:42 -0700606 mTargetState = null;
Sunny Goyalbe93f262017-10-20 17:05:27 -0700607
Tony31fbd4c2018-04-27 12:33:24 -0500608 if (playbackController != null) {
609 playbackController.getAnimationPlayer().cancel();
610 playbackController.dispatchOnCancel();
611 } else if (mCurrentAnimation != null) {
Sunny Goyalbe93f262017-10-20 17:05:27 -0700612 mCurrentAnimation.setDuration(0);
613 mCurrentAnimation.cancel();
Sunny Goyalbe93f262017-10-20 17:05:27 -0700614 }
Tony31fbd4c2018-04-27 12:33:24 -0500615
616 mCurrentAnimation = null;
617 playbackController = null;
Sunny Goyal2db53422019-03-01 16:06:12 -0800618 mChangeId ++;
Sunny Goyalaeb16432017-10-16 11:46:41 -0700619 }
620
Tony31fbd4c2018-04-27 12:33:24 -0500621 public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
622 if (mPropertySetter == null) {
623 mPropertySetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
Sunny Goyal7185dd62018-03-14 17:51:49 -0700624 : new AnimatedPropertySetter(duration, builder);
625 }
Tony31fbd4c2018-04-27 12:33:24 -0500626 return mPropertySetter;
Sunny Goyal7185dd62018-03-14 17:51:49 -0700627 }
628
Sunny Goyalbe93f262017-10-20 17:05:27 -0700629 @Override
630 public void onAnimationEnd(Animator animation) {
Sunny Goyal87a6ad12018-06-05 11:56:08 -0700631 if (playbackController != null && playbackController.getTarget() == animation) {
632 playbackController = null;
633 }
Sunny Goyalbe93f262017-10-20 17:05:27 -0700634 if (mCurrentAnimation == animation) {
635 mCurrentAnimation = null;
636 }
637 }
638
Sunny Goyald754dcd2018-04-12 14:16:42 -0700639 public void setAnimation(AnimatorSet animation, LauncherState targetState) {
Sunny Goyalbe93f262017-10-20 17:05:27 -0700640 mCurrentAnimation = animation;
Sunny Goyald754dcd2018-04-12 14:16:42 -0700641 mTargetState = targetState;
Sunny Goyalbe93f262017-10-20 17:05:27 -0700642 mCurrentAnimation.addListener(this);
643 }
Tony Wickham6becf7c2018-04-19 11:39:34 -0700644
Tony1787ee92019-03-20 12:38:35 -0500645 public boolean playAtomicOverviewScaleComponent() {
646 return (animComponents & ATOMIC_OVERVIEW_SCALE_COMPONENT) != 0;
647 }
648
649 public boolean playAtomicOverviewPeekComponent() {
650 return (animComponents & ATOMIC_OVERVIEW_PEEK_COMPONENT) != 0;
Tony Wickham6becf7c2018-04-19 11:39:34 -0700651 }
652
653 public boolean playNonAtomicComponent() {
654 return (animComponents & NON_ATOMIC_COMPONENT) != 0;
655 }
Sunny Goyalaeb16432017-10-16 11:46:41 -0700656 }
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800657
658 public interface StateHandler {
659
660 /**
661 * Updates the UI to {@param state} without any animations
662 */
663 void setState(LauncherState state);
664
665 /**
666 * Sets the UI to {@param state} by animating any changes.
667 */
Sunny Goyal3c585dd2017-12-07 11:42:33 -0800668 void setStateWithAnimation(LauncherState toState,
Sunny Goyale3c6d582017-12-07 12:45:49 -0800669 AnimatorSetBuilder builder, AnimationConfig config);
Sunny Goyalc4fa8c32017-11-07 12:23:58 -0800670 }
Jon Miranda758d5042017-11-08 14:38:23 -0800671
672 public interface StateListener {
673
Jon Miranda758d5042017-11-08 14:38:23 -0800674 void onStateTransitionStart(LauncherState toState);
675 void onStateTransitionComplete(LauncherState finalState);
676 }
Adam Cohen15588932015-05-26 23:03:31 -0700677}