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.camera.util; 18 19import java.util.ArrayList; 20import java.util.Collections; 21import java.util.EnumMap; 22import java.util.List; 23 24/** 25 * Enables thread-safe multiplexing of multiple input boolean states into a 26 * single listener to be invoked upon change in the conjunction (logical AND) of 27 * all inputs. 28 */ 29public class ConjunctionListenerMux<Input extends Enum<Input>> { 30 /** 31 * Callback for listening to changes to the conjunction of all inputs. 32 */ 33 public static interface OutputChangeListener { 34 /** 35 * Called whenever the conjunction of all inputs changes. Listeners MUST 36 * NOT call {@link #setInput} while still registered as a listener, as 37 * this will result in infinite recursion. 38 * 39 * @param state the conjunction of all input values. 40 */ 41 public void onOutputChange(boolean state); 42 } 43 44 /** Mutex for mValues and mState. */ 45 private final Object mLock = new Object(); 46 /** Stores the current input state. */ 47 private final EnumMap<Input, Boolean> mInputs; 48 /** The current output state */ 49 private boolean mOutput; 50 /** 51 * The set of listeners to notify when the output (the conjunction of all 52 * inputs) changes. 53 */ 54 private final List<OutputChangeListener> mListeners = Collections.synchronizedList( 55 new ArrayList<OutputChangeListener>()); 56 57 public void addListener(OutputChangeListener listener) { 58 mListeners.add(listener); 59 } 60 61 public void removeListener(OutputChangeListener listener) { 62 mListeners.remove(listener); 63 } 64 65 public boolean getOutput() { 66 synchronized (mLock) { 67 return mOutput; 68 } 69 } 70 71 /** 72 * Updates the state of the given input, dispatching to all output change 73 * listeners if the output changes. 74 * 75 * @param index the index of the input to change. 76 * @param newValue the new value of the input. 77 * @return The new output. 78 */ 79 public boolean setInput(Input input, boolean newValue) { 80 synchronized (mLock) { 81 mInputs.put(input, newValue); 82 83 // If the new input value is the same as the existing output, 84 // then nothing will change. 85 if (newValue == mOutput) { 86 return mOutput; 87 } else { 88 boolean oldOutput = mOutput; 89 90 // Recompute the output by AND'ing all the inputs. 91 mOutput = true; 92 for (Boolean b : mInputs.values()) { 93 mOutput &= b; 94 } 95 96 // If the output has changed, notify the listeners. 97 if (oldOutput != mOutput) { 98 notifyListeners(); 99 } 100 101 return mOutput; 102 } 103 } 104 } 105 106 public ConjunctionListenerMux(Class<Input> clazz, OutputChangeListener listener) { 107 this(clazz); 108 addListener(listener); 109 } 110 111 public ConjunctionListenerMux(Class<Input> clazz) { 112 mInputs = new EnumMap<Input, Boolean>(clazz); 113 114 for (Input i : clazz.getEnumConstants()) { 115 mInputs.put(i, false); 116 } 117 118 mOutput = false; 119 } 120 121 /** 122 * Notifies all listeners of the current state, regardless of whether or not 123 * it has actually changed. 124 */ 125 public void notifyListeners() { 126 synchronized (mLock) { 127 for (OutputChangeListener listener : mListeners) { 128 listener.onOutputChange(mOutput); 129 } 130 } 131 } 132} 133