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
17package com.android.systemui.recents.misc;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21
22import 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 */
28public 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
49    public ReferenceCountedTrigger() {
50        this(null, null, null);
51    }
52
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 */
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 */
72    public Runnable incrementAsRunnable() {
73        return mIncrementRunnable;
74    }
75
76    /** Adds a runnable to the last-decrement runnables list. */
77    public void addLastDecrementRunnable(Runnable r) {
78        mLastDecRunnables.add(r);
79    }
80
81    /** Decrements the ref count */
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     */
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     */
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 */
127    public int getCount() {
128        return mCount;
129    }
130}
131