Home | History | Annotate | Download | only in wm
      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      */
     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);
     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      */
     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() {
     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 
    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