/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera.util; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.List; /** * Enables thread-safe multiplexing of multiple input boolean states into a * single listener to be invoked upon change in the conjunction (logical AND) of * all inputs. */ public class ListenerCombiner> { /** * Callback for listening to changes to the conjunction of all inputs. */ public static interface StateChangeListener { /** * Called whenever the conjunction of all inputs changes. Listeners MUST * NOT call {@link #setInput} while still registered as a listener, as * this will result in infinite recursion. * * @param state the conjunction of all input values. */ public void onStateChange(boolean state); } /** Mutex for mValues and mState. */ private final Object mLock = new Object(); /** Stores the current input state. */ private final EnumMap mInputs; /** The current output state */ private boolean mOutput; /** * The set of listeners to notify when the output (the conjunction of all * inputs) changes. */ private final List mListeners = Collections.synchronizedList( new ArrayList()); public void addListener(StateChangeListener listener) { mListeners.add(listener); } public void removeListener(StateChangeListener listener) { mListeners.remove(listener); } public boolean getOutput() { synchronized (mLock) { return mOutput; } } /** * Updates the state of the given input, dispatching to all output change * listeners if the output changes. * * @param index the index of the input to change. * @param newValue the new value of the input. * @return The new output. */ public boolean setInput(Input input, boolean newValue) { synchronized (mLock) { mInputs.put(input, newValue); // If the new input value is the same as the existing output, // then nothing will change. if (newValue == mOutput) { return mOutput; } else { boolean oldOutput = mOutput; // Recompute the output by AND'ing all the inputs. mOutput = true; for (Boolean b : mInputs.values()) { mOutput &= b; } // If the output has changed, notify the listeners. if (oldOutput != mOutput) { notifyListeners(); } return mOutput; } } } public ListenerCombiner(Class clazz, StateChangeListener listener) { this(clazz); addListener(listener); } public ListenerCombiner(Class clazz) { mInputs = new EnumMap(clazz); for (Input i : clazz.getEnumConstants()) { mInputs.put(i, false); } mOutput = false; } /** * Notifies all listeners of the current state, regardless of whether or not * it has actually changed. */ public void notifyListeners() { synchronized (mLock) { for (StateChangeListener listener : mListeners) { listener.onStateChange(mOutput); } } } }