• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 
17 package com.android.quickstep.util;
18 
19 import static com.android.app.animation.Interpolators.clampToProgress;
20 import static com.android.launcher3.LauncherState.NORMAL;
21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
22 
23 import android.animation.AnimatorSet;
24 import android.os.BinderUtils;
25 import android.os.Bundle;
26 import android.os.IBinder;
27 import android.os.IRemoteCallback;
28 import android.view.animation.Interpolator;
29 
30 import androidx.annotation.NonNull;
31 import androidx.lifecycle.DefaultLifecycleObserver;
32 import androidx.lifecycle.LifecycleOwner;
33 
34 import com.android.launcher3.logging.StatsLogManager;
35 import com.android.launcher3.statemanager.BaseState;
36 import com.android.launcher3.statemanager.StateManager;
37 import com.android.launcher3.states.StateAnimationConfig;
38 import com.android.launcher3.util.RunnableList;
39 import com.android.launcher3.views.ActivityContext;
40 import com.android.quickstep.views.RecentsViewContainer;
41 
42 /**
43  * Utility class containing methods to help manage animations, interpolators, and timings.
44  */
45 public class AnimUtils {
46     private static final int DURATION_DEFAULT_SPLIT_DISMISS = 350;
47 
48     /**
49      * Fetches device-specific timings for the Overview > Split animation
50      * (splitscreen initiated from Overview).
51      */
getDeviceOverviewToSplitTimings(boolean isTablet)52     public static SplitAnimationTimings getDeviceOverviewToSplitTimings(boolean isTablet) {
53         return isTablet
54                 ? SplitAnimationTimings.TABLET_OVERVIEW_TO_SPLIT
55                 : SplitAnimationTimings.PHONE_OVERVIEW_TO_SPLIT;
56     }
57 
58     /**
59      * Fetches device-specific timings for the Split > Confirm animation
60      * (splitscreen confirmed by selecting a second app).
61      */
getDeviceSplitToConfirmTimings(boolean isTablet)62     public static SplitAnimationTimings getDeviceSplitToConfirmTimings(boolean isTablet) {
63         return isTablet
64                 ? SplitAnimationTimings.TABLET_SPLIT_TO_CONFIRM
65                 : SplitAnimationTimings.PHONE_SPLIT_TO_CONFIRM;
66     }
67 
68     /**
69      * Fetches device-specific timings for the app pair launch animation.
70      */
getDeviceAppPairLaunchTimings(boolean isTablet)71     public static SplitAnimationTimings getDeviceAppPairLaunchTimings(boolean isTablet) {
72         return isTablet
73                 ? SplitAnimationTimings.TABLET_APP_PAIR_LAUNCH
74                 : SplitAnimationTimings.PHONE_APP_PAIR_LAUNCH;
75     }
76 
77     /**
78      * Synchronizes the timing for the split dismiss animation to the current transition to
79      * NORMAL (launcher home/workspace)
80      */
goToNormalStateWithSplitDismissal(@onNull StateManager stateManager, @NonNull RecentsViewContainer container, @NonNull StatsLogManager.LauncherEvent exitReason, @NonNull SplitAnimationController animationController)81     public static void goToNormalStateWithSplitDismissal(@NonNull StateManager stateManager,
82             @NonNull RecentsViewContainer container,
83             @NonNull StatsLogManager.LauncherEvent exitReason,
84             @NonNull SplitAnimationController animationController) {
85         StateAnimationConfig config = new StateAnimationConfig();
86         BaseState startState = stateManager.getState();
87         long duration = startState.getTransitionDuration(container, false /*isToState*/);
88         if (duration == 0) {
89             // Case where we're in contextual on workspace (NORMAL), which by default has 0
90             // transition duration
91             duration = DURATION_DEFAULT_SPLIT_DISMISS;
92         }
93         config.duration = duration;
94         AnimatorSet stateAnim = stateManager.createAtomicAnimation(
95                 startState, NORMAL, config);
96         AnimatorSet dismissAnim = animationController
97                 .createPlaceholderDismissAnim(container, exitReason, duration);
98         stateAnim.play(dismissAnim);
99         stateManager.setCurrentAnimation(stateAnim, NORMAL);
100         stateAnim.start();
101     }
102 
103     /**
104      * Returns a IRemoteCallback which completes the provided list as a result or when the owner
105      * is destroyed
106      */
completeRunnableListCallback( RunnableList list, ActivityContext owner)107     public static IRemoteCallback completeRunnableListCallback(
108             RunnableList list, ActivityContext owner) {
109         DefaultLifecycleObserver destroyObserver = new DefaultLifecycleObserver() {
110             @Override
111             public void onDestroy(@NonNull LifecycleOwner owner) {
112                 list.executeAllAndClear();
113             }
114         };
115         MAIN_EXECUTOR.execute(() -> owner.getLifecycle().addObserver(destroyObserver));
116         list.add(() -> owner.getLifecycle().removeObserver(destroyObserver));
117 
118         return new IRemoteCallback.Stub() {
119             @Override
120             public void sendResult(Bundle bundle) {
121                 MAIN_EXECUTOR.execute(list::executeAllAndDestroy);
122             }
123 
124             @Override
125             public IBinder asBinder() {
126                 return BinderUtils.wrapLifecycle(this, owner.getOwnerCleanupSet());
127             }
128         };
129     }
130 
131     /**
132      * Returns a function that runs the given interpolator such that the entire progress is set
133      * between the given duration. That is, we set the interpolation to 0 until startDelay and reach
134      * 1 by (startDelay + duration).
135      */
136     public static Interpolator clampToDuration(Interpolator interpolator, float startDelay,
137             float duration, float totalDuration) {
138         return clampToProgress(interpolator, startDelay / totalDuration,
139                 (startDelay + duration) / totalDuration);
140     }
141 }
142