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