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