Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 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 | |
Jorim Jaggi | b8a9cbe | 2018-03-27 18:02:18 +0200 | [diff] [blame] | 22 | import java.io.PrintWriter; |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 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 | |
Jorim Jaggi | 68653ab | 2018-04-05 23:29:10 +0200 | [diff] [blame] | 40 | private boolean mEndingDeferredFinish; |
| 41 | |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 42 | /** |
| 43 | * Notifies that an {@link AppWindowToken} has started animating. |
| 44 | */ |
| 45 | void notifyStarting(AppWindowToken token) { |
| 46 | mAnimatingTokens.add(token); |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Notifies that an {@link AppWindowToken} has finished animating. |
| 51 | */ |
| 52 | void notifyFinished(AppWindowToken token) { |
| 53 | mAnimatingTokens.remove(token); |
| 54 | mFinishedTokens.remove(token); |
Jorim Jaggi | 68653ab | 2018-04-05 23:29:10 +0200 | [diff] [blame] | 55 | |
| 56 | // If we were the last token, make sure the end all deferred finishes. |
| 57 | if (mAnimatingTokens.isEmpty()) { |
| 58 | endDeferringFinished(); |
| 59 | } |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 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 | */ |
| 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 | |
| 87 | private void endDeferringFinished() { |
Jorim Jaggi | 68653ab | 2018-04-05 23:29:10 +0200 | [diff] [blame] | 88 | |
| 89 | // Don't start recursing. Running the finished listener invokes notifyFinished, which may |
| 90 | // invoked us again. |
| 91 | if (mEndingDeferredFinish) { |
| 92 | return; |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 93 | } |
Jorim Jaggi | 68653ab | 2018-04-05 23:29:10 +0200 | [diff] [blame] | 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; |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 109 | } |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 110 | } |
Jorim Jaggi | b8a9cbe | 2018-03-27 18:02:18 +0200 | [diff] [blame] | 111 | |
| 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 | } |
Jorim Jaggi | 6de6101 | 2018-03-19 14:53:23 +0100 | [diff] [blame] | 120 | } |