1 /*
2  * Copyright (C) 2015 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 package android.databinding.adapters;
17 
18 import android.os.Build.VERSION;
19 import android.os.Build.VERSION_CODES;
20 import android.util.SparseArray;
21 import android.view.View;
22 
23 import java.lang.ref.WeakReference;
24 import java.util.WeakHashMap;
25 
26 public class ListenerUtil {
27     private static SparseArray<WeakHashMap<View, WeakReference<?>>> sListeners =
28             new SparseArray<WeakHashMap<View, WeakReference<?>>>();
29 
30     /**
31      * This method tracks listeners for a View. Only one listener per listenerResourceId
32      * can be tracked at a time. This is useful for add*Listener and remove*Listener methods
33      * when used with BindingAdapters. This guarantees not to leak the listener or the View,
34      * so will not keep a strong reference to either.
35      *
36      * Example usage:
37      *<pre><code>@BindingAdapter("onFoo")
38      * public static void addFooListener(MyView view, OnFooListener listener) {
39      *     OnFooListener oldValue = ListenerUtil.trackListener(view, listener, R.id.fooListener);
40      *     if (oldValue != null) {
41      *         view.removeOnFooListener(oldValue);
42      *     }
43      *     if (listener != null) {
44      *         view.addOnFooListener(listener);
45      *     }
46      * }</code></pre>
47      *
48      * @param view The View that will have this listener
49      * @param listener The listener to keep track of. May be null if the listener is being removed.
50      * @param listenerResourceId A unique resource ID associated with the listener type.
51      * @return The previously tracked listener. This will be null if the View did not have
52      * a previously-tracked listener.
53      */
54     @SuppressWarnings("unchecked")
trackListener(View view, T listener, int listenerResourceId)55     public static <T> T trackListener(View view, T listener, int listenerResourceId) {
56         if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
57             final T oldValue = (T) view.getTag(listenerResourceId);
58             view.setTag(listenerResourceId, listener);
59             return oldValue;
60         } else {
61             synchronized (sListeners) {
62                 WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
63                 if (listeners == null) {
64                     listeners = new WeakHashMap<View, WeakReference<?>>();
65                     sListeners.put(listenerResourceId, listeners);
66                 }
67                 final WeakReference<T> oldValue;
68                 if (listener == null) {
69                     oldValue = (WeakReference<T>) listeners.remove(view);
70                 } else {
71                     oldValue = (WeakReference<T>) listeners.put(view, new WeakReference(listener));
72                 }
73                 if (oldValue == null) {
74                     return null;
75                 } else {
76                     return oldValue.get();
77                 }
78             }
79         }
80     }
81 
82     /**
83      * Returns the previous value for a listener if one was stored previously with
84      * {@link #trackListener(View, Object, int)}
85      * @param view The View to check for a listener previously stored with
86      * {@link #trackListener(View, Object, int)}
87      * @param listenerResourceId A unique resource ID associated with the listener type.
88      * @return The previously tracked listener. This will be null if the View did not have
89      * a previously-tracked listener.
90      */
getListener(View view, int listenerResourceId)91     public static <T> T getListener(View view, int listenerResourceId) {
92         if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
93             return (T) view.getTag(listenerResourceId);
94         } else {
95             synchronized (sListeners) {
96                 WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
97                 if (listeners == null) {
98                     return null;
99                 }
100                 final WeakReference<T> oldValue = (WeakReference<T>) listeners.get(view);
101                 if (oldValue == null) {
102                     return null;
103                 } else {
104                     return oldValue.get();
105                 }
106             }
107         }
108     }
109 }
110