1 /* 2 * Copyright (C) 2019 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 android.view.RemoteAnimationTarget.MODE_CLOSING; 19 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; 20 21 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 22 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_CANCEL_RECENTS_ANIMATION; 23 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_FINISH_RECENTS_ANIMATION; 24 import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_START_RECENTS_ANIMATION; 25 26 import android.graphics.Rect; 27 import android.os.Bundle; 28 import android.util.ArraySet; 29 import android.view.RemoteAnimationTarget; 30 31 import androidx.annotation.BinderThread; 32 import androidx.annotation.NonNull; 33 import androidx.annotation.UiThread; 34 35 import com.android.launcher3.Utilities; 36 import com.android.launcher3.util.Preconditions; 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.RecentsAnimationControllerCompat; 41 42 import java.io.PrintWriter; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.HashMap; 46 import java.util.Set; 47 48 /** 49 * Wrapper around {@link com.android.systemui.shared.system.RecentsAnimationListener} which 50 * delegates callbacks to multiple listeners on the main thread 51 */ 52 public class RecentsAnimationCallbacks implements 53 com.android.systemui.shared.system.RecentsAnimationListener { 54 55 private final Set<RecentsAnimationListener> mListeners = new ArraySet<>(); 56 private final SystemUiProxy mSystemUiProxy; 57 private final boolean mAllowMinimizeSplitScreen; 58 59 // TODO(141886704): Remove these references when they are no longer needed 60 private RecentsAnimationController mController; 61 62 private boolean mCancelled; 63 RecentsAnimationCallbacks(SystemUiProxy systemUiProxy, boolean allowMinimizeSplitScreen)64 public RecentsAnimationCallbacks(SystemUiProxy systemUiProxy, 65 boolean allowMinimizeSplitScreen) { 66 mSystemUiProxy = systemUiProxy; 67 mAllowMinimizeSplitScreen = allowMinimizeSplitScreen; 68 } 69 70 @UiThread addListener(RecentsAnimationListener listener)71 public void addListener(RecentsAnimationListener listener) { 72 Preconditions.assertUIThread(); 73 mListeners.add(listener); 74 } 75 76 @UiThread removeListener(RecentsAnimationListener listener)77 public void removeListener(RecentsAnimationListener listener) { 78 Preconditions.assertUIThread(); 79 mListeners.remove(listener); 80 } 81 82 @UiThread removeAllListeners()83 public void removeAllListeners() { 84 Preconditions.assertUIThread(); 85 mListeners.clear(); 86 } 87 notifyAnimationCanceled()88 public void notifyAnimationCanceled() { 89 mCancelled = true; 90 onAnimationCanceled(new HashMap<>()); 91 } 92 93 // Called only in Q platform 94 @BinderThread 95 @Deprecated onAnimationStart(RecentsAnimationControllerCompat controller, RemoteAnimationTarget[] appTargets, Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras)96 public final void onAnimationStart(RecentsAnimationControllerCompat controller, 97 RemoteAnimationTarget[] appTargets, Rect homeContentInsets, 98 Rect minimizedHomeBounds, Bundle extras) { 99 onAnimationStart(controller, appTargets, new RemoteAnimationTarget[0], 100 homeContentInsets, minimizedHomeBounds, extras); 101 } 102 103 // Called only in R+ platform 104 @BinderThread onAnimationStart(RecentsAnimationControllerCompat animationController, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras)105 public final void onAnimationStart(RecentsAnimationControllerCompat animationController, 106 RemoteAnimationTarget[] appTargets, 107 RemoteAnimationTarget[] wallpaperTargets, 108 Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) { 109 long appCount = Arrays.stream(appTargets) 110 .filter(app -> app.mode == MODE_CLOSING) 111 .count(); 112 if (appCount == 0) { 113 // Edge case, if there are no closing app targets, then Launcher has nothing to handle 114 ActiveGestureLog.INSTANCE.addLog( 115 /* event= */ "RecentsAnimationCallbacks.onAnimationStart (canceled)", 116 /* extras= */ 0, 117 /* gestureEvent= */ ON_START_RECENTS_ANIMATION); 118 notifyAnimationCanceled(); 119 animationController.finish(false /* toHome */, false /* sendUserLeaveHint */, 120 null /* finishCb */); 121 return; 122 } 123 124 mController = new RecentsAnimationController(animationController, 125 mAllowMinimizeSplitScreen, this::onAnimationFinished); 126 if (mCancelled) { 127 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), 128 mController::finishAnimationToApp); 129 } else { 130 RemoteAnimationTarget[] nonAppTargets; 131 if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) { 132 nonAppTargets = mSystemUiProxy.onGoingToRecentsLegacy(appTargets); 133 } else { 134 final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>(); 135 final ArrayList<RemoteAnimationTarget> nonApps = new ArrayList<>(); 136 classifyTargets(appTargets, apps, nonApps); 137 appTargets = apps.toArray(new RemoteAnimationTarget[apps.size()]); 138 nonAppTargets = nonApps.toArray(new RemoteAnimationTarget[nonApps.size()]); 139 } 140 if (nonAppTargets == null) { 141 nonAppTargets = new RemoteAnimationTarget[0]; 142 } 143 final RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets, 144 wallpaperTargets, nonAppTargets, homeContentInsets, minimizedHomeBounds, 145 extras); 146 147 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { 148 ActiveGestureLog.INSTANCE.addLog( 149 /* event= */ "RecentsAnimationCallbacks.onAnimationStart", 150 /* extras= */ targets.apps.length, 151 /* gestureEvent= */ ON_START_RECENTS_ANIMATION); 152 for (RecentsAnimationListener listener : getListeners()) { 153 listener.onRecentsAnimationStart(mController, targets); 154 } 155 }); 156 } 157 } 158 159 @BinderThread 160 @Override onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas)161 public final void onAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) { 162 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { 163 ActiveGestureLog.INSTANCE.addLog( 164 /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled", 165 /* gestureEvent= */ ON_CANCEL_RECENTS_ANIMATION); 166 for (RecentsAnimationListener listener : getListeners()) { 167 listener.onRecentsAnimationCanceled(thumbnailDatas); 168 } 169 }); 170 } 171 172 @BinderThread 173 @Override onTasksAppeared(RemoteAnimationTarget[] apps)174 public void onTasksAppeared(RemoteAnimationTarget[] apps) { 175 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { 176 ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared", 177 ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED); 178 for (RecentsAnimationListener listener : getListeners()) { 179 listener.onTasksAppeared(apps); 180 } 181 }); 182 } 183 184 @BinderThread 185 @Override onSwitchToScreenshot(Runnable onFinished)186 public boolean onSwitchToScreenshot(Runnable onFinished) { 187 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { 188 for (RecentsAnimationListener listener : getListeners()) { 189 if (listener.onSwitchToScreenshot(onFinished)) return; 190 } 191 onFinished.run(); 192 }); 193 return true; 194 } 195 onAnimationFinished(RecentsAnimationController controller)196 private final void onAnimationFinished(RecentsAnimationController controller) { 197 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { 198 ActiveGestureLog.INSTANCE.addLog( 199 /* event= */ "RecentsAnimationCallbacks.onAnimationFinished", 200 ON_FINISH_RECENTS_ANIMATION); 201 for (RecentsAnimationListener listener : getListeners()) { 202 listener.onRecentsAnimationFinished(controller); 203 } 204 }); 205 } 206 getListeners()207 private RecentsAnimationListener[] getListeners() { 208 return mListeners.toArray(new RecentsAnimationListener[mListeners.size()]); 209 } 210 classifyTargets(RemoteAnimationTarget[] appTargets, ArrayList<RemoteAnimationTarget> apps, ArrayList<RemoteAnimationTarget> nonApps)211 private void classifyTargets(RemoteAnimationTarget[] appTargets, 212 ArrayList<RemoteAnimationTarget> apps, ArrayList<RemoteAnimationTarget> nonApps) { 213 for (int i = 0; i < appTargets.length; i++) { 214 RemoteAnimationTarget target = appTargets[i]; 215 if (target.windowType == TYPE_DOCK_DIVIDER) { 216 nonApps.add(target); 217 } else { 218 apps.add(target); 219 } 220 } 221 } 222 dump(String prefix, PrintWriter pw)223 public void dump(String prefix, PrintWriter pw) { 224 pw.println(prefix + "RecentsAnimationCallbacks:"); 225 226 pw.println(prefix + "\tmAllowMinimizeSplitScreen=" + mAllowMinimizeSplitScreen); 227 pw.println(prefix + "\tmCancelled=" + mCancelled); 228 } 229 230 /** 231 * Listener for the recents animation callbacks. 232 */ 233 public interface RecentsAnimationListener { onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets)234 default void onRecentsAnimationStart(RecentsAnimationController controller, 235 RecentsAnimationTargets targets) {} 236 237 /** 238 * Callback from the system when the recents animation is canceled. {@param thumbnailData} 239 * is passed back for rendering screenshot to replace live tile. 240 */ onRecentsAnimationCanceled( @onNull HashMap<Integer, ThumbnailData> thumbnailDatas)241 default void onRecentsAnimationCanceled( 242 @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {} 243 244 /** 245 * Callback made whenever the recents animation is finished. 246 */ onRecentsAnimationFinished(@onNull RecentsAnimationController controller)247 default void onRecentsAnimationFinished(@NonNull RecentsAnimationController controller) {} 248 249 /** 250 * Callback made when a task started from the recents is ready for an app transition. 251 */ onTasksAppeared(@onNull RemoteAnimationTarget[] appearedTaskTarget)252 default void onTasksAppeared(@NonNull RemoteAnimationTarget[] appearedTaskTarget) {} 253 254 /** 255 * @return whether this will call onFinished or not (onFinished should only be called once). 256 */ onSwitchToScreenshot(Runnable onFinished)257 default boolean onSwitchToScreenshot(Runnable onFinished) { 258 return false; 259 } 260 } 261 } 262