Home | History | Annotate | Download | only in util
      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 
     17 package com.android.camera.util;
     18 
     19 import java.util.ArrayList;
     20 import java.util.Collections;
     21 import java.util.EnumMap;
     22 import 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  */
     29 public class ListenerCombiner<Input extends Enum<Input>> {
     30     /**
     31      * Callback for listening to changes to the conjunction of all inputs.
     32      */
     33     public static interface StateChangeListener {
     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 onStateChange(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<StateChangeListener> mListeners = Collections.synchronizedList(
     55             new ArrayList<StateChangeListener>());
     56 
     57     public void addListener(StateChangeListener listener) {
     58         mListeners.add(listener);
     59     }
     60 
     61     public void removeListener(StateChangeListener 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 ListenerCombiner(Class<Input> clazz, StateChangeListener listener) {
    107         this(clazz);
    108         addListener(listener);
    109     }
    110 
    111     public ListenerCombiner(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 (StateChangeListener listener : mListeners) {
    128                 listener.onStateChange(mOutput);
    129             }
    130         }
    131     }
    132 }
    133