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