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