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 package com.android.quickstep; 17 18 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 20 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 21 22 import android.os.Bundle; 23 import android.view.RemoteAnimationTarget; 24 25 import java.io.PrintWriter; 26 import java.util.ArrayList; 27 import java.util.concurrent.CopyOnWriteArrayList; 28 29 /** 30 * Holds a collection of RemoteAnimationTargets, filtered by different properties. 31 */ 32 public class RemoteAnimationTargets { 33 34 private final CopyOnWriteArrayList<ReleaseCheck> mReleaseChecks = new CopyOnWriteArrayList<>(); 35 36 public final RemoteAnimationTarget[] unfilteredApps; 37 public final RemoteAnimationTarget[] apps; 38 public final RemoteAnimationTarget[] wallpapers; 39 public final RemoteAnimationTarget[] nonApps; 40 public final Bundle extras; 41 public final int targetMode; 42 public final boolean hasRecents; 43 44 private boolean mReleased = false; 45 RemoteAnimationTargets(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, int targetMode, Bundle extras)46 public RemoteAnimationTargets(RemoteAnimationTarget[] apps, 47 RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, 48 int targetMode, Bundle extras) { 49 ArrayList<RemoteAnimationTarget> filteredApps = new ArrayList<>(); 50 boolean hasRecents = false; 51 if (apps != null) { 52 for (RemoteAnimationTarget target : apps) { 53 if (target.mode == targetMode) { 54 filteredApps.add(target); 55 } 56 57 hasRecents |= target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_RECENTS; 58 } 59 } 60 61 this.unfilteredApps = apps; 62 this.apps = filteredApps.toArray(new RemoteAnimationTarget[filteredApps.size()]); 63 this.wallpapers = wallpapers; 64 this.targetMode = targetMode; 65 this.hasRecents = hasRecents; 66 this.nonApps = nonApps; 67 this.extras = extras; 68 } 69 RemoteAnimationTargets(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, int targetMode)70 public RemoteAnimationTargets(RemoteAnimationTarget[] apps, 71 RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, 72 int targetMode) { 73 this(apps, wallpapers, nonApps, targetMode, new Bundle()); 74 } 75 findTask(int taskId)76 public RemoteAnimationTarget findTask(int taskId) { 77 for (RemoteAnimationTarget target : apps) { 78 if (target.taskId == taskId) { 79 return target; 80 } 81 } 82 return null; 83 } 84 85 /** 86 * Gets the navigation bar remote animation target if exists. 87 */ getNavBarRemoteAnimationTarget()88 public RemoteAnimationTarget getNavBarRemoteAnimationTarget() { 89 return getNonAppTargetOfType(TYPE_NAVIGATION_BAR); 90 } 91 getNonAppTargetOfType(int type)92 public RemoteAnimationTarget getNonAppTargetOfType(int type) { 93 for (RemoteAnimationTarget target : nonApps) { 94 if (target.windowType == type) { 95 return target; 96 } 97 } 98 return null; 99 } 100 101 /** Returns the first opening app target. */ getFirstAppTarget()102 public RemoteAnimationTarget getFirstAppTarget() { 103 return apps.length > 0 ? apps[0] : null; 104 } 105 106 /** Returns the task id of the first opening app target, or -1 if none is found. */ getFirstAppTargetTaskId()107 public int getFirstAppTargetTaskId() { 108 RemoteAnimationTarget target = getFirstAppTarget(); 109 return target == null ? -1 : target.taskId; 110 } 111 isAnimatingHome()112 public boolean isAnimatingHome() { 113 for (RemoteAnimationTarget target : unfilteredApps) { 114 if (target.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) { 115 return true; 116 } 117 } 118 return false; 119 } 120 addReleaseCheck(ReleaseCheck check)121 public void addReleaseCheck(ReleaseCheck check) { 122 mReleaseChecks.add(check); 123 } 124 release()125 public void release() { 126 if (mReleased) { 127 return; 128 } 129 for (ReleaseCheck check : mReleaseChecks) { 130 if (!check.mCanRelease) { 131 check.addOnSafeToReleaseCallback(this::release); 132 return; 133 } 134 } 135 mReleaseChecks.clear(); 136 mReleased = true; 137 release(unfilteredApps); 138 release(wallpapers); 139 release(nonApps); 140 } 141 release(RemoteAnimationTarget[] targets)142 private static void release(RemoteAnimationTarget[] targets) { 143 for (RemoteAnimationTarget target : targets) { 144 if (target.leash != null) { 145 target.leash.release(); 146 } 147 if (target.startLeash != null) { 148 target.startLeash.release(); 149 } 150 } 151 } 152 dump(String prefix, PrintWriter pw)153 public void dump(String prefix, PrintWriter pw) { 154 pw.println(prefix + "RemoteAnimationTargets:"); 155 156 pw.println(prefix + "\ttargetMode=" + targetMode); 157 pw.println(prefix + "\thasRecents=" + hasRecents); 158 pw.println(prefix + "\tmReleased=" + mReleased); 159 } 160 161 /** 162 * Interface for intercepting surface release method 163 */ 164 public static class ReleaseCheck { 165 166 boolean mCanRelease = false; 167 private Runnable mAfterApplyCallback; 168 setCanRelease(boolean canRelease)169 protected void setCanRelease(boolean canRelease) { 170 mCanRelease = canRelease; 171 if (mCanRelease && mAfterApplyCallback != null) { 172 Runnable r = mAfterApplyCallback; 173 mAfterApplyCallback = null; 174 r.run(); 175 } 176 } 177 178 /** 179 * Adds a callback to notify when the surface can safely be released 180 */ addOnSafeToReleaseCallback(Runnable callback)181 void addOnSafeToReleaseCallback(Runnable callback) { 182 if (mCanRelease) { 183 callback.run(); 184 } else { 185 if (mAfterApplyCallback == null) { 186 mAfterApplyCallback = callback; 187 } else { 188 final Runnable oldCallback = mAfterApplyCallback; 189 mAfterApplyCallback = () -> { 190 callback.run(); 191 oldCallback.run(); 192 }; 193 } 194 } 195 } 196 } 197 } 198