1 /*
2  * Copyright (C) 2018 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.systemui.shared.system;
18 
19 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
20 import static android.view.WindowManager.TRANSIT_CHANGE;
21 import static android.view.WindowManager.TRANSIT_CLOSE;
22 import static android.view.WindowManager.TRANSIT_OPEN;
23 import static android.view.WindowManager.TRANSIT_TO_BACK;
24 import static android.view.WindowManager.TRANSIT_TO_FRONT;
25 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
26 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
27 
28 import android.annotation.NonNull;
29 import android.annotation.SuppressLint;
30 import android.app.ActivityManager;
31 import android.app.WindowConfiguration;
32 import android.graphics.Point;
33 import android.graphics.Rect;
34 import android.util.ArrayMap;
35 import android.view.RemoteAnimationTarget;
36 import android.view.SurfaceControl;
37 import android.view.WindowManager;
38 import android.window.TransitionInfo;
39 
40 import java.util.ArrayList;
41 
42 /**
43  * @see RemoteAnimationTarget
44  */
45 public class RemoteAnimationTargetCompat {
46 
47     public static final int MODE_OPENING = RemoteAnimationTarget.MODE_OPENING;
48     public static final int MODE_CLOSING = RemoteAnimationTarget.MODE_CLOSING;
49     public static final int MODE_CHANGING = RemoteAnimationTarget.MODE_CHANGING;
50     public final int mode;
51 
52     public static final int ACTIVITY_TYPE_UNDEFINED = WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
53     public static final int ACTIVITY_TYPE_STANDARD = WindowConfiguration.ACTIVITY_TYPE_STANDARD;
54     public static final int ACTIVITY_TYPE_HOME = WindowConfiguration.ACTIVITY_TYPE_HOME;
55     public static final int ACTIVITY_TYPE_RECENTS = WindowConfiguration.ACTIVITY_TYPE_RECENTS;
56     public static final int ACTIVITY_TYPE_ASSISTANT = WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
57     public final int activityType;
58 
59     public final int taskId;
60     public final SurfaceControlCompat leash;
61     public final boolean isTranslucent;
62     public final Rect clipRect;
63     public final int prefixOrderIndex;
64     public final Point position;
65     public final Rect localBounds;
66     public final Rect sourceContainerBounds;
67     public final Rect screenSpaceBounds;
68     public final boolean isNotInRecents;
69     public final Rect contentInsets;
70     public final ActivityManager.RunningTaskInfo taskInfo;
71     public final boolean allowEnterPip;
72     public final int rotationChange;
73     public final int windowType;
74     public final WindowConfiguration windowConfiguration;
75 
76     private final SurfaceControl mStartLeash;
77 
78     // Fields used only to unrap into RemoteAnimationTarget
79     private final Rect startBounds;
80 
RemoteAnimationTargetCompat(RemoteAnimationTarget app)81     public RemoteAnimationTargetCompat(RemoteAnimationTarget app) {
82         taskId = app.taskId;
83         mode = app.mode;
84         leash = new SurfaceControlCompat(app.leash);
85         isTranslucent = app.isTranslucent;
86         clipRect = app.clipRect;
87         position = app.position;
88         localBounds = app.localBounds;
89         sourceContainerBounds = app.sourceContainerBounds;
90         screenSpaceBounds = app.screenSpaceBounds;
91         prefixOrderIndex = app.prefixOrderIndex;
92         isNotInRecents = app.isNotInRecents;
93         contentInsets = app.contentInsets;
94         activityType = app.windowConfiguration.getActivityType();
95         taskInfo = app.taskInfo;
96         allowEnterPip = app.allowEnterPip;
97         rotationChange = 0;
98 
99         mStartLeash = app.startLeash;
100         windowType = app.windowType;
101         windowConfiguration = app.windowConfiguration;
102         startBounds = app.startBounds;
103     }
104 
newModeToLegacyMode(int newMode)105     private static int newModeToLegacyMode(int newMode) {
106         switch (newMode) {
107             case WindowManager.TRANSIT_OPEN:
108             case WindowManager.TRANSIT_TO_FRONT:
109                 return MODE_OPENING;
110             case WindowManager.TRANSIT_CLOSE:
111             case WindowManager.TRANSIT_TO_BACK:
112                 return MODE_CLOSING;
113             default:
114                 return 2; // MODE_CHANGING
115         }
116     }
117 
unwrap()118     public RemoteAnimationTarget unwrap() {
119         return new RemoteAnimationTarget(
120                 taskId, mode, leash.getSurfaceControl(), isTranslucent, clipRect, contentInsets,
121                 prefixOrderIndex, position, localBounds, screenSpaceBounds, windowConfiguration,
122                 isNotInRecents, mStartLeash, startBounds, taskInfo, allowEnterPip, windowType
123         );
124     }
125 
126 
127     /**
128      * Almost a copy of Transitions#setupStartState.
129      * TODO: remove when there is proper cross-process transaction sync.
130      */
131     @SuppressLint("NewApi")
setupLeash(@onNull SurfaceControl leash, @NonNull TransitionInfo.Change change, int layer, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t)132     private static void setupLeash(@NonNull SurfaceControl leash,
133             @NonNull TransitionInfo.Change change, int layer,
134             @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
135         boolean isOpening = info.getType() == TRANSIT_OPEN || info.getType() == TRANSIT_TO_FRONT;
136         // Put animating stuff above this line and put static stuff below it.
137         int zSplitLine = info.getChanges().size();
138         // changes should be ordered top-to-bottom in z
139         final int mode = change.getMode();
140 
141         // Don't move anything that isn't independent within its parents
142         if (!TransitionInfo.isIndependent(change, info)) {
143             if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
144                 t.show(leash);
145                 t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
146             }
147             return;
148         }
149 
150         boolean hasParent = change.getParent() != null;
151 
152         if (!hasParent) {
153             t.reparent(leash, info.getRootLeash());
154             t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
155                     change.getStartAbsBounds().top - info.getRootOffset().y);
156         }
157         t.show(leash);
158         // Put all the OPEN/SHOW on top
159         if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
160             if (isOpening) {
161                 t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
162                 if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
163                     // if transferred, it should be left visible.
164                     t.setAlpha(leash, 0.f);
165                 }
166             } else {
167                 // put on bottom and leave it visible
168                 t.setLayer(leash, zSplitLine - layer);
169             }
170         } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
171             if (isOpening) {
172                 // put on bottom and leave visible
173                 t.setLayer(leash, zSplitLine - layer);
174             } else {
175                 // put on top
176                 t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
177             }
178         } else { // CHANGE
179             t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
180         }
181     }
182 
183     @SuppressLint("NewApi")
createLeash(TransitionInfo info, TransitionInfo.Change change, int order, SurfaceControl.Transaction t)184     private static SurfaceControl createLeash(TransitionInfo info, TransitionInfo.Change change,
185             int order, SurfaceControl.Transaction t) {
186         // TODO: once we can properly sync transactions across process, then get rid of this leash.
187         if (change.getParent() != null && (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
188             // Special case for wallpaper atm. Normally these are left alone; but, a quirk of
189             // making leashes means we have to handle them specially.
190             return change.getLeash();
191         }
192         SurfaceControl leashSurface = new SurfaceControl.Builder()
193                 .setName(change.getLeash().toString() + "_transition-leash")
194                 .setContainerLayer().setParent(change.getParent() == null ? info.getRootLeash()
195                         : info.getChange(change.getParent()).getLeash()).build();
196         // Copied Transitions setup code (which expects bottom-to-top order, so we swap here)
197         setupLeash(leashSurface, change, info.getChanges().size() - order, info, t);
198         t.reparent(change.getLeash(), leashSurface);
199         t.setAlpha(change.getLeash(), 1.0f);
200         t.show(change.getLeash());
201         t.setPosition(change.getLeash(), 0, 0);
202         t.setLayer(change.getLeash(), 0);
203         return leashSurface;
204     }
205 
RemoteAnimationTargetCompat(TransitionInfo.Change change, int order, TransitionInfo info, SurfaceControl.Transaction t)206     public RemoteAnimationTargetCompat(TransitionInfo.Change change, int order,
207             TransitionInfo info, SurfaceControl.Transaction t) {
208         taskId = change.getTaskInfo() != null ? change.getTaskInfo().taskId : -1;
209         mode = newModeToLegacyMode(change.getMode());
210 
211         // TODO: once we can properly sync transactions across process, then get rid of this leash.
212         leash = new SurfaceControlCompat(createLeash(info, change, order, t));
213 
214         isTranslucent = (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0
215                 || (change.getFlags() & TransitionInfo.FLAG_SHOW_WALLPAPER) != 0;
216         clipRect = null;
217         position = null;
218         localBounds = new Rect(change.getEndAbsBounds());
219         localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);
220         sourceContainerBounds = null;
221         screenSpaceBounds = new Rect(change.getEndAbsBounds());
222         prefixOrderIndex = order;
223         // TODO(shell-transitions): I guess we need to send content insets? evaluate how its used.
224         contentInsets = new Rect(0, 0, 0, 0);
225         if (change.getTaskInfo() != null) {
226             isNotInRecents = !change.getTaskInfo().isRunning;
227             activityType = change.getTaskInfo().getActivityType();
228         } else {
229             isNotInRecents = true;
230             activityType = ACTIVITY_TYPE_UNDEFINED;
231         }
232         taskInfo = change.getTaskInfo();
233         allowEnterPip = change.getAllowEnterPip();
234         mStartLeash = null;
235         rotationChange = change.getEndRotation() - change.getStartRotation();
236         windowType = INVALID_WINDOW_TYPE;
237 
238         windowConfiguration = change.getTaskInfo() != null
239             ? change.getTaskInfo().configuration.windowConfiguration
240             : new WindowConfiguration();
241         startBounds = change.getStartAbsBounds();
242     }
243 
wrap(RemoteAnimationTarget[] apps)244     public static RemoteAnimationTargetCompat[] wrap(RemoteAnimationTarget[] apps) {
245         final int length = apps != null ? apps.length : 0;
246         final RemoteAnimationTargetCompat[] appsCompat = new RemoteAnimationTargetCompat[length];
247         for (int i = 0; i < length; i++) {
248             appsCompat[i] = new RemoteAnimationTargetCompat(apps[i]);
249         }
250         return appsCompat;
251     }
252 
253     /**
254      * Represents a TransitionInfo object as an array of old-style targets
255      *
256      * @param wallpapers If true, this will return wallpaper targets; otherwise it returns
257      *                   non-wallpaper targets.
258      * @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should be
259      *                 populated by this function. If null, it is ignored.
260      */
wrap(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap)261     public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers,
262             SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
263         final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>();
264         for (int i = 0; i < info.getChanges().size(); i++) {
265             boolean changeIsWallpaper =
266                     (info.getChanges().get(i).getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0;
267             if (wallpapers != changeIsWallpaper) continue;
268             out.add(new RemoteAnimationTargetCompat(info.getChanges().get(i),
269                     info.getChanges().size() - i, info, t));
270             if (leashMap == null) continue;
271             leashMap.put(info.getChanges().get(i).getLeash(),
272                     out.get(out.size() - 1).leash.mSurfaceControl);
273         }
274         return out.toArray(new RemoteAnimationTargetCompat[out.size()]);
275     }
276 
277     /**
278      * @see SurfaceControl#release()
279      */
release()280     public void release() {
281         if (leash.mSurfaceControl != null) {
282             leash.mSurfaceControl.release();
283         }
284         if (mStartLeash != null) {
285             mStartLeash.release();
286         }
287     }
288 }
289