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); } } } }