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 
21 import androidx.annotation.Nullable;
22 
23 import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
24 import com.android.quickstep.util.AnimatorControllerWithResistance;
25 import com.android.quickstep.util.LauncherSplitScreenListener;
26 import com.android.quickstep.util.TaskViewSimulator;
27 import com.android.quickstep.util.TransformParams;
28 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
29 
30 /**
31  * Glues together the necessary components to animate a remote target using a
32  * {@link TaskViewSimulator}
33  */
34 public class RemoteTargetGluer {
35     private RemoteTargetHandle[] mRemoteTargetHandles;
36     private StagedSplitBounds mStagedSplitBounds;
37 
38     /**
39      * Use this constructor if remote targets are split-screen independent
40      */
RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy, RemoteAnimationTargets targets)41     public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy,
42             RemoteAnimationTargets targets) {
43         mRemoteTargetHandles = createHandles(context, sizingStrategy, targets.apps.length);
44     }
45 
46     /**
47      * Use this constructor if you want the number of handles created to match the number of active
48      * running tasks
49      */
RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy)50     public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
51         int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
52                 .getRunningSplitTaskIds();
53         mRemoteTargetHandles = createHandles(context, sizingStrategy, splitIds.length == 2 ? 2 : 1);
54     }
55 
createHandles(Context context, BaseActivityInterface sizingStrategy, int numHandles)56     private RemoteTargetHandle[] createHandles(Context context,
57             BaseActivityInterface sizingStrategy, int numHandles) {
58         RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
59         for (int i = 0; i < numHandles; i++) {
60             TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy);
61             TransformParams transformParams = new TransformParams();
62             handles[i] = new RemoteTargetHandle(tvs, transformParams);
63         }
64         return handles;
65     }
66 
67     /**
68      * Pairs together {@link TaskViewSimulator}s and {@link TransformParams} into a
69      * {@link RemoteTargetHandle}
70      * Assigns only the apps associated with {@param targets} into their own TaskViewSimulators.
71      * Length of targets.apps should match that of {@link #mRemoteTargetHandles}.
72      *
73      * If split screen may be active when this is called, you might want to use
74      * {@link #assignTargetsForSplitScreen(RemoteAnimationTargets)}
75      */
assignTargets(RemoteAnimationTargets targets)76     public RemoteTargetHandle[] assignTargets(RemoteAnimationTargets targets) {
77         for (int i = 0; i < mRemoteTargetHandles.length; i++) {
78             RemoteAnimationTargetCompat primaryTaskTarget = targets.apps[i];
79             mRemoteTargetHandles[i].mTransformParams.setTargetSet(
80                     createRemoteAnimationTargetsForTarget(targets, null));
81             mRemoteTargetHandles[i].mTaskViewSimulator.setPreview(primaryTaskTarget, null);
82         }
83         return mRemoteTargetHandles;
84     }
85 
86     /**
87      * Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this matches the
88      * apps in targets.apps to that of the _active_ split screened tasks.
89      * See {@link #assignTargetsForSplitScreen(RemoteAnimationTargets, int[])}
90      */
assignTargetsForSplitScreen(RemoteAnimationTargets targets)91     public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
92         int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
93                 .getRunningSplitTaskIds();
94         return assignTargetsForSplitScreen(targets, splitIds);
95     }
96 
97     /**
98      * Assigns the provided splitIDs to the {@link #mRemoteTargetHandles}, with index 0 will being
99      * the left/top task, index 1 right/bottom
100      */
assignTargetsForSplitScreen(RemoteAnimationTargets targets, int[] splitIds)101     public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
102             int[] splitIds) {
103         RemoteAnimationTargetCompat topLeftTarget; // only one set if single/fullscreen task
104         RemoteAnimationTargetCompat bottomRightTarget;
105         if (mRemoteTargetHandles.length == 1) {
106             // If we're not in split screen, the splitIds count doesn't really matter since we
107             // should always hit this case.
108             mRemoteTargetHandles[0].mTransformParams.setTargetSet(targets);
109             if (targets.apps.length > 0) {
110                 // Unclear why/when target.apps length == 0, but it sure does happen :(
111                 topLeftTarget = targets.apps[0];
112                 mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget, null);
113             }
114         } else {
115             // split screen
116             topLeftTarget = targets.findTask(splitIds[0]);
117             bottomRightTarget = targets.findTask(splitIds[1]);
118 
119             // remoteTargetHandle[0] denotes topLeft task, so we pass in the bottomRight to exclude,
120             // vice versa
121             mStagedSplitBounds = new StagedSplitBounds(
122                     topLeftTarget.screenSpaceBounds,
123                     bottomRightTarget.screenSpaceBounds, splitIds[0], splitIds[1]);
124             mRemoteTargetHandles[0].mTransformParams.setTargetSet(
125                     createRemoteAnimationTargetsForTarget(targets, bottomRightTarget));
126             mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(topLeftTarget,
127                     mStagedSplitBounds);
128 
129             mRemoteTargetHandles[1].mTransformParams.setTargetSet(
130                     createRemoteAnimationTargetsForTarget(targets, topLeftTarget));
131             mRemoteTargetHandles[1].mTaskViewSimulator.setPreview(bottomRightTarget,
132                     mStagedSplitBounds);
133         }
134         return mRemoteTargetHandles;
135     }
136 
137     /**
138      * Ensures that we aren't excluding ancillary targets such as home/recents
139      *
140      * @param targetToExclude Will be excluded from the resulting return value.
141      *                        Pass in {@code null} to not exclude anything
142      * @return RemoteAnimationTargets where all the app targets from the passed in
143      *         {@param targets} are included except {@param targetToExclude}
144      */
createRemoteAnimationTargetsForTarget( RemoteAnimationTargets targets, @Nullable RemoteAnimationTargetCompat targetToExclude)145     private RemoteAnimationTargets createRemoteAnimationTargetsForTarget(
146             RemoteAnimationTargets targets,
147             @Nullable RemoteAnimationTargetCompat targetToExclude) {
148         int finalLength = targets.unfilteredApps.length - (targetToExclude == null ? 0 : 1);
149         RemoteAnimationTargetCompat[] targetsWithoutExcluded =
150                 new RemoteAnimationTargetCompat[finalLength];
151         int i = 0;
152         for (RemoteAnimationTargetCompat targetCompat : targets.unfilteredApps) {
153             if (targetCompat == targetToExclude) {
154                 continue;
155             }
156             targetsWithoutExcluded[i] = targetCompat;
157             i++;
158         }
159         return new RemoteAnimationTargets(targetsWithoutExcluded,
160                 targets.wallpapers, targets.nonApps, targets.targetMode);
161     }
162 
getRemoteTargetHandles()163     public RemoteTargetHandle[] getRemoteTargetHandles() {
164         return mRemoteTargetHandles;
165     }
166 
getStagedSplitBounds()167     public StagedSplitBounds getStagedSplitBounds() {
168         return mStagedSplitBounds;
169     }
170 
171     /**
172      * Container to keep together all the associated objects whose properties need to be updated to
173      * animate a single remote app target
174      */
175     public static class RemoteTargetHandle {
176         private final TaskViewSimulator mTaskViewSimulator;
177         private final TransformParams mTransformParams;
178         @Nullable
179         private AnimatorControllerWithResistance mPlaybackController;
180 
RemoteTargetHandle(TaskViewSimulator taskViewSimulator, TransformParams transformParams)181         public RemoteTargetHandle(TaskViewSimulator taskViewSimulator,
182                 TransformParams transformParams) {
183             mTransformParams = transformParams;
184             mTaskViewSimulator = taskViewSimulator;
185         }
186 
getTaskViewSimulator()187         public TaskViewSimulator getTaskViewSimulator() {
188             return mTaskViewSimulator;
189         }
190 
getTransformParams()191         public TransformParams getTransformParams() {
192             return mTransformParams;
193         }
194 
195         @Nullable
getPlaybackController()196         public AnimatorControllerWithResistance getPlaybackController() {
197             return mPlaybackController;
198         }
199 
setPlaybackController( @ullable AnimatorControllerWithResistance playbackController)200         public void setPlaybackController(
201                 @Nullable AnimatorControllerWithResistance playbackController) {
202             mPlaybackController = playbackController;
203         }
204     }
205 }
206