1716ba89e7f459f49ea85070d4710c1d79d715298George Mount/*
2716ba89e7f459f49ea85070d4710c1d79d715298George Mount * Copyright (C) 2015 The Android Open Source Project
3716ba89e7f459f49ea85070d4710c1d79d715298George Mount *
4716ba89e7f459f49ea85070d4710c1d79d715298George Mount * Licensed under the Apache License, Version 2.0 (the "License");
5716ba89e7f459f49ea85070d4710c1d79d715298George Mount * you may not use this file except in compliance with the License.
6716ba89e7f459f49ea85070d4710c1d79d715298George Mount * You may obtain a copy of the License at
7716ba89e7f459f49ea85070d4710c1d79d715298George Mount *
8716ba89e7f459f49ea85070d4710c1d79d715298George Mount *      http://www.apache.org/licenses/LICENSE-2.0
9716ba89e7f459f49ea85070d4710c1d79d715298George Mount *
10716ba89e7f459f49ea85070d4710c1d79d715298George Mount * Unless required by applicable law or agreed to in writing, software
11716ba89e7f459f49ea85070d4710c1d79d715298George Mount * distributed under the License is distributed on an "AS IS" BASIS,
12716ba89e7f459f49ea85070d4710c1d79d715298George Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13716ba89e7f459f49ea85070d4710c1d79d715298George Mount * See the License for the specific language governing permissions and
14716ba89e7f459f49ea85070d4710c1d79d715298George Mount * limitations under the License.
15716ba89e7f459f49ea85070d4710c1d79d715298George Mount */
16716ba89e7f459f49ea85070d4710c1d79d715298George Mountpackage android.databinding.adapters;
17716ba89e7f459f49ea85070d4710c1d79d715298George Mount
18716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport android.os.Build.VERSION;
19716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport android.os.Build.VERSION_CODES;
20716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport android.util.SparseArray;
21716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport android.view.View;
22716ba89e7f459f49ea85070d4710c1d79d715298George Mount
23716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport java.lang.ref.WeakReference;
24716ba89e7f459f49ea85070d4710c1d79d715298George Mountimport java.util.WeakHashMap;
25716ba89e7f459f49ea85070d4710c1d79d715298George Mount
26716ba89e7f459f49ea85070d4710c1d79d715298George Mountpublic class ListenerUtil {
27716ba89e7f459f49ea85070d4710c1d79d715298George Mount    private static SparseArray<WeakHashMap<View, WeakReference<?>>> sListeners =
289784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar            new SparseArray<WeakHashMap<View, WeakReference<?>>>();
29716ba89e7f459f49ea85070d4710c1d79d715298George Mount
30716ba89e7f459f49ea85070d4710c1d79d715298George Mount    /**
31716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * This method tracks listeners for a View. Only one listener per listenerResourceId
32716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * can be tracked at a time. This is useful for add*Listener and remove*Listener methods
33716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * when used with BindingAdapters. This guarantees not to leak the listener or the View,
34716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * so will not keep a strong reference to either.
35716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *
36716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * Example usage:
37c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     *<pre><code>@BindingAdapter("onFoo")
38716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * public static void addFooListener(MyView view, OnFooListener listener) {
39716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *     OnFooListener oldValue = ListenerUtil.trackListener(view, listener, R.id.fooListener);
40716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *     if (oldValue != null) {
41716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *         view.removeOnFooListener(oldValue);
42716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *     }
43716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *     if (listener != null) {
44716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *         view.addOnFooListener(listener);
45716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *     }
46c9a5d6f140f732ca0ff279a4b1ee315072e1c422George Mount     * }</code></pre>
47716ba89e7f459f49ea85070d4710c1d79d715298George Mount     *
48716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * @param view The View that will have this listener
49716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * @param listener The listener to keep track of. May be null if the listener is being removed.
50716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * @param listenerResourceId A unique resource ID associated with the listener type.
51716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * @return The previously tracked listener. This will be null if the View did not have
52716ba89e7f459f49ea85070d4710c1d79d715298George Mount     * a previously-tracked listener.
53716ba89e7f459f49ea85070d4710c1d79d715298George Mount     */
54716ba89e7f459f49ea85070d4710c1d79d715298George Mount    @SuppressWarnings("unchecked")
55716ba89e7f459f49ea85070d4710c1d79d715298George Mount    public static <T> T trackListener(View view, T listener, int listenerResourceId) {
56716ba89e7f459f49ea85070d4710c1d79d715298George Mount        if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
57716ba89e7f459f49ea85070d4710c1d79d715298George Mount            final T oldValue = (T) view.getTag(listenerResourceId);
58716ba89e7f459f49ea85070d4710c1d79d715298George Mount            view.setTag(listenerResourceId, listener);
59716ba89e7f459f49ea85070d4710c1d79d715298George Mount            return oldValue;
60716ba89e7f459f49ea85070d4710c1d79d715298George Mount        } else {
61716ba89e7f459f49ea85070d4710c1d79d715298George Mount            synchronized (sListeners) {
62716ba89e7f459f49ea85070d4710c1d79d715298George Mount                WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
63716ba89e7f459f49ea85070d4710c1d79d715298George Mount                if (listeners == null) {
649784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar                    listeners = new WeakHashMap<View, WeakReference<?>>();
65716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    sListeners.put(listenerResourceId, listeners);
66716ba89e7f459f49ea85070d4710c1d79d715298George Mount                }
67716ba89e7f459f49ea85070d4710c1d79d715298George Mount                final WeakReference<T> oldValue;
68716ba89e7f459f49ea85070d4710c1d79d715298George Mount                if (listener == null) {
69716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    oldValue = (WeakReference<T>) listeners.remove(view);
70716ba89e7f459f49ea85070d4710c1d79d715298George Mount                } else {
71716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    oldValue = (WeakReference<T>) listeners.put(view, new WeakReference(listener));
72716ba89e7f459f49ea85070d4710c1d79d715298George Mount                }
73716ba89e7f459f49ea85070d4710c1d79d715298George Mount                if (oldValue == null) {
74716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    return null;
75716ba89e7f459f49ea85070d4710c1d79d715298George Mount                } else {
76716ba89e7f459f49ea85070d4710c1d79d715298George Mount                    return oldValue.get();
77716ba89e7f459f49ea85070d4710c1d79d715298George Mount                }
78716ba89e7f459f49ea85070d4710c1d79d715298George Mount            }
79716ba89e7f459f49ea85070d4710c1d79d715298George Mount        }
80716ba89e7f459f49ea85070d4710c1d79d715298George Mount    }
81716ba89e7f459f49ea85070d4710c1d79d715298George Mount
823b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount    /**
833b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * Returns the previous value for a listener if one was stored previously with
843b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * {@link #trackListener(View, Object, int)}
853b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * @param view The View to check for a listener previously stored with
863b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * {@link #trackListener(View, Object, int)}
873b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * @param listenerResourceId A unique resource ID associated with the listener type.
883b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * @return The previously tracked listener. This will be null if the View did not have
893b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     * a previously-tracked listener.
903b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount     */
913b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount    public static <T> T getListener(View view, int listenerResourceId) {
923b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount        if (VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH) {
933b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount            return (T) view.getTag(listenerResourceId);
943b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount        } else {
953b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount            synchronized (sListeners) {
963b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                WeakHashMap<View, WeakReference<?>> listeners = sListeners.get(listenerResourceId);
973b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                if (listeners == null) {
983b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                    return null;
993b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                }
1003b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                final WeakReference<T> oldValue = (WeakReference<T>) listeners.get(view);
1013b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                if (oldValue == null) {
1023b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                    return null;
1033b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                } else {
1043b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                    return oldValue.get();
1053b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount                }
1063b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount            }
1073b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount        }
1083b920788e90bb0abe615a5d5c899915f0014444bGeorge Mount    }
109716ba89e7f459f49ea85070d4710c1d79d715298George Mount}
110