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.server.wm; 18 19 import android.util.ArrayMap; 20 import android.util.ArraySet; 21 22 import java.io.PrintWriter; 23 import java.util.ArrayList; 24 25 /** 26 * Keeps track of all {@link AppWindowToken} that are animating and makes sure all animations are 27 * finished at the same time such that we don't run into issues with z-ordering: An activity A 28 * that has a shorter animation that is above another activity B with a longer animation in the same 29 * task, the animation layer would put the B on top of A, but from the hierarchy, A needs to be on 30 * top of B. Thus, we defer reparenting A to the original hierarchy such that it stays on top of B 31 * until B finishes animating. 32 */ 33 class AnimatingAppWindowTokenRegistry { 34 35 private ArraySet<AppWindowToken> mAnimatingTokens = new ArraySet<>(); 36 private ArrayMap<AppWindowToken, Runnable> mFinishedTokens = new ArrayMap<>(); 37 38 private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>(); 39 40 private boolean mEndingDeferredFinish; 41 42 /** 43 * Notifies that an {@link AppWindowToken} has started animating. 44 */ notifyStarting(AppWindowToken token)45 void notifyStarting(AppWindowToken token) { 46 mAnimatingTokens.add(token); 47 } 48 49 /** 50 * Notifies that an {@link AppWindowToken} has finished animating. 51 */ notifyFinished(AppWindowToken token)52 void notifyFinished(AppWindowToken token) { 53 mAnimatingTokens.remove(token); 54 mFinishedTokens.remove(token); 55 56 // If we were the last token, make sure the end all deferred finishes. 57 if (mAnimatingTokens.isEmpty()) { 58 endDeferringFinished(); 59 } 60 } 61 62 /** 63 * Called when an {@link AppWindowToken} is about to finish animating. 64 * 65 * @param endDeferFinishCallback Callback to run when defer finish should be ended. 66 * @return {@code true} if finishing the animation should be deferred, {@code false} otherwise. 67 */ notifyAboutToFinish(AppWindowToken token, Runnable endDeferFinishCallback)68 boolean notifyAboutToFinish(AppWindowToken token, Runnable endDeferFinishCallback) { 69 final boolean removed = mAnimatingTokens.remove(token); 70 if (!removed) { 71 return false; 72 } 73 74 if (mAnimatingTokens.isEmpty()) { 75 76 // If no animations are animating anymore, finish all others. 77 endDeferringFinished(); 78 return false; 79 } else { 80 81 // Otherwise let's put it into the pending list of to be finished animations. 82 mFinishedTokens.put(token, endDeferFinishCallback); 83 return true; 84 } 85 } 86 endDeferringFinished()87 private void endDeferringFinished() { 88 89 // Don't start recursing. Running the finished listener invokes notifyFinished, which may 90 // invoked us again. 91 if (mEndingDeferredFinish) { 92 return; 93 } 94 try { 95 mEndingDeferredFinish = true; 96 97 // Copy it into a separate temp list to avoid modifying the collection while iterating 98 // as calling the callback may call back into notifyFinished. 99 for (int i = mFinishedTokens.size() - 1; i >= 0; i--) { 100 mTmpRunnableList.add(mFinishedTokens.valueAt(i)); 101 } 102 mFinishedTokens.clear(); 103 for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) { 104 mTmpRunnableList.get(i).run(); 105 } 106 mTmpRunnableList.clear(); 107 } finally { 108 mEndingDeferredFinish = false; 109 } 110 } 111 dump(PrintWriter pw, String header, String prefix)112 void dump(PrintWriter pw, String header, String prefix) { 113 if (!mAnimatingTokens.isEmpty() || !mFinishedTokens.isEmpty()) { 114 pw.print(prefix); pw.println(header); 115 prefix = prefix + " "; 116 pw.print(prefix); pw.print("mAnimatingTokens="); pw.println(mAnimatingTokens); 117 pw.print(prefix); pw.print("mFinishedTokens="); pw.println(mFinishedTokens); 118 } 119 } 120 } 121