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