1 /*
2  * Copyright (C) 2014 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.systemui.recents.misc;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 
22 import java.util.ArrayList;
23 
24 /**
25  * A ref counted trigger that does some logic when the count is first incremented, or last
26  * decremented.  Not thread safe as it's not currently needed.
27  */
28 public class ReferenceCountedTrigger {
29 
30     int mCount;
31     ArrayList<Runnable> mFirstIncRunnables = new ArrayList<>();
32     ArrayList<Runnable> mLastDecRunnables = new ArrayList<>();
33     Runnable mErrorRunnable;
34 
35     // Convenience runnables
36     Runnable mIncrementRunnable = new Runnable() {
37         @Override
38         public void run() {
39             increment();
40         }
41     };
42     Runnable mDecrementRunnable = new Runnable() {
43         @Override
44         public void run() {
45             decrement();
46         }
47     };
48 
ReferenceCountedTrigger()49     public ReferenceCountedTrigger() {
50         this(null, null, null);
51     }
52 
ReferenceCountedTrigger(Runnable firstIncRunnable, Runnable lastDecRunnable, Runnable errorRunanable)53     public ReferenceCountedTrigger(Runnable firstIncRunnable, Runnable lastDecRunnable,
54             Runnable errorRunanable) {
55         if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
56         if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
57         mErrorRunnable = errorRunanable;
58     }
59 
60     /** Increments the ref count */
increment()61     public void increment() {
62         if (mCount == 0 && !mFirstIncRunnables.isEmpty()) {
63             int numRunnables = mFirstIncRunnables.size();
64             for (int i = 0; i < numRunnables; i++) {
65                 mFirstIncRunnables.get(i).run();
66             }
67         }
68         mCount++;
69     }
70 
71     /** Convenience method to increment this trigger as a runnable */
incrementAsRunnable()72     public Runnable incrementAsRunnable() {
73         return mIncrementRunnable;
74     }
75 
76     /** Adds a runnable to the last-decrement runnables list. */
addLastDecrementRunnable(Runnable r)77     public void addLastDecrementRunnable(Runnable r) {
78         mLastDecRunnables.add(r);
79     }
80 
81     /** Decrements the ref count */
decrement()82     public void decrement() {
83         mCount--;
84         if (mCount == 0) {
85             flushLastDecrementRunnables();
86         } else if (mCount < 0) {
87             if (mErrorRunnable != null) {
88                 mErrorRunnable.run();
89             } else {
90                 throw new RuntimeException("Invalid ref count");
91             }
92         }
93     }
94 
95     /**
96      * Runs and clears all the last-decrement runnables now.
97      */
flushLastDecrementRunnables()98     public void flushLastDecrementRunnables() {
99         if (!mLastDecRunnables.isEmpty()) {
100             int numRunnables = mLastDecRunnables.size();
101             for (int i = 0; i < numRunnables; i++) {
102                 mLastDecRunnables.get(i).run();
103             }
104         }
105         mLastDecRunnables.clear();
106     }
107 
108     /**
109      * Convenience method to decrement this trigger as a animator listener.  This listener is
110      * guarded to prevent being called back multiple times, and will trigger a decrement once and
111      * only once.
112      */
decrementOnAnimationEnd()113     public Animator.AnimatorListener decrementOnAnimationEnd() {
114         return new AnimatorListenerAdapter() {
115             private boolean hasEnded;
116 
117             @Override
118             public void onAnimationEnd(Animator animation) {
119                 if (hasEnded) return;
120                 decrement();
121                 hasEnded = true;
122             }
123         };
124     }
125 
126     /** Returns the current ref count */
getCount()127     public int getCount() {
128         return mCount;
129     }
130 }
131