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(int targetTaskId)118 public void removeTaskTarget(int targetTaskId) { 119 UI_HELPER_EXECUTOR.execute(() -> mController.removeTask(targetTaskId)); 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 finishController(toRecents, callback, sendUserLeaveHint, false /* forceFinish */); 154 } 155 156 @UiThread finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint, boolean forceFinish)157 public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint, 158 boolean forceFinish) { 159 mPendingFinishCallbacks.add(callback); 160 if (!forceFinish && mFinishRequested) { 161 // If finish has already been requested, then add the callback to the pending list. 162 // If already finished, then adding it to the destroyed RunnableList will just 163 // trigger the callback to be called immediately 164 return; 165 } 166 ActiveGestureLog.INSTANCE.addLog( 167 /* event= */ "finishRecentsAnimation", 168 /* extras= */ toRecents, 169 /* gestureEvent= */ FINISH_RECENTS_ANIMATION); 170 // Finish not yet requested 171 mFinishRequested = true; 172 mFinishTargetIsLauncher = toRecents; 173 mOnFinishedListener.accept(this); 174 Runnable finishCb = () -> { 175 mController.finish(toRecents, sendUserLeaveHint); 176 InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); 177 InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME); 178 InteractionJankMonitorWrapper.end( 179 InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS); 180 MAIN_EXECUTOR.execute(mPendingFinishCallbacks::executeAllAndDestroy); 181 }; 182 if (forceFinish) { 183 finishCb.run(); 184 } else { 185 UI_HELPER_EXECUTOR.execute(finishCb); 186 } 187 } 188 189 /** 190 * @see IRecentsAnimationController#cleanupScreenshot() 191 */ 192 @UiThread cleanupScreenshot()193 public void cleanupScreenshot() { 194 UI_HELPER_EXECUTOR.execute(() -> { 195 ActiveGestureLog.INSTANCE.addLog( 196 "cleanupScreenshot", 197 ActiveGestureErrorDetector.GestureEvent.CLEANUP_SCREENSHOT); 198 mController.cleanupScreenshot(); 199 }); 200 } 201 202 /** 203 * @see RecentsAnimationControllerCompat#detachNavigationBarFromApp 204 */ 205 @UiThread detachNavigationBarFromApp(boolean moveHomeToTop)206 public void detachNavigationBarFromApp(boolean moveHomeToTop) { 207 UI_HELPER_EXECUTOR.execute(() -> mController.detachNavigationBarFromApp(moveHomeToTop)); 208 } 209 210 /** 211 * @see IRecentsAnimationController#animateNavigationBarToApp(long) 212 */ 213 @UiThread animateNavigationBarToApp(long duration)214 public void animateNavigationBarToApp(long duration) { 215 UI_HELPER_EXECUTOR.execute(() -> mController.animateNavigationBarToApp(duration)); 216 } 217 218 /** 219 * @see IRecentsAnimationController#setWillFinishToHome(boolean) 220 */ 221 @UiThread setWillFinishToHome(boolean willFinishToHome)222 public void setWillFinishToHome(boolean willFinishToHome) { 223 UI_HELPER_EXECUTOR.execute(() -> mController.setWillFinishToHome(willFinishToHome)); 224 } 225 226 /** 227 * Sets the final surface transaction on a Task. This is used by Launcher to notify the system 228 * that animating Activity to PiP has completed and the associated task surface should be 229 * updated accordingly. This should be called before `finish` 230 * @param taskId for which the leash should be updated 231 * @param finishTransaction the transaction to transfer to the task surface control after the 232 * leash is removed 233 * @param overlay the surface control for an overlay being shown above the pip (can be null) 234 */ setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay)235 public void setFinishTaskTransaction(int taskId, 236 PictureInPictureSurfaceTransaction finishTransaction, 237 SurfaceControl overlay) { 238 UI_HELPER_EXECUTOR.execute( 239 () -> mController.setFinishTaskTransaction(taskId, finishTransaction, overlay)); 240 } 241 242 /** 243 * Enables the input consumer to start intercepting touches in the app window. 244 */ enableInputConsumer()245 public void enableInputConsumer() { 246 UI_HELPER_EXECUTOR.submit(() -> { 247 mController.setInputConsumerEnabled(true); 248 }); 249 } 250 251 /** @return wrapper controller. */ getController()252 public RecentsAnimationControllerCompat getController() { 253 return mController; 254 } 255 256 /** 257 * RecentsAnimationListeners can check this in onRecentsAnimationFinished() to determine whether 258 * the animation was finished to launcher vs an app. 259 */ getFinishTargetIsLauncher()260 public boolean getFinishTargetIsLauncher() { 261 return mFinishTargetIsLauncher; 262 } 263 } 264