• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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