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