From b7196e328a25a28078906d8afa50ba9636ae7bcb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 26 Dec 2017 14:12:37 -0600 Subject: Rename package bindings -> databinding Signed-off-by: Samuel Holland --- .../com/wireguard/android/ConfigListFragment.java | 2 +- .../com/wireguard/android/backends/VpnService.java | 4 +- .../android/bindings/BindingAdapters.java | 123 -------------------- .../android/bindings/ItemChangeListener.java | 128 --------------------- .../android/bindings/ObservableListAdapter.java | 119 ------------------- .../android/bindings/ObservableMapAdapter.java | 120 ------------------- .../android/bindings/ObservableSortedMap.java | 13 --- .../android/bindings/ObservableTreeMap.java | 66 ----------- .../android/databinding/BindingAdapters.java | 123 ++++++++++++++++++++ .../android/databinding/ItemChangeListener.java | 128 +++++++++++++++++++++ .../android/databinding/ObservableListAdapter.java | 119 +++++++++++++++++++ .../android/databinding/ObservableMapAdapter.java | 120 +++++++++++++++++++ .../android/databinding/ObservableSortedMap.java | 13 +++ .../android/databinding/ObservableTreeMap.java | 66 +++++++++++ 14 files changed, 572 insertions(+), 572 deletions(-) delete mode 100644 app/src/main/java/com/wireguard/android/bindings/BindingAdapters.java delete mode 100644 app/src/main/java/com/wireguard/android/bindings/ItemChangeListener.java delete mode 100644 app/src/main/java/com/wireguard/android/bindings/ObservableListAdapter.java delete mode 100644 app/src/main/java/com/wireguard/android/bindings/ObservableMapAdapter.java delete mode 100644 app/src/main/java/com/wireguard/android/bindings/ObservableSortedMap.java delete mode 100644 app/src/main/java/com/wireguard/android/bindings/ObservableTreeMap.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/ItemChangeListener.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/ObservableListAdapter.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/ObservableMapAdapter.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/ObservableSortedMap.java create mode 100644 app/src/main/java/com/wireguard/android/databinding/ObservableTreeMap.java (limited to 'app/src/main/java/com/wireguard/android') diff --git a/app/src/main/java/com/wireguard/android/ConfigListFragment.java b/app/src/main/java/com/wireguard/android/ConfigListFragment.java index a8c8bcd4..b7460134 100644 --- a/app/src/main/java/com/wireguard/android/ConfigListFragment.java +++ b/app/src/main/java/com/wireguard/android/ConfigListFragment.java @@ -16,7 +16,7 @@ import android.widget.AbsListView; import android.widget.AdapterView; import com.wireguard.android.backends.VpnService; -import com.wireguard.android.bindings.ObservableMapAdapter; +import com.wireguard.android.databinding.ObservableMapAdapter; import com.wireguard.android.databinding.ConfigListFragmentBinding; import com.wireguard.config.Config; diff --git a/app/src/main/java/com/wireguard/android/backends/VpnService.java b/app/src/main/java/com/wireguard/android/backends/VpnService.java index 16596721..5002a835 100644 --- a/app/src/main/java/com/wireguard/android/backends/VpnService.java +++ b/app/src/main/java/com/wireguard/android/backends/VpnService.java @@ -21,8 +21,8 @@ import android.widget.Toast; import com.wireguard.android.NotSupportedActivity; import com.wireguard.android.QuickTileService; import com.wireguard.android.R; -import com.wireguard.android.bindings.ObservableSortedMap; -import com.wireguard.android.bindings.ObservableTreeMap; +import com.wireguard.android.databinding.ObservableSortedMap; +import com.wireguard.android.databinding.ObservableTreeMap; import com.wireguard.config.Config; import com.wireguard.config.Peer; diff --git a/app/src/main/java/com/wireguard/android/bindings/BindingAdapters.java b/app/src/main/java/com/wireguard/android/bindings/BindingAdapters.java deleted file mode 100644 index fffe191a..00000000 --- a/app/src/main/java/com/wireguard/android/bindings/BindingAdapters.java +++ /dev/null @@ -1,123 +0,0 @@ -package com.wireguard.android.bindings; - -import android.databinding.BindingAdapter; -import android.databinding.ObservableList; -import android.databinding.adapters.ListenerUtil; -import android.graphics.Typeface; -import android.text.InputFilter; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.TextView; - -import com.wireguard.android.R; -import com.wireguard.android.widgets.ToggleSwitch; - -/** - * Static methods for use by generated code in the Android data binding library. - */ - -@SuppressWarnings("unused") -public final class BindingAdapters { - @BindingAdapter({"app:checked"}) - public static void setChecked(final ToggleSwitch view, final boolean checked) { - view.setCheckedInternal(checked); - } - - @BindingAdapter({"filter"}) - public static void setFilter(final TextView view, final InputFilter filter) { - view.setFilters(new InputFilter[]{filter}); - } - - @BindingAdapter({"items", "layout"}) - public static void setItems(final LinearLayout view, - final ObservableList oldList, final int oldLayoutId, - final ObservableList newList, final int newLayoutId) { - if (oldList == newList && oldLayoutId == newLayoutId) - return; - ItemChangeListener listener = ListenerUtil.getListener(view, R.id.item_change_listener); - // If the layout changes, any existing listener must be replaced. - if (listener != null && oldList != null && oldLayoutId != newLayoutId) { - listener.setList(null); - listener = null; - // Stop tracking the old listener. - ListenerUtil.trackListener(view, null, R.id.item_change_listener); - } - // Avoid adding a listener when there is no new list or layout. - if (newList == null || newLayoutId == 0) - return; - if (listener == null) { - listener = new ItemChangeListener<>(view, newLayoutId); - ListenerUtil.trackListener(view, listener, R.id.item_change_listener); - } - // Either the list changed, or this is an entirely new listener because the layout changed. - listener.setList(newList); - } - - @BindingAdapter({"items", "layout"}) - public static void setItems(final ListView view, - final ObservableList oldList, final int oldLayoutId, - final ObservableList newList, final int newLayoutId) { - if (oldList == newList && oldLayoutId == newLayoutId) - return; - // The ListAdapter interface is not generic, so this cannot be checked. - @SuppressWarnings("unchecked") - ObservableListAdapter adapter = (ObservableListAdapter) view.getAdapter(); - // If the layout changes, any existing adapter must be replaced. - if (adapter != null && oldList != null && oldLayoutId != newLayoutId) { - adapter.setList(null); - adapter = null; - } - // Avoid setting an adapter when there is no new list or layout. - if (newList == null || newLayoutId == 0) - return; - if (adapter == null) { - adapter = new ObservableListAdapter<>(view.getContext(), newLayoutId, newList); - view.setAdapter(adapter); - } - // Either the list changed, or this is an entirely new listener because the layout changed. - adapter.setList(newList); - } - - @BindingAdapter({"items", "layout"}) - public static , V> void setItems(final ListView view, - final ObservableSortedMap oldMap, - final int oldLayoutId, - final ObservableSortedMap newMap, - final int newLayoutId) { - if (oldMap == newMap && oldLayoutId == newLayoutId) - return; - // The ListAdapter interface is not generic, so this cannot be checked. - @SuppressWarnings("unchecked") - ObservableMapAdapter adapter = (ObservableMapAdapter) view.getAdapter(); - // If the layout changes, any existing adapter must be replaced. - if (adapter != null && oldMap != null && oldLayoutId != newLayoutId) { - adapter.setMap(null); - adapter = null; - } - // Avoid setting an adapter when there is no new list or layout. - if (newMap == null || newLayoutId == 0) - return; - if (adapter == null) { - adapter = new ObservableMapAdapter<>(view.getContext(), newLayoutId, newMap); - view.setAdapter(adapter); - } - // Either the list changed, or this is an entirely new listener because the layout changed. - adapter.setMap(newMap); - } - - @BindingAdapter({"app:onBeforeCheckedChanged"}) - public static void setOnBeforeCheckedChanged(final ToggleSwitch view, - final ToggleSwitch.OnBeforeCheckedChangeListener - listener) { - view.setOnBeforeCheckedChangeListener(listener); - } - - @BindingAdapter({"android:textStyle"}) - public static void setTextStyle(final TextView view, final Typeface typeface) { - view.setTypeface(typeface); - } - - private BindingAdapters() { - // Prevent instantiation. - } -} diff --git a/app/src/main/java/com/wireguard/android/bindings/ItemChangeListener.java b/app/src/main/java/com/wireguard/android/bindings/ItemChangeListener.java deleted file mode 100644 index 1ee37a86..00000000 --- a/app/src/main/java/com/wireguard/android/bindings/ItemChangeListener.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.wireguard.android.bindings; - -import android.databinding.DataBindingUtil; -import android.databinding.ObservableList; -import android.databinding.ViewDataBinding; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.wireguard.android.BR; - -import java.lang.ref.WeakReference; - -/** - * Helper class for binding an ObservableList to the children of a ViewGroup. - */ - -class ItemChangeListener { - private final OnListChangedCallback callback = new OnListChangedCallback<>(this); - private final ViewGroup container; - private final int layoutId; - private final LayoutInflater layoutInflater; - private ObservableList list; - - ItemChangeListener(final ViewGroup container, final int layoutId) { - this.container = container; - this.layoutId = layoutId; - layoutInflater = LayoutInflater.from(container.getContext()); - } - - private View getView(final int position, final View convertView) { - ViewDataBinding binding = DataBindingUtil.getBinding(convertView); - if (binding == null) - binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false); - binding.setVariable(BR.item, list.get(position)); - binding.executePendingBindings(); - return binding.getRoot(); - } - - void setList(final ObservableList newList) { - if (list != null) - list.removeOnListChangedCallback(callback); - list = newList; - if (list != null) { - list.addOnListChangedCallback(callback); - callback.onChanged(list); - } else { - container.removeAllViews(); - } - } - - private static class OnListChangedCallback - extends ObservableList.OnListChangedCallback> { - - private final WeakReference> weakListener; - - private OnListChangedCallback(final ItemChangeListener listener) { - weakListener = new WeakReference<>(listener); - } - - @Override - public void onChanged(final ObservableList sender) { - final ItemChangeListener listener = weakListener.get(); - if (listener != null) { - // TODO: recycle views - listener.container.removeAllViews(); - for (int i = 0; i < sender.size(); ++i) - listener.container.addView(listener.getView(i, null)); - } else { - sender.removeOnListChangedCallback(this); - } - } - - @Override - public void onItemRangeChanged(final ObservableList sender, final int positionStart, - final int itemCount) { - final ItemChangeListener listener = weakListener.get(); - if (listener != null) { - for (int i = positionStart; i < positionStart + itemCount; ++i) { - final View child = listener.container.getChildAt(i); - listener.container.removeViewAt(i); - listener.container.addView(listener.getView(i, child)); - } - } else { - sender.removeOnListChangedCallback(this); - } - } - - @Override - public void onItemRangeInserted(final ObservableList sender, final int positionStart, - final int itemCount) { - final ItemChangeListener listener = weakListener.get(); - if (listener != null) { - for (int i = positionStart; i < positionStart + itemCount; ++i) - listener.container.addView(listener.getView(i, null)); - } else { - sender.removeOnListChangedCallback(this); - } - } - - @Override - public void onItemRangeMoved(final ObservableList sender, final int fromPosition, - final int toPosition, final int itemCount) { - final ItemChangeListener listener = weakListener.get(); - if (listener != null) { - final View[] views = new View[itemCount]; - for (int i = 0; i < itemCount; ++i) - views[i] = listener.container.getChildAt(fromPosition + i); - listener.container.removeViews(fromPosition, itemCount); - for (int i = 0; i < itemCount; ++i) - listener.container.addView(views[i], toPosition + i); - } else { - sender.removeOnListChangedCallback(this); - } - } - - @Override - public void onItemRangeRemoved(final ObservableList sender, final int positionStart, - final int itemCount) { - final ItemChangeListener listener = weakListener.get(); - if (listener != null) { - listener.container.removeViews(positionStart, itemCount); - } else { - sender.removeOnListChangedCallback(this); - } - } - } -} diff --git a/app/src/main/java/com/wireguard/android/bindings/ObservableListAdapter.java b/app/src/main/java/com/wireguard/android/bindings/ObservableListAdapter.java deleted file mode 100644 index 7938a2a6..00000000 --- a/app/src/main/java/com/wireguard/android/bindings/ObservableListAdapter.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.wireguard.android.bindings; - -import android.content.Context; -import android.databinding.DataBindingUtil; -import android.databinding.ObservableList; -import android.databinding.ViewDataBinding; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListAdapter; - -import com.wireguard.android.BR; - -import java.lang.ref.WeakReference; - -/** - * A generic ListAdapter backed by an ObservableList. - */ - -class ObservableListAdapter extends BaseAdapter implements ListAdapter { - private final OnListChangedCallback callback = new OnListChangedCallback<>(this); - private final int layoutId; - private final LayoutInflater layoutInflater; - private ObservableList list; - - ObservableListAdapter(final Context context, final int layoutId, final ObservableList list) { - this.layoutId = layoutId; - layoutInflater = LayoutInflater.from(context); - setList(list); - } - - @Override - public int getCount() { - return list != null ? list.size() : 0; - } - - @Override - public T getItem(final int position) { - if (list == null || position < 0 || position >= list.size()) - return null; - return list.get(position); - } - - @Override - public long getItemId(final int position) { - if (list == null || position < 0 || position >= list.size()) - return -1; - return list.get(position).hashCode(); - } - - @Override - public View getView(final int position, final View convertView, final ViewGroup parent) { - ViewDataBinding binding = DataBindingUtil.getBinding(convertView); - if (binding == null) - binding = DataBindingUtil.inflate(layoutInflater, layoutId, parent, false); - binding.setVariable(BR.item, getItem(position)); - binding.executePendingBindings(); - return binding.getRoot(); - } - - @Override - public boolean hasStableIds() { - return true; - } - - void setList(final ObservableList newList) { - if (list != null) - list.removeOnListChangedCallback(callback); - list = newList; - if (list != null) { - list.addOnListChangedCallback(callback); - } - notifyDataSetChanged(); - } - - private static class OnListChangedCallback - extends ObservableList.OnListChangedCallback> { - - private final WeakReference> weakAdapter; - - private OnListChangedCallback(final ObservableListAdapter adapter) { - weakAdapter = new WeakReference<>(adapter); - } - - @Override - public void onChanged(final ObservableList sender) { - final ObservableListAdapter adapter = weakAdapter.get(); - if (adapter != null) - adapter.notifyDataSetChanged(); - else - sender.removeOnListChangedCallback(this); - } - - @Override - public void onItemRangeChanged(final ObservableList sender, final int positionStart, - final int itemCount) { - onChanged(sender); - } - - @Override - public void onItemRangeInserted(final ObservableList sender, final int positionStart, - final int itemCount) { - onChanged(sender); - } - - @Override - public void onItemRangeMoved(final ObservableList sender, final int fromPosition, - final int toPosition, final int itemCount) { - onChanged(sender); - } - - @Override - public void onItemRangeRemoved(final ObservableList sender, final int positionStart, - final int itemCount) { - onChanged(sender); - } - } -} diff --git a/app/src/main/java/com/wireguard/android/bindings/ObservableMapAdapter.java b/app/src/main/java/com/wireguard/android/bindings/ObservableMapAdapter.java deleted file mode 100644 index 8e474e47..00000000 --- a/app/src/main/java/com/wireguard/android/bindings/ObservableMapAdapter.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.wireguard.android.bindings; - -import android.content.Context; -import android.databinding.DataBindingUtil; -import android.databinding.ObservableMap; -import android.databinding.ViewDataBinding; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ListAdapter; - -import com.wireguard.android.BR; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; - -/** - * A generic ListAdapter backed by a TreeMap that adds observability. - */ - -public class ObservableMapAdapter, V> extends BaseAdapter - implements ListAdapter { - private final OnMapChangedCallback callback = new OnMapChangedCallback<>(this); - private ArrayList keys; - private final int layoutId; - private final LayoutInflater layoutInflater; - private ObservableSortedMap map; - - ObservableMapAdapter(final Context context, final int layoutId, - final ObservableSortedMap map) { - this.layoutId = layoutId; - layoutInflater = LayoutInflater.from(context); - setMap(map); - } - - @Override - public int getCount() { - return map != null ? map.size() : 0; - } - - @Override - public V getItem(final int position) { - if (map == null || position < 0 || position >= map.size()) - return null; - return map.get(getKey(position)); - } - - @Override - public long getItemId(final int position) { - if (map == null || position < 0 || position >= map.size()) - return -1; - return getItem(position).hashCode(); - } - - private K getKey(final int position) { - return getKeys().get(position); - } - - private ArrayList getKeys() { - if (keys == null) - keys = new ArrayList<>(map.keySet()); - return keys; - } - - public int getPosition(final K key) { - if (map == null || key == null) - return -1; - return Collections.binarySearch(getKeys(), key); - } - - @Override - public View getView(final int position, final View convertView, final ViewGroup parent) { - ViewDataBinding binding = DataBindingUtil.getBinding(convertView); - if (binding == null) - binding = DataBindingUtil.inflate(layoutInflater, layoutId, parent, false); - binding.setVariable(BR.key, getKey(position)); - binding.setVariable(BR.item, getItem(position)); - binding.executePendingBindings(); - return binding.getRoot(); - } - - @Override - public boolean hasStableIds() { - return true; - } - - void setMap(final ObservableSortedMap newMap) { - if (map != null) - map.removeOnMapChangedCallback(callback); - keys = null; - map = newMap; - if (map != null) { - map.addOnMapChangedCallback(callback); - } - notifyDataSetChanged(); - } - - private static class OnMapChangedCallback, V> - extends ObservableMap.OnMapChangedCallback, K, V> { - - private final WeakReference> weakAdapter; - - private OnMapChangedCallback(final ObservableMapAdapter adapter) { - weakAdapter = new WeakReference<>(adapter); - } - - @Override - public void onMapChanged(final ObservableSortedMap sender, final K key) { - final ObservableMapAdapter adapter = weakAdapter.get(); - if (adapter != null) { - adapter.keys = null; - adapter.notifyDataSetChanged(); - } else { - sender.removeOnMapChangedCallback(this); - } - } - } -} diff --git a/app/src/main/java/com/wireguard/android/bindings/ObservableSortedMap.java b/app/src/main/java/com/wireguard/android/bindings/ObservableSortedMap.java deleted file mode 100644 index 3317c6a7..00000000 --- a/app/src/main/java/com/wireguard/android/bindings/ObservableSortedMap.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.wireguard.android.bindings; - -import android.databinding.ObservableMap; - -import java.util.SortedMap; - -/** - * Interface for maps that are both observable and sorted. - */ - -public interface ObservableSortedMap extends ObservableMap, SortedMap { - // No additional methods. -} diff --git a/app/src/main/java/com/wireguard/android/bindings/ObservableTreeMap.java b/app/src/main/java/com/wireguard/android/bindings/ObservableTreeMap.java deleted file mode 100644 index 34fcab61..00000000 --- a/app/src/main/java/com/wireguard/android/bindings/ObservableTreeMap.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.wireguard.android.bindings; - -import android.databinding.MapChangeRegistry; -import android.databinding.ObservableMap; -import android.support.annotation.NonNull; - -import java.util.Map; -import java.util.TreeMap; - -/** - * Observable version of a TreeMap. Only notifies for changes made through methods, not iterators or - * views. This behavior is in line with that of ObservableArrayMap. - */ - -public class ObservableTreeMap extends TreeMap implements ObservableSortedMap { - private transient MapChangeRegistry listeners; - - @Override - public void clear() { - super.clear(); - notifyChange(null); - } - - @Override - public void addOnMapChangedCallback( - final OnMapChangedCallback, K, V> listener) { - if (listeners == null) - listeners = new MapChangeRegistry(); - listeners.add(listener); - } - - private void notifyChange(final K key) { - if (listeners != null) - listeners.notifyChange(this, key); - } - - @Override - public V put(final K key, final V value) { - final V oldValue = super.put(key, value); - notifyChange(key); - return oldValue; - } - - @Override - public void putAll(@NonNull final Map map) { - super.putAll(map); - for (final K key : map.keySet()) - notifyChange(key); - } - - @Override - public V remove(final Object key) { - final V oldValue = super.remove(key); - @SuppressWarnings("unchecked") - final K k = (K) key; - notifyChange(k); - return oldValue; - } - - @Override - public void removeOnMapChangedCallback( - final OnMapChangedCallback, K, V> listener) { - if (listeners != null) - listeners.remove(listener); - } -} diff --git a/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java b/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java new file mode 100644 index 00000000..3e7a9270 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/BindingAdapters.java @@ -0,0 +1,123 @@ +package com.wireguard.android.databinding; + +import android.databinding.BindingAdapter; +import android.databinding.ObservableList; +import android.databinding.adapters.ListenerUtil; +import android.graphics.Typeface; +import android.text.InputFilter; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import com.wireguard.android.R; +import com.wireguard.android.widgets.ToggleSwitch; + +/** + * Static methods for use by generated code in the Android data binding library. + */ + +@SuppressWarnings("unused") +public final class BindingAdapters { + @BindingAdapter({"app:checked"}) + public static void setChecked(final ToggleSwitch view, final boolean checked) { + view.setCheckedInternal(checked); + } + + @BindingAdapter({"filter"}) + public static void setFilter(final TextView view, final InputFilter filter) { + view.setFilters(new InputFilter[]{filter}); + } + + @BindingAdapter({"items", "layout"}) + public static void setItems(final LinearLayout view, + final ObservableList oldList, final int oldLayoutId, + final ObservableList newList, final int newLayoutId) { + if (oldList == newList && oldLayoutId == newLayoutId) + return; + ItemChangeListener listener = ListenerUtil.getListener(view, R.id.item_change_listener); + // If the layout changes, any existing listener must be replaced. + if (listener != null && oldList != null && oldLayoutId != newLayoutId) { + listener.setList(null); + listener = null; + // Stop tracking the old listener. + ListenerUtil.trackListener(view, null, R.id.item_change_listener); + } + // Avoid adding a listener when there is no new list or layout. + if (newList == null || newLayoutId == 0) + return; + if (listener == null) { + listener = new ItemChangeListener<>(view, newLayoutId); + ListenerUtil.trackListener(view, listener, R.id.item_change_listener); + } + // Either the list changed, or this is an entirely new listener because the layout changed. + listener.setList(newList); + } + + @BindingAdapter({"items", "layout"}) + public static void setItems(final ListView view, + final ObservableList oldList, final int oldLayoutId, + final ObservableList newList, final int newLayoutId) { + if (oldList == newList && oldLayoutId == newLayoutId) + return; + // The ListAdapter interface is not generic, so this cannot be checked. + @SuppressWarnings("unchecked") + ObservableListAdapter adapter = (ObservableListAdapter) view.getAdapter(); + // If the layout changes, any existing adapter must be replaced. + if (adapter != null && oldList != null && oldLayoutId != newLayoutId) { + adapter.setList(null); + adapter = null; + } + // Avoid setting an adapter when there is no new list or layout. + if (newList == null || newLayoutId == 0) + return; + if (adapter == null) { + adapter = new ObservableListAdapter<>(view.getContext(), newLayoutId, newList); + view.setAdapter(adapter); + } + // Either the list changed, or this is an entirely new listener because the layout changed. + adapter.setList(newList); + } + + @BindingAdapter({"items", "layout"}) + public static , V> void setItems(final ListView view, + final ObservableSortedMap oldMap, + final int oldLayoutId, + final ObservableSortedMap newMap, + final int newLayoutId) { + if (oldMap == newMap && oldLayoutId == newLayoutId) + return; + // The ListAdapter interface is not generic, so this cannot be checked. + @SuppressWarnings("unchecked") + ObservableMapAdapter adapter = (ObservableMapAdapter) view.getAdapter(); + // If the layout changes, any existing adapter must be replaced. + if (adapter != null && oldMap != null && oldLayoutId != newLayoutId) { + adapter.setMap(null); + adapter = null; + } + // Avoid setting an adapter when there is no new list or layout. + if (newMap == null || newLayoutId == 0) + return; + if (adapter == null) { + adapter = new ObservableMapAdapter<>(view.getContext(), newLayoutId, newMap); + view.setAdapter(adapter); + } + // Either the list changed, or this is an entirely new listener because the layout changed. + adapter.setMap(newMap); + } + + @BindingAdapter({"app:onBeforeCheckedChanged"}) + public static void setOnBeforeCheckedChanged(final ToggleSwitch view, + final ToggleSwitch.OnBeforeCheckedChangeListener + listener) { + view.setOnBeforeCheckedChangeListener(listener); + } + + @BindingAdapter({"android:textStyle"}) + public static void setTextStyle(final TextView view, final Typeface typeface) { + view.setTypeface(typeface); + } + + private BindingAdapters() { + // Prevent instantiation. + } +} diff --git a/app/src/main/java/com/wireguard/android/databinding/ItemChangeListener.java b/app/src/main/java/com/wireguard/android/databinding/ItemChangeListener.java new file mode 100644 index 00000000..6d8debf2 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/ItemChangeListener.java @@ -0,0 +1,128 @@ +package com.wireguard.android.databinding; + +import android.databinding.DataBindingUtil; +import android.databinding.ObservableList; +import android.databinding.ViewDataBinding; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.wireguard.android.BR; + +import java.lang.ref.WeakReference; + +/** + * Helper class for binding an ObservableList to the children of a ViewGroup. + */ + +class ItemChangeListener { + private final OnListChangedCallback callback = new OnListChangedCallback<>(this); + private final ViewGroup container; + private final int layoutId; + private final LayoutInflater layoutInflater; + private ObservableList list; + + ItemChangeListener(final ViewGroup container, final int layoutId) { + this.container = container; + this.layoutId = layoutId; + layoutInflater = LayoutInflater.from(container.getContext()); + } + + private View getView(final int position, final View convertView) { + ViewDataBinding binding = DataBindingUtil.getBinding(convertView); + if (binding == null) + binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false); + binding.setVariable(BR.item, list.get(position)); + binding.executePendingBindings(); + return binding.getRoot(); + } + + void setList(final ObservableList newList) { + if (list != null) + list.removeOnListChangedCallback(callback); + list = newList; + if (list != null) { + list.addOnListChangedCallback(callback); + callback.onChanged(list); + } else { + container.removeAllViews(); + } + } + + private static class OnListChangedCallback + extends ObservableList.OnListChangedCallback> { + + private final WeakReference> weakListener; + + private OnListChangedCallback(final ItemChangeListener listener) { + weakListener = new WeakReference<>(listener); + } + + @Override + public void onChanged(final ObservableList sender) { + final ItemChangeListener listener = weakListener.get(); + if (listener != null) { + // TODO: recycle views + listener.container.removeAllViews(); + for (int i = 0; i < sender.size(); ++i) + listener.container.addView(listener.getView(i, null)); + } else { + sender.removeOnListChangedCallback(this); + } + } + + @Override + public void onItemRangeChanged(final ObservableList sender, final int positionStart, + final int itemCount) { + final ItemChangeListener listener = weakListener.get(); + if (listener != null) { + for (int i = positionStart; i < positionStart + itemCount; ++i) { + final View child = listener.container.getChildAt(i); + listener.container.removeViewAt(i); + listener.container.addView(listener.getView(i, child)); + } + } else { + sender.removeOnListChangedCallback(this); + } + } + + @Override + public void onItemRangeInserted(final ObservableList sender, final int positionStart, + final int itemCount) { + final ItemChangeListener listener = weakListener.get(); + if (listener != null) { + for (int i = positionStart; i < positionStart + itemCount; ++i) + listener.container.addView(listener.getView(i, null)); + } else { + sender.removeOnListChangedCallback(this); + } + } + + @Override + public void onItemRangeMoved(final ObservableList sender, final int fromPosition, + final int toPosition, final int itemCount) { + final ItemChangeListener listener = weakListener.get(); + if (listener != null) { + final View[] views = new View[itemCount]; + for (int i = 0; i < itemCount; ++i) + views[i] = listener.container.getChildAt(fromPosition + i); + listener.container.removeViews(fromPosition, itemCount); + for (int i = 0; i < itemCount; ++i) + listener.container.addView(views[i], toPosition + i); + } else { + sender.removeOnListChangedCallback(this); + } + } + + @Override + public void onItemRangeRemoved(final ObservableList sender, final int positionStart, + final int itemCount) { + final ItemChangeListener listener = weakListener.get(); + if (listener != null) { + listener.container.removeViews(positionStart, itemCount); + } else { + sender.removeOnListChangedCallback(this); + } + } + } +} diff --git a/app/src/main/java/com/wireguard/android/databinding/ObservableListAdapter.java b/app/src/main/java/com/wireguard/android/databinding/ObservableListAdapter.java new file mode 100644 index 00000000..5727900d --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/ObservableListAdapter.java @@ -0,0 +1,119 @@ +package com.wireguard.android.databinding; + +import android.content.Context; +import android.databinding.DataBindingUtil; +import android.databinding.ObservableList; +import android.databinding.ViewDataBinding; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListAdapter; + +import com.wireguard.android.BR; + +import java.lang.ref.WeakReference; + +/** + * A generic ListAdapter backed by an ObservableList. + */ + +class ObservableListAdapter extends BaseAdapter implements ListAdapter { + private final OnListChangedCallback callback = new OnListChangedCallback<>(this); + private final int layoutId; + private final LayoutInflater layoutInflater; + private ObservableList list; + + ObservableListAdapter(final Context context, final int layoutId, final ObservableList list) { + this.layoutId = layoutId; + layoutInflater = LayoutInflater.from(context); + setList(list); + } + + @Override + public int getCount() { + return list != null ? list.size() : 0; + } + + @Override + public T getItem(final int position) { + if (list == null || position < 0 || position >= list.size()) + return null; + return list.get(position); + } + + @Override + public long getItemId(final int position) { + if (list == null || position < 0 || position >= list.size()) + return -1; + return list.get(position).hashCode(); + } + + @Override + public View getView(final int position, final View convertView, final ViewGroup parent) { + ViewDataBinding binding = DataBindingUtil.getBinding(convertView); + if (binding == null) + binding = DataBindingUtil.inflate(layoutInflater, layoutId, parent, false); + binding.setVariable(BR.item, getItem(position)); + binding.executePendingBindings(); + return binding.getRoot(); + } + + @Override + public boolean hasStableIds() { + return true; + } + + void setList(final ObservableList newList) { + if (list != null) + list.removeOnListChangedCallback(callback); + list = newList; + if (list != null) { + list.addOnListChangedCallback(callback); + } + notifyDataSetChanged(); + } + + private static class OnListChangedCallback + extends ObservableList.OnListChangedCallback> { + + private final WeakReference> weakAdapter; + + private OnListChangedCallback(final ObservableListAdapter adapter) { + weakAdapter = new WeakReference<>(adapter); + } + + @Override + public void onChanged(final ObservableList sender) { + final ObservableListAdapter adapter = weakAdapter.get(); + if (adapter != null) + adapter.notifyDataSetChanged(); + else + sender.removeOnListChangedCallback(this); + } + + @Override + public void onItemRangeChanged(final ObservableList sender, final int positionStart, + final int itemCount) { + onChanged(sender); + } + + @Override + public void onItemRangeInserted(final ObservableList sender, final int positionStart, + final int itemCount) { + onChanged(sender); + } + + @Override + public void onItemRangeMoved(final ObservableList sender, final int fromPosition, + final int toPosition, final int itemCount) { + onChanged(sender); + } + + @Override + public void onItemRangeRemoved(final ObservableList sender, final int positionStart, + final int itemCount) { + onChanged(sender); + } + } +} diff --git a/app/src/main/java/com/wireguard/android/databinding/ObservableMapAdapter.java b/app/src/main/java/com/wireguard/android/databinding/ObservableMapAdapter.java new file mode 100644 index 00000000..70180728 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/ObservableMapAdapter.java @@ -0,0 +1,120 @@ +package com.wireguard.android.databinding; + +import android.content.Context; +import android.databinding.DataBindingUtil; +import android.databinding.ObservableMap; +import android.databinding.ViewDataBinding; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListAdapter; + +import com.wireguard.android.BR; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; + +/** + * A generic ListAdapter backed by a TreeMap that adds observability. + */ + +public class ObservableMapAdapter, V> extends BaseAdapter + implements ListAdapter { + private final OnMapChangedCallback callback = new OnMapChangedCallback<>(this); + private ArrayList keys; + private final int layoutId; + private final LayoutInflater layoutInflater; + private ObservableSortedMap map; + + ObservableMapAdapter(final Context context, final int layoutId, + final ObservableSortedMap map) { + this.layoutId = layoutId; + layoutInflater = LayoutInflater.from(context); + setMap(map); + } + + @Override + public int getCount() { + return map != null ? map.size() : 0; + } + + @Override + public V getItem(final int position) { + if (map == null || position < 0 || position >= map.size()) + return null; + return map.get(getKey(position)); + } + + @Override + public long getItemId(final int position) { + if (map == null || position < 0 || position >= map.size()) + return -1; + return getItem(position).hashCode(); + } + + private K getKey(final int position) { + return getKeys().get(position); + } + + private ArrayList getKeys() { + if (keys == null) + keys = new ArrayList<>(map.keySet()); + return keys; + } + + public int getPosition(final K key) { + if (map == null || key == null) + return -1; + return Collections.binarySearch(getKeys(), key); + } + + @Override + public View getView(final int position, final View convertView, final ViewGroup parent) { + ViewDataBinding binding = DataBindingUtil.getBinding(convertView); + if (binding == null) + binding = DataBindingUtil.inflate(layoutInflater, layoutId, parent, false); + binding.setVariable(BR.key, getKey(position)); + binding.setVariable(BR.item, getItem(position)); + binding.executePendingBindings(); + return binding.getRoot(); + } + + @Override + public boolean hasStableIds() { + return true; + } + + void setMap(final ObservableSortedMap newMap) { + if (map != null) + map.removeOnMapChangedCallback(callback); + keys = null; + map = newMap; + if (map != null) { + map.addOnMapChangedCallback(callback); + } + notifyDataSetChanged(); + } + + private static class OnMapChangedCallback, V> + extends ObservableMap.OnMapChangedCallback, K, V> { + + private final WeakReference> weakAdapter; + + private OnMapChangedCallback(final ObservableMapAdapter adapter) { + weakAdapter = new WeakReference<>(adapter); + } + + @Override + public void onMapChanged(final ObservableSortedMap sender, final K key) { + final ObservableMapAdapter adapter = weakAdapter.get(); + if (adapter != null) { + adapter.keys = null; + adapter.notifyDataSetChanged(); + } else { + sender.removeOnMapChangedCallback(this); + } + } + } +} diff --git a/app/src/main/java/com/wireguard/android/databinding/ObservableSortedMap.java b/app/src/main/java/com/wireguard/android/databinding/ObservableSortedMap.java new file mode 100644 index 00000000..1cbfeb6b --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/ObservableSortedMap.java @@ -0,0 +1,13 @@ +package com.wireguard.android.databinding; + +import android.databinding.ObservableMap; + +import java.util.SortedMap; + +/** + * Interface for maps that are both observable and sorted. + */ + +public interface ObservableSortedMap extends ObservableMap, SortedMap { + // No additional methods. +} diff --git a/app/src/main/java/com/wireguard/android/databinding/ObservableTreeMap.java b/app/src/main/java/com/wireguard/android/databinding/ObservableTreeMap.java new file mode 100644 index 00000000..dc5f705b --- /dev/null +++ b/app/src/main/java/com/wireguard/android/databinding/ObservableTreeMap.java @@ -0,0 +1,66 @@ +package com.wireguard.android.databinding; + +import android.databinding.MapChangeRegistry; +import android.databinding.ObservableMap; +import android.support.annotation.NonNull; + +import java.util.Map; +import java.util.TreeMap; + +/** + * Observable version of a TreeMap. Only notifies for changes made through methods, not iterators or + * views. This behavior is in line with that of ObservableArrayMap. + */ + +public class ObservableTreeMap extends TreeMap implements ObservableSortedMap { + private transient MapChangeRegistry listeners; + + @Override + public void clear() { + super.clear(); + notifyChange(null); + } + + @Override + public void addOnMapChangedCallback( + final OnMapChangedCallback, K, V> listener) { + if (listeners == null) + listeners = new MapChangeRegistry(); + listeners.add(listener); + } + + private void notifyChange(final K key) { + if (listeners != null) + listeners.notifyChange(this, key); + } + + @Override + public V put(final K key, final V value) { + final V oldValue = super.put(key, value); + notifyChange(key); + return oldValue; + } + + @Override + public void putAll(@NonNull final Map map) { + super.putAll(map); + for (final K key : map.keySet()) + notifyChange(key); + } + + @Override + public V remove(final Object key) { + final V oldValue = super.remove(key); + @SuppressWarnings("unchecked") + final K k = (K) key; + notifyChange(k); + return oldValue; + } + + @Override + public void removeOnMapChangedCallback( + final OnMapChangedCallback, K, V> listener) { + if (listeners != null) + listeners.remove(listener); + } +} -- cgit v1.2.3