• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.quickstep;
17 
18 import android.util.Log;
19 import android.util.SparseArray;
20 
21 import com.android.launcher3.config.FeatureFlags;
22 
23 import java.util.StringJoiner;
24 import java.util.function.Consumer;
25 
26 /**
27  * Utility class to help manage multiple callbacks based on different states.
28  */
29 public class MultiStateCallback {
30 
31     private static final String TAG = "MultiStateCallback";
32     public static final boolean DEBUG_STATES = false;
33 
34     private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
35     private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
36 
37     private final String[] mStateNames;
38 
MultiStateCallback(String[] stateNames)39     public MultiStateCallback(String[] stateNames) {
40         mStateNames = DEBUG_STATES ? stateNames : null;
41     }
42 
43     private int mState = 0;
44 
45     /**
46      * Adds the provided state flags to the global state and executes any callbacks as a result.
47      */
setState(int stateFlag)48     public void setState(int stateFlag) {
49         if (DEBUG_STATES) {
50             Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
51                     + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
52         }
53 
54         int oldState = mState;
55         mState = mState | stateFlag;
56 
57         int count = mCallbacks.size();
58         for (int i = 0; i < count; i++) {
59             int state = mCallbacks.keyAt(i);
60 
61             if ((mState & state) == state) {
62                 Runnable callback = mCallbacks.valueAt(i);
63                 if (callback != null) {
64                     // Set the callback to null, so that it does not run again.
65                     mCallbacks.setValueAt(i, null);
66                     callback.run();
67                 }
68             }
69         }
70         notifyStateChangeHandlers(oldState);
71     }
72 
73     /**
74      * Adds the provided state flags to the global state and executes any change handlers
75      * as a result.
76      */
clearState(int stateFlag)77     public void clearState(int stateFlag) {
78         if (DEBUG_STATES) {
79             Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
80                     + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
81         }
82 
83         int oldState = mState;
84         mState = mState & ~stateFlag;
85         notifyStateChangeHandlers(oldState);
86     }
87 
notifyStateChangeHandlers(int oldState)88     private void notifyStateChangeHandlers(int oldState) {
89         int count = mStateChangeHandlers.size();
90         for (int i = 0; i < count; i++) {
91             int state = mStateChangeHandlers.keyAt(i);
92             boolean wasOn = (state & oldState) == state;
93             boolean isOn = (state & mState) == state;
94 
95             if (wasOn != isOn) {
96                 mStateChangeHandlers.valueAt(i).accept(isOn);
97             }
98         }
99     }
100 
101     /**
102      * Sets the callbacks to be run when the provided states are enabled.
103      * The callback is only run once.
104      */
addCallback(int stateMask, Runnable callback)105     public void addCallback(int stateMask, Runnable callback) {
106         if (FeatureFlags.IS_DOGFOOD_BUILD && mCallbacks.get(stateMask) != null) {
107             throw new IllegalStateException("Multiple callbacks on same state");
108         }
109         mCallbacks.put(stateMask, callback);
110     }
111 
112     /**
113      * Sets the handler to be called when the provided states are enabled or disabled.
114      */
addChangeHandler(int stateMask, Consumer<Boolean> handler)115     public void addChangeHandler(int stateMask, Consumer<Boolean> handler) {
116         mStateChangeHandlers.put(stateMask, handler);
117     }
118 
getState()119     public int getState() {
120         return mState;
121     }
122 
hasStates(int stateMask)123     public boolean hasStates(int stateMask) {
124         return (mState & stateMask) == stateMask;
125     }
126 
convertToFlagNames(int flags)127     private String convertToFlagNames(int flags) {
128         StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
129         for (int i = 0; i < mStateNames.length; i++) {
130             if ((flags & (1 << i)) != 0) {
131                 joiner.add(mStateNames[i]);
132             }
133         }
134         return joiner.toString();
135     }
136 
137 }
138