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