summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/util/ConjunctionListenerMux.java
blob: a32072400ff1ebb944b055462941fa62e15b5ee5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * 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 ConjunctionListenerMux<Input extends Enum<Input>> {
    /**
     * Callback for listening to changes to the conjunction of all inputs.
     */
    public static interface OutputChangeListener {
        /**
         * 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 onOutputChange(boolean state);
    }

    /** Mutex for mValues and mState. */
    private final Object mLock = new Object();
    /** Stores the current input state. */
    private final EnumMap<Input, Boolean> 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<OutputChangeListener> mListeners = Collections.synchronizedList(
            new ArrayList<OutputChangeListener>());

    public void addListener(OutputChangeListener listener) {
        mListeners.add(listener);
    }

    public void removeListener(OutputChangeListener 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 ConjunctionListenerMux(Class<Input> clazz, OutputChangeListener listener) {
        this(clazz);
        addListener(listener);
    }

    public ConjunctionListenerMux(Class<Input> clazz) {
        mInputs = new EnumMap<Input, Boolean>(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 (OutputChangeListener listener : mListeners) {
                listener.onOutputChange(mOutput);
            }
        }
    }
}