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