• 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 
21 import android.os.Bundle;
22 import android.os.RemoteException;
23 import android.util.Log;
24 import android.view.RemoteAnimationTarget;
25 import android.view.SurfaceControl;
26 import android.view.WindowManagerGlobal;
27 import android.window.PictureInPictureSurfaceTransaction;
28 import android.window.WindowAnimationState;
29 
30 import androidx.annotation.UiThread;
31 
32 import com.android.internal.jank.Cuj;
33 import com.android.internal.os.IResultReceiver;
34 import com.android.launcher3.util.Preconditions;
35 import com.android.launcher3.util.RunnableList;
36 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
37 import com.android.systemui.animation.TransitionAnimator;
38 import com.android.systemui.shared.recents.model.ThumbnailData;
39 import com.android.systemui.shared.system.ActivityManagerWrapper;
40 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
41 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
42 import com.android.wm.shell.recents.IRecentsAnimationController;
43 
44 import java.io.PrintWriter;
45 import java.util.function.Consumer;
46 
47 /**
48  * Wrapper around RecentsAnimationControllerCompat to help with some synchronization
49  */
50 public class RecentsAnimationController {
51 
52     private static final String TAG = "RecentsAnimationController";
53     private final RecentsAnimationControllerCompat mController;
54     private final Consumer<RecentsAnimationController> mOnFinishedListener;
55 
56     private boolean mUseLauncherSysBarFlags = false;
57     private boolean mFinishRequested = false;
58     // Only valid when mFinishRequested == true.
59     private boolean mFinishTargetIsLauncher;
60     // Only valid when mFinishRequested == true
61     private boolean mLauncherIsVisibleAtFinish;
62     private RunnableList mPendingFinishCallbacks = new RunnableList();
63 
RecentsAnimationController(RecentsAnimationControllerCompat controller, Consumer<RecentsAnimationController> onFinishedListener)64     public RecentsAnimationController(RecentsAnimationControllerCompat controller,
65             Consumer<RecentsAnimationController> onFinishedListener) {
66         mController = controller;
67         mOnFinishedListener = onFinishedListener;
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 ActivityManagerWrapper.getInstance().takeTaskThumbnail(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                 try {
87                     WindowManagerGlobal.getWindowManagerService().setRecentsAppBehindSystemBars(
88                             useLauncherSysBarFlags);
89                 } catch (RemoteException e) {
90                     Log.e(TAG, "Unable to reach window manager", e);
91                 }
92             });
93         }
94     }
95 
96     @UiThread
handOffAnimation(RemoteAnimationTarget[] targets, WindowAnimationState[] states)97     public void handOffAnimation(RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
98         if (TransitionAnimator.Companion.longLivedReturnAnimationsEnabled()) {
99             UI_HELPER_EXECUTOR.execute(() -> mController.handOffAnimation(targets, states));
100         } else {
101             Log.e(TAG, "Tried to hand off the animation, but the feature is disabled",
102                     new Exception());
103         }
104     }
105 
106     @UiThread
finishAnimationToHome()107     public void finishAnimationToHome() {
108         finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
109     }
110 
111     @UiThread
finishAnimationToApp()112     public void finishAnimationToApp() {
113         finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
114     }
115 
116     /** See {@link #finish(boolean, Runnable, boolean)} */
117     @UiThread
finish(boolean toRecents, Runnable onFinishComplete)118     public void finish(boolean toRecents, Runnable onFinishComplete) {
119         finish(toRecents, onFinishComplete, false /* sendUserLeaveHint */);
120     }
121 
122     /**
123      * @param onFinishComplete A callback that runs on the main thread after the animation
124      *                         controller has finished on the background thread.
125      * @param sendUserLeaveHint Determines whether userLeaveHint flag will be set on the pausing
126      *                          activity. If userLeaveHint is true, the activity will enter into
127      *                          picture-in-picture mode upon being paused.
128      */
129     @UiThread
finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint)130     public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
131         Preconditions.assertUIThread();
132         finishController(toRecents, onFinishComplete, sendUserLeaveHint);
133     }
134 
135     @UiThread
finish(boolean toRecents, boolean launcherIsVisibleAtFinish, Runnable onFinishComplete, boolean sendUserLeaveHint)136     public void finish(boolean toRecents, boolean launcherIsVisibleAtFinish,
137             Runnable onFinishComplete, boolean sendUserLeaveHint) {
138         Preconditions.assertUIThread();
139         finishController(toRecents, launcherIsVisibleAtFinish, onFinishComplete, sendUserLeaveHint,
140                 false);
141     }
142 
143     @UiThread
finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint)144     public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
145         finishController(toRecents, false, callback, sendUserLeaveHint, false /* forceFinish */);
146     }
147 
148     @UiThread
finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint, boolean forceFinish)149     public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint,
150             boolean forceFinish) {
151         finishController(toRecents, toRecents, callback, sendUserLeaveHint, forceFinish);
152     }
153 
154     @UiThread
finishController(boolean toRecents, boolean launcherIsVisibleAtFinish, Runnable callback, boolean sendUserLeaveHint, boolean forceFinish)155     public void finishController(boolean toRecents, boolean launcherIsVisibleAtFinish,
156             Runnable callback, boolean sendUserLeaveHint, boolean forceFinish) {
157         mPendingFinishCallbacks.add(callback);
158         if (!forceFinish && mFinishRequested) {
159             // If finish has already been requested, then add the callback to the pending list.
160             // If already finished, then adding it to the destroyed RunnableList will just
161             // trigger the callback to be called immediately
162             return;
163         }
164         ActiveGestureProtoLogProxy.logFinishRecentsAnimation(toRecents);
165         // Finish not yet requested
166         mFinishRequested = true;
167         mFinishTargetIsLauncher = toRecents;
168         mLauncherIsVisibleAtFinish = launcherIsVisibleAtFinish;
169         mOnFinishedListener.accept(this);
170         Runnable finishCb = () -> {
171             mController.finish(toRecents, sendUserLeaveHint, new IResultReceiver.Stub() {
172                 @Override
173                 public void send(int i, Bundle bundle) throws RemoteException {
174                     ActiveGestureProtoLogProxy.logFinishRecentsAnimationCallback();
175                     MAIN_EXECUTOR.execute(() -> {
176                         mPendingFinishCallbacks.executeAllAndDestroy();
177                     });
178                 }
179             });
180             InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
181             InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
182             InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
183         };
184         if (forceFinish) {
185             finishCb.run();
186         } else {
187             UI_HELPER_EXECUTOR.execute(finishCb);
188         }
189     }
190 
191     /**
192      * @see RecentsAnimationControllerCompat#detachNavigationBarFromApp
193      */
194     @UiThread
detachNavigationBarFromApp(boolean moveHomeToTop)195     public void detachNavigationBarFromApp(boolean moveHomeToTop) {
196         UI_HELPER_EXECUTOR.execute(() -> mController.detachNavigationBarFromApp(moveHomeToTop));
197     }
198 
199     /**
200      * @see IRecentsAnimationController#setWillFinishToHome(boolean)
201      */
202     @UiThread
setWillFinishToHome(boolean willFinishToHome)203     public void setWillFinishToHome(boolean willFinishToHome) {
204         UI_HELPER_EXECUTOR.execute(() -> mController.setWillFinishToHome(willFinishToHome));
205     }
206 
207     /**
208      * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
209      * that animating Activity to PiP has completed and the associated task surface should be
210      * updated accordingly. This should be called before `finish`
211      * @param taskId for which the leash should be updated
212      * @param finishTransaction the transaction to transfer to the task surface control after the
213      *                          leash is removed
214      * @param overlay the surface control for an overlay being shown above the pip (can be null)
215      */
setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay)216     public void setFinishTaskTransaction(int taskId,
217             PictureInPictureSurfaceTransaction finishTransaction,
218             SurfaceControl overlay) {
219         UI_HELPER_EXECUTOR.execute(
220                 () -> mController.setFinishTaskTransaction(taskId, finishTransaction, overlay));
221     }
222 
223     /**
224      * Enables the input consumer to start intercepting touches in the app window.
225      */
enableInputConsumer()226     public void enableInputConsumer() {
227         UI_HELPER_EXECUTOR.submit(() -> {
228             mController.setInputConsumerEnabled(true);
229         });
230     }
231 
232     /** @return wrapper controller. */
getController()233     public RecentsAnimationControllerCompat getController() {
234         return mController;
235     }
236 
237     /**
238      * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
239      * the animation was finished to launcher vs an app.
240      */
getFinishTargetIsLauncher()241     public boolean getFinishTargetIsLauncher() {
242         return mFinishTargetIsLauncher;
243     }
244 
245     /**
246      * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether
247      * the animation was finished to launcher vs an app.
248      */
getLauncherIsVisibleAtFinish()249     public boolean getLauncherIsVisibleAtFinish() {
250         return mLauncherIsVisibleAtFinish;
251     }
252 
dump(String prefix, PrintWriter pw)253     public void dump(String prefix, PrintWriter pw) {
254         pw.println(prefix + "RecentsAnimationController:");
255 
256         pw.println(prefix + "\tmUseLauncherSysBarFlags=" + mUseLauncherSysBarFlags);
257         pw.println(prefix + "\tmFinishRequested=" + mFinishRequested);
258         pw.println(prefix + "\tmFinishTargetIsLauncher=" + mFinishTargetIsLauncher);
259     }
260 }
261