• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.quickstep;
18 
19 import android.content.Context;
20 import android.graphics.Rect;
21 import android.view.RemoteAnimationTarget;
22 
23 import androidx.annotation.Nullable;
24 
25 import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
26 import com.android.quickstep.util.AnimatorControllerWithResistance;
27 import com.android.quickstep.util.TaskViewSimulator;
28 import com.android.quickstep.util.TransformParams;
29 import com.android.quickstep.views.DesktopTaskView;
30 
31 import java.util.ArrayList;
32 
33 /**
34  * Glues together the necessary components to animate a remote target using a
35  * {@link TaskViewSimulator}
36  */
37 public class RemoteTargetGluer {
38     private RemoteTargetHandle[] mRemoteTargetHandles;
39     private SplitBounds mSplitBounds;
40 
41     /**
42      * Use this constructor if remote targets are split-screen independent
43      */
RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy, RemoteAnimationTargets targets, boolean forDesktop)44     public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy,
45             RemoteAnimationTargets targets, boolean forDesktop) {
46         init(context, sizingStrategy, targets.apps.length, forDesktop);
47     }
48 
49     /**
50      * Use this constructor if you want the number of handles created to match the number of active
51      * running tasks
52      */
RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy)53     public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
54         if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
55             // TODO: binder call, only for prototyping. Creating the gluer should be postponed so
56             //  we can create it when we have the remote animation targets ready.
57             int desktopTasks = SystemUiProxy.INSTANCE.get(context).getVisibleDesktopTaskCount();
58             if (desktopTasks > 0) {
59                 init(context, sizingStrategy, desktopTasks, true /* forDesktop */);
60                 return;
61             }
62         }
63 
64         int[] splitIds = TopTaskTracker.INSTANCE.get(context).getRunningSplitTaskIds();
65         init(context, sizingStrategy, splitIds.length == 2 ? 2 : 1, false /* forDesktop */);
66     }
67 
init(Context context, BaseActivityInterface sizingStrategy, int numHandles, boolean forDesktop)68     private void init(Context context, BaseActivityInterface sizingStrategy, int numHandles,
69             boolean forDesktop) {
70         mRemoteTargetHandles = createHandles(context, sizingStrategy, numHandles, forDesktop);
71     }
72 
createHandles(Context context, BaseActivityInterface sizingStrategy, int numHandles, boolean forDesktop)73     private RemoteTargetHandle[] createHandles(Context context,
74             BaseActivityInterface sizingStrategy, int numHandles, boolean forDesktop) {
75         RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
76         for (int i = 0; i < numHandles; i++) {
77             TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy);
78             tvs.setIsDesktopTask(forDesktop);
79             TransformParams transformParams = new TransformParams();
80             handles[i] = new RemoteTargetHandle(tvs, transformParams);
81         }
82         return handles;
83     }
84 
85     /**
86      * Pairs together {@link TaskViewSimulator}s and {@link TransformParams} into a
87      * {@link RemoteTargetHandle}
88      * Assigns only the apps associated with {@param targets} into their own TaskViewSimulators.
89      * Length of targets.apps should match that of {@link #mRemoteTargetHandles}.
90      *
91      * If split screen may be active when this is called, you might want to use
92      * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)}
93      */
assignTargets(RemoteAnimationTargets targets)94     public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
95         for (int i = 0; i < mRemoteTargetHandles.length; i++) {
96             RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
97             mRemoteTargetHandles[i].mTransformParams.setTargetSet(
98                     createRemoteAnimationTargetsForTarget(targets, null));
99             mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
100         }
101         return mRemoteTargetHandles;
102     }
103 
104     /**
105      * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this assigns the
106      * apps in {@code targets.apps} to the {@link #mRemoteTargetHandles} with index 0 will being
107      * the left/top task, index 1 right/bottom.
108      */
assignTargetsForSplitScreen(RemoteAnimationTargets targets)109     public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
110         if (mRemoteTargetHandles.length == 1) {
111             // If we're not in split screen, the splitIds count doesn't really matter since we
112             // should always hit this case.
113             mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
114             if (targets.apps.length > 0) {
115                 // Unclear why/when target.apps length == 0, but it sure does happen :(
116                 mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(targets.apps[0], null);
117             }
118         } else {
119             RemoteAnimationTarget topLeftTarget = targets.apps[0];
120 
121             // Fetch the adjacent target for split screen.
122             RemoteAnimationTarget bottomRightTarget = null;
123             for (int i = 1; i < targets.apps.length; i++) {
124                 final RemoteAnimationTarget target = targets.apps[i];
125                 Rect topLeftBounds = getStartBounds(topLeftTarget);
126                 Rect bounds = getStartBounds(target);
127                 if (topLeftBounds.left > bounds.right || topLeftBounds.top > bounds.bottom) {
128                     bottomRightTarget = topLeftTarget;
129                     topLeftTarget = target;
130                     break;
131                 } else if (topLeftBounds.right < bounds.left || topLeftBounds.bottom < bounds.top) {
132                     bottomRightTarget = target;
133                     break;
134                 }
135             }
136 
137             // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
138             // vice versa
139             mSplitBounds = new SplitBounds(
140                     getStartBounds(topLeftTarget),
141                     getStartBounds(bottomRightTarget),
142                     topLeftTarget.taskId,
143                     bottomRightTarget.taskId);
144             mRemoteTargetHandles[0].mTransformParams.setTargetSet(
145                     createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
146             mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
147                     mSplitBounds);
148 
149             mRemoteTargetHandles[1].mTransformParams.setTargetSet(
150                     createRemoteAnimationTargetsForTarget(targets, topLeftTarget));
151             mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget,
152                     mSplitBounds);
153         }
154         return mRemoteTargetHandles;
155     }
156 
157     /**
158      * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this creates distinct
159      * transform params per app in {@code targets.apps} list.
160      */
assignTargetsForDesktop(RemoteAnimationTargets targets)161     public RemoteTargetHandle[] assignTargetsForDesktop(RemoteAnimationTargets targets) {
162         for (int i = 0; i < mRemoteTargetHandles.length; i++) {
163             RemoteAnimationTarget primaryTaskTarget = targets.apps[i];
164             mRemoteTargetHandles[i].mTransformParams.setTargetSet(
165                     createRemoteAnimationTargetsForTaskId(targets, primaryTaskTarget.taskId));
166             mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
167         }
168         return mRemoteTargetHandles;
169     }
170 
getStartBounds(RemoteAnimationTarget target)171     private Rect getStartBounds(RemoteAnimationTarget target) {
172         return target.startBounds == null ? target.screenSpaceBounds : target.startBounds;
173     }
174 
175     /**
176      * Ensures that we aren't excluding ancillary targets such as home/recents
177      *
178      * @param targetToExclude Will be excluded from the resulting return value.
179      *                        Pass in {@code null} to not exclude anything
180      * @return RemoteAnimationTargets where all the app targets from the passed in
181      *         {@param targets} are included except {@param targetToExclude}
182      */
createRemoteAnimationTargetsForTarget( RemoteAnimationTargets targets, RemoteAnimationTarget targetToExclude)183     private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
184             RemoteAnimationTargets targets,
185             RemoteAnimationTarget targetToExclude) {
186         ArrayList<RemoteAnimationTarget> targetsWithoutExcluded = new ArrayList<>();
187 
188         for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
189             if (targetCompat == targetToExclude) {
190                 continue;
191             }
192             if (targetToExclude != null
193                     && targetToExclude.taskInfo != null
194                     && targetCompat.taskInfo != null
195                     && targetToExclude.taskInfo.parentTaskId == targetCompat.taskInfo.taskId) {
196                 // Also exclude corresponding parent task
197                 continue;
198             }
199 
200             targetsWithoutExcluded.add(targetCompat);
201         }
202         final RemoteAnimationTarget[] filteredApps = targetsWithoutExcluded.toArray(
203                 new RemoteAnimationTarget[targetsWithoutExcluded.size()]);
204         return new RemoteAnimationTargets(
205                 filteredApps, targets.wallpapers, targets.nonApps, targets.targetMode);
206     }
207 
208     /**
209      * Ensures that we only animate one specific app target. Includes ancillary targets such as
210      * home/recents
211      *
212      * @param targets remote animation targets to filter
213      * @param taskId  id for a task that we want this remote animation to apply to
214      * @return {@link RemoteAnimationTargets} where app target only includes the app that has the
215      * {@code taskId} that was passed in
216      */
createRemoteAnimationTargetsForTaskId( RemoteAnimationTargets targets, int taskId)217     private RemoteAnimationTargets createRemoteAnimationTargetsForTaskId(
218             RemoteAnimationTargets targets, int taskId) {
219         RemoteAnimationTarget[] targetApp = null;
220         for (RemoteAnimationTarget targetCompat : targets.unfilteredApps) {
221             if (targetCompat.taskId == taskId) {
222                 targetApp = new RemoteAnimationTarget[]{targetCompat};
223                 break;
224             }
225         }
226 
227         if (targetApp == null) {
228             targetApp = new RemoteAnimationTarget[0];
229         }
230 
231         return new RemoteAnimationTargets(targetApp, targets.wallpapers, targets.nonApps,
232                 targets.targetMode);
233     }
234 
getRemoteTargetHandles()235     public RemoteTargetHandle[] getRemoteTargetHandles() {
236         return mRemoteTargetHandles;
237     }
238 
getSplitBounds()239     public SplitBounds getSplitBounds() {
240         return mSplitBounds;
241     }
242 
243     /**
244      * Container to keep together all the associated objects whose properties need to be updated to
245      * animate a single remote app target
246      */
247     public static class RemoteTargetHandle {
248         private final TaskViewSimulator mTaskViewSimulator;
249         private final TransformParams mTransformParams;
250         @Nullable
251         private AnimatorControllerWithResistance mPlaybackController;
252 
RemoteTargetHandle(TaskViewSimulator taskViewSimulator, TransformParams transformParams)253         public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
254                 TransformParams transformParams) {
255             mTransformParams = transformParams;
256             mTaskViewSimulator = taskViewSimulator;
257         }
258 
getTaskViewSimulator()259         public TaskViewSimulator getTaskViewSimulator() {
260             return mTaskViewSimulator;
261         }
262 
getTransformParams()263         public TransformParams getTransformParams() {
264             return mTransformParams;
265         }
266 
267         @Nullable
getPlaybackController()268         public AnimatorControllerWithResistance getPlaybackController() {
269             return mPlaybackController;
270         }
271 
setPlaybackController( @ullable AnimatorControllerWithResistance playbackController)272         public void setPlaybackController(
273                 @Nullable AnimatorControllerWithResistance playbackController) {
274             mPlaybackController = playbackController;
275         }
276     }
277 }
278