• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.quickstep;
17 
18 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
19 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
20 import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
21 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION;
22 
23 import android.content.Context;
24 import android.os.RemoteException;
25 import android.util.Log;
26 import android.view.IRecentsAnimationController;
27 import android.view.RemoteAnimationTarget;
28 import android.view.SurfaceControl;
29 import android.view.WindowManagerGlobal;
30 import android.window.PictureInPictureSurfaceTransaction;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.UiThread;
34 
35 import com.android.launcher3.util.Preconditions;
36 import com.android.launcher3.util.RunnableList;
37 import com.android.quickstep.util.ActiveGestureErrorDetector;
38 import com.android.quickstep.util.ActiveGestureLog;
39 import com.android.systemui.shared.recents.model.ThumbnailData;
40 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
41 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
42 
43 import java.util.function.Consumer;
44 
45 /**
46  * Wrapper around RecentsAnimationControllerCompat to help with some synchronization
47  */
48 public class RecentsAnimationController {
49 
50     private static final String TAG = "RecentsAnimationController";
51     private final RecentsAnimationControllerCompat mController;
52     private final Consumer<RecentsAnimationController> mOnFinishedListener;
53     private final boolean mAllowMinimizeSplitScreen;
54 
55     private boolean mUseLauncherSysBarFlags = false;
56     private boolean mSplitScreenMinimized = false;
57     private boolean mFinishRequested = false;
58     // Only valid when mFinishRequested == true.
59     private boolean mFinishTargetIsLauncher;
60     private RunnableList mPendingFinishCallbacks = new RunnableList();
61 
RecentsAnimationController(RecentsAnimationControllerCompat controller, boolean allowMinimizeSplitScreen, Consumer<RecentsAnimationController> onFinishedListener)62     public RecentsAnimationController(RecentsAnimationControllerCompat controller,
63             boolean allowMinimizeSplitScreen,
64             Consumer<RecentsAnimationController> onFinishedListener) {
65         mController = controller;
66         mOnFinishedListener = onFinishedListener;
67         mAllowMinimizeSplitScreen = allowMinimizeSplitScreen;
68     }
69 
70     /**
71      * Synchronously takes a screenshot of the task with the given {@param taskId} if the task is
72      * currently being animated.
73      */
screenshotTask(int taskId)74     public ThumbnailData screenshotTask(int taskId) {
75         return mController.screenshotTask(taskId);
76     }
77 
78     /**
79      * Indicates that the gesture has crossed the window boundary threshold and system UI can be
80      * update the system bar flags accordingly.
81      */
setUseLauncherSystemBarFlags(boolean useLauncherSysBarFlags)82     public void setUseLauncherSystemBarFlags(boolean useLauncherSysBarFlags) {
83         if (mUseLauncherSysBarFlags != useLauncherSysBarFlags) {
84             mUseLauncherSysBarFlags = useLauncherSysBarFlags;
85             UI_HELPER_EXECUTOR.execute(() -> {
86                 if (!ENABLE_SHELL_TRANSITIONS) {
87                     mController.setAnimationTargetsBehindSystemBars(!useLauncherSysBarFlags);
88                 } else {
89                     try {
90                         WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars(
91                                 useLauncherSysBarFlags);
92                     } catch (RemoteException e) {
93                         Log.e(TAG, "Unable to reach window manager", e);
94                     }
95                 }
96             });
97         }
98     }
99 
100     /**
101      * Indicates that the gesture has crossed the window boundary threshold and we should minimize
102      * if we are in splitscreen.
103      */
setSplitScreenMinimized(Context context, boolean splitScreenMinimized)104     public void setSplitScreenMinimized(Context context, boolean splitScreenMinimized) {
105         if (!mAllowMinimizeSplitScreen) {
106             return;
107         }
108         if (mSplitScreenMinimized != splitScreenMinimized) {
109             mSplitScreenMinimized = splitScreenMinimized;
110         }
111     }
112 
113     /**
114      * Remove task remote animation target from
115      * {@link RecentsAnimationCallbacks#onTasksAppeared}}.
116      */
117     @UiThread
removeTaskTarget(@onNull RemoteAnimationTarget target)118     public void removeTaskTarget(@NonNull RemoteAnimationTarget target) {
119         UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
120     }
121 
122     @UiThread
finishAnimationToHome()123     public void finishAnimationToHome() {
124         finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
125     }
126 
127     @UiThread
finishAnimationToApp()128     public void finishAnimationToApp() {
129         finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
130     }
131 
132     /** See {@link #finish(boolean, Runnable, boolean)} */
133     @UiThread
finish(boolean toRecents, Runnable onFinishComplete)134     public void finish(boolean toRecents, Runnable onFinishComplete) {
135         finish(toRecents, onFinishComplete, false /* sendUserLeaveHint */);
136     }
137 
138     /**
139      * @param onFinishComplete A callback that runs on the main thread after the animation
140      *                         controller has finished on the background thread.
141      * @param sendUserLeaveHint Determines whether userLeaveHint flag will be set on the pausing
142      *                          activity. If userLeaveHint is true, the activity will enter into
143      *                          picture-in-picture mode upon being paused.
144      */
145     @UiThread
finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint)146     public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
147         Preconditions.assertUIThread();
148         finishController(toRecents, onFinishComplete, sendUserLeaveHint);
149     }
150 
151     @UiThread
finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint)152     public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
153         if (mFinishRequested) {
154             // If finishing, add to pending finish callbacks, otherwise, if finished, adding to the
155             // destroyed RunnableList will just trigger the callback to be called immediately
156             mPendingFinishCallbacks.add(callback);
157             return;
158         }
159         ActiveGestureLog.INSTANCE.addLog(
160                 /* event= */ "finishRecentsAnimation",
161                 /* extras= */ toRecents,
162                 /* gestureEvent= */ FINISH_RECENTS_ANIMATION);
163 
164         // Finish not yet requested
165         mFinishRequested = true;
166         mFinishTargetIsLauncher = toRecents;
167         mOnFinishedListener.accept(this);
168         mPendingFinishCallbacks.add(callback);
169         UI_HELPER_EXECUTOR.execute(() -> {
170             mController.finish(toRecents, sendUserLeaveHint);
171             InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
172             InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
173             InteractionJankMonitorWrapper.end(
174                     InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
175             MAIN_EXECUTOR.execute(mPendingFinishCallbacks::executeAllAndDestroy);
176         });
177     }
178 
179     /**
180      * @see IRecentsAnimationController#cleanupScreenshot()
181      */
182     @UiThread
cleanupScreenshot()183     public void cleanupScreenshot() {
184         UI_HELPER_EXECUTOR.execute(() -> {
185             ActiveGestureLog.INSTANCE.addLog(
186                     "cleanupScreenshot",
187                     ActiveGestureErrorDetector.GestureEvent.CLEANUP_SCREENSHOT);
188             mController.cleanupScreenshot();
189         });
190     }
191 
192     /**
193      * @see RecentsAnimationControllerCompat#detachNavigationBarFromApp
194      */
195     @UiThread
detachNavigationBarFromApp(boolean moveHomeToTop)196     public void detachNavigationBarFromApp(boolean moveHomeToTop) {
197         UI_HELPER_EXECUTOR.execute(() -> mController.detachNavigationBarFromApp(moveHomeToTop));
198     }
199 
200     /**
201      * @see IRecentsAnimationController#animateNavigationBarToApp(long)
202      */
203     @UiThread
animateNavigationBarToApp(long duration)204     public void animateNavigationBarToApp(long duration) {
205         UI_HELPER_EXECUTOR.execute(() -> mController.animateNavigationBarToApp(duration));
206     }
207 
208     /**
209      * @see IRecentsAnimationController#setWillFinishToHome(boolean)
210      */
211     @UiThread
setWillFinishToHome(boolean willFinishToHome)212     public void setWillFinishToHome(boolean willFinishToHome) {
213         UI_HELPER_EXECUTOR.execute(() -> mController.setWillFinishToHome(willFinishToHome));
214     }
215 
216     /**
217      * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
218      * that animating Activity to PiP has completed and the associated task surface should be
219      * updated accordingly. This should be called before `finish`
220      * @param taskId for which the leash should be updated
221      * @param finishTransaction the transaction to transfer to the task surface control after the
222      *                          leash is removed
223      * @param overlay the surface control for an overlay being shown above the pip (can be null)
224      */
setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay)225     public void setFinishTaskTransaction(int taskId,
226             PictureInPictureSurfaceTransaction finishTransaction,
227             SurfaceControl overlay) {
228         UI_HELPER_EXECUTOR.execute(
229                 () -> mController.setFinishTaskTransaction(taskId, finishTransaction, overlay));
230     }
231 
232     /**
233      * Enables the input consumer to start intercepting touches in the app window.
234      */
enableInputConsumer()235     public void enableInputConsumer() {
236         UI_HELPER_EXECUTOR.submit(() -> {
237             mController.setInputConsumerEnabled(true);
238         });
239     }
240 
241     /** @return wrapper controller. */
getController()242     public RecentsAnimationControllerCompat getController() {
243         return mController;
244     }
245 
246     /**
247      * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
248      * the animation was finished to launcher vs an app.
249      */
getFinishTargetIsLauncher()250     public boolean getFinishTargetIsLauncher() {
251         return mFinishTargetIsLauncher;
252     }
253 }
254