1 /*
2  * Copyright (C) 2020 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 androidx.constraintlayout.widget;
18 
19 import android.util.SparseIntArray;
20 
21 import java.lang.ref.WeakReference;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.List;
26 
27 /**
28  * Shared values
29  */
30 public class SharedValues {
31     public static final int UNSET = -1;
32 
33     private SparseIntArray mValues = new SparseIntArray();
34     private HashMap<Integer, HashSet<WeakReference<SharedValuesListener>>> mValuesListeners =
35             new HashMap<>();
36 
37     /**
38      * interface for listeners
39      */
40     public interface SharedValuesListener {
41         // @TODO: add description
42 
43         /**
44          *
45          * @param key
46          * @param newValue
47          * @param oldValue
48          */
onNewValue(int key, int newValue, int oldValue)49         void onNewValue(int key, int newValue, int oldValue);
50     }
51 
52     /**
53      * Add a listener for a key
54      * @param key
55      * @param listener
56      */
addListener(int key, SharedValuesListener listener)57     public void addListener(int key, SharedValuesListener listener) {
58         HashSet<WeakReference<SharedValuesListener>> listeners = mValuesListeners.get(key);
59         if (listeners == null) {
60             listeners = new HashSet<>();
61             mValuesListeners.put(key, listeners);
62         }
63         listeners.add(new WeakReference<>(listener));
64     }
65 
66     /**
67      * Remove listener for a key (will not be removed for other keys)
68      * @param key
69      * @param listener
70      */
removeListener(int key, SharedValuesListener listener)71     public void removeListener(int key, SharedValuesListener listener) {
72         HashSet<WeakReference<SharedValuesListener>> listeners = mValuesListeners.get(key);
73         if (listeners == null) {
74             return;
75         }
76         List<WeakReference<SharedValuesListener>> toRemove = new ArrayList<>();
77         for (WeakReference<SharedValuesListener> listenerWeakReference : listeners) {
78             SharedValuesListener l = listenerWeakReference.get();
79             if (l == null || l == listener) {
80                 toRemove.add(listenerWeakReference);
81             }
82         }
83         listeners.removeAll(toRemove);
84     }
85 
86     /**
87      * Remove a listener
88      * @param listener
89      */
removeListener(SharedValuesListener listener)90     public void removeListener(SharedValuesListener listener) {
91         for (Integer key : mValuesListeners.keySet()) {
92             removeListener(key, listener);
93         }
94     }
95 
96     /**
97      * remove all listeners
98      */
clearListeners()99     public void clearListeners() {
100         mValuesListeners.clear();
101     }
102 
103     /**
104      * get the value from the map
105      * @param key
106      * @return
107      */
getValue(int key)108     public int getValue(int key) {
109         return mValues.get(key, UNSET);
110     }
111 
112     /**
113      * notify that value has changed
114      * @param key
115      * @param value
116      */
fireNewValue(int key, int value)117     public void fireNewValue(int key, int value) {
118         boolean needsCleanup = false;
119         int previousValue = mValues.get(key, UNSET);
120         if (previousValue == value) {
121             // don't send the value to listeners if it's the same one.
122             return;
123         }
124         mValues.put(key, value);
125         HashSet<WeakReference<SharedValuesListener>> listeners = mValuesListeners.get(key);
126         if (listeners == null) {
127             return;
128         }
129 
130         for (WeakReference<SharedValuesListener> listenerWeakReference : listeners) {
131             SharedValuesListener l = listenerWeakReference.get();
132             if (l != null) {
133                 l.onNewValue(key, value, previousValue);
134             } else {
135                 needsCleanup = true;
136             }
137         }
138 
139         if (needsCleanup) {
140             List<WeakReference<SharedValuesListener>> toRemove = new ArrayList<>();
141             for (WeakReference<SharedValuesListener> listenerWeakReference : listeners) {
142                 SharedValuesListener listener = listenerWeakReference.get();
143                 if (listener == null) {
144                     toRemove.add(listenerWeakReference);
145                 }
146             }
147             listeners.removeAll(toRemove);
148         }
149     }
150 }
151