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