Convert the list of tunnels to a KeyedObservableList

Signed-off-by: Samuel Holland <samuel@sholland.org>
This commit is contained in:
Samuel Holland 2018-01-06 04:04:42 -06:00
parent c73287f64b
commit ff0bb081a0
8 changed files with 73 additions and 45 deletions

View File

@ -6,7 +6,8 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.databinding.Observable;
import android.databinding.Observable.OnPropertyChangedCallback;
import android.databinding.ObservableMap.OnMapChangedCallback;
import android.databinding.ObservableList;
import android.databinding.ObservableList.OnListChangedCallback;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.service.quicksettings.Tile;
@ -19,8 +20,8 @@ import com.wireguard.android.activity.MainActivity;
import com.wireguard.android.activity.SettingsActivity;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.model.TunnelCollection;
import com.wireguard.android.model.TunnelManager;
import com.wireguard.android.util.KeyedObservableList;
import java.util.Objects;
@ -33,9 +34,8 @@ import java.util.Objects;
@TargetApi(Build.VERSION_CODES.N)
public class QuickTileService extends TileService implements OnSharedPreferenceChangeListener {
private static final String TAG = QuickTileService.class.getSimpleName();
private final OnTunnelListChangedCallback listCallback = new OnTunnelListChangedCallback();
private final OnTunnelStateChangedCallback tunnelCallback = new OnTunnelStateChangedCallback();
private final OnTunnelMapChangedCallback tunnelMapCallback = new OnTunnelMapChangedCallback();
private SharedPreferences preferences;
private Tunnel tunnel;
private TunnelManager tunnelManager;
@ -75,7 +75,7 @@ public class QuickTileService extends TileService implements OnSharedPreferenceC
@Override
public void onStartListening() {
preferences.registerOnSharedPreferenceChangeListener(this);
tunnelManager.getTunnels().addOnMapChangedCallback(tunnelMapCallback);
tunnelManager.getTunnels().addOnListChangedCallback(listCallback);
if (tunnel != null)
tunnel.addOnPropertyChangedCallback(tunnelCallback);
updateTile();
@ -84,7 +84,7 @@ public class QuickTileService extends TileService implements OnSharedPreferenceC
@Override
public void onStopListening() {
preferences.unregisterOnSharedPreferenceChangeListener(this);
tunnelManager.getTunnels().removeOnMapChangedCallback(tunnelMapCallback);
tunnelManager.getTunnels().removeOnListChangedCallback(listCallback);
if (tunnel != null)
tunnel.removeOnPropertyChangedCallback(tunnelCallback);
}
@ -104,7 +104,7 @@ public class QuickTileService extends TileService implements OnSharedPreferenceC
final String currentName = tunnel != null ? tunnel.getName() : null;
final String newName = preferences.getString(TunnelManager.KEY_PRIMARY_TUNNEL, null);
if (!Objects.equals(currentName, newName)) {
final TunnelCollection tunnels = tunnelManager.getTunnels();
final KeyedObservableList<String, Tunnel> tunnels = tunnelManager.getTunnels();
final Tunnel newTunnel = newName != null ? tunnels.get(newName) : null;
if (tunnel != null)
tunnel.removeOnPropertyChangedCallback(tunnelCallback);
@ -134,12 +134,35 @@ public class QuickTileService extends TileService implements OnSharedPreferenceC
tile.updateTile();
}
private final class OnTunnelMapChangedCallback
extends OnMapChangedCallback<TunnelCollection, String, Tunnel> {
private final class OnTunnelListChangedCallback
extends OnListChangedCallback<ObservableList<Tunnel>> {
@Override
public void onMapChanged(final TunnelCollection sender, final String key) {
if (!key.equals(preferences.getString(TunnelManager.KEY_PRIMARY_TUNNEL, null)))
return;
public void onChanged(final ObservableList<Tunnel> sender) {
updateTile();
}
@Override
public void onItemRangeChanged(final ObservableList<Tunnel> sender,
final int positionStart, final int itemCount) {
updateTile();
}
@Override
public void onItemRangeInserted(final ObservableList<Tunnel> sender,
final int positionStart, final int itemCount) {
// Do nothing.
}
@Override
public void onItemRangeMoved(final ObservableList<Tunnel> sender,
final int fromPosition, final int toPosition,
final int itemCount) {
// Do nothing.
}
@Override
public void onItemRangeRemoved(final ObservableList<Tunnel> sender,
final int positionStart, final int itemCount) {
updateTile();
}
}

View File

@ -10,6 +10,8 @@ import android.widget.ListView;
import android.widget.TextView;
import com.wireguard.android.R;
import com.wireguard.android.util.Keyed;
import com.wireguard.android.util.KeyedObservableList;
import com.wireguard.android.widget.ToggleSwitch;
import org.threeten.bp.Instant;
@ -63,14 +65,15 @@ public final class BindingAdapters {
}
@BindingAdapter({"items", "layout"})
public static <T> void setItems(final ListView view,
final ObservableList<T> oldList, final int oldLayoutId,
final ObservableList<T> newList, final int newLayoutId) {
public static <K, E extends Keyed<? extends K>>
void setItems(final ListView view,
final KeyedObservableList<K, E> oldList, final int oldLayoutId,
final KeyedObservableList<K, E> newList, final int newLayoutId) {
if (oldList == newList && oldLayoutId == newLayoutId)
return;
// The ListAdapter interface is not generic, so this cannot be checked.
@SuppressWarnings("unchecked")
ObservableListAdapter<T> adapter = (ObservableListAdapter<T>) view.getAdapter();
@SuppressWarnings("unchecked") KeyedObservableListAdapter<K, E> adapter =
(KeyedObservableListAdapter<K, E>) view.getAdapter();
// If the layout changes, any existing adapter must be replaced.
if (adapter != null && oldList != null && oldLayoutId != newLayoutId) {
adapter.setList(null);
@ -80,7 +83,7 @@ public final class BindingAdapters {
if (newList == null || newLayoutId == 0)
return;
if (adapter == null) {
adapter = new ObservableListAdapter<>(view.getContext(), newLayoutId, newList);
adapter = new KeyedObservableListAdapter<>(view.getContext(), newLayoutId, newList);
view.setAdapter(adapter);
}
// Either the list changed, or this is an entirely new listener because the layout changed.

View File

@ -9,6 +9,7 @@ import com.wireguard.android.BR;
import com.wireguard.android.backend.Backend;
import com.wireguard.android.configStore.ConfigStore;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.android.util.Keyed;
import com.wireguard.config.Config;
import org.threeten.bp.Instant;
@ -23,7 +24,7 @@ import java9.util.concurrent.CompletionStage;
* Encapsulates the volatile and nonvolatile state of a WireGuard tunnel.
*/
public class Tunnel extends BaseObservable implements Comparable<Tunnel> {
public class Tunnel extends BaseObservable implements Keyed<String> {
public static final int NAME_MAX_LENGTH = 16;
private static final Pattern NAME_PATTERN = Pattern.compile("[a-zA-Z0-9_=+.-]{1,16}");
private static final String TAG = Tunnel.class.getSimpleName();
@ -48,11 +49,6 @@ public class Tunnel extends BaseObservable implements Comparable<Tunnel> {
return name != null && NAME_PATTERN.matcher(name).matches();
}
@Override
public int compareTo(@NonNull final Tunnel tunnel) {
return name.compareTo(tunnel.name);
}
@Bindable
public Config getConfig() {
if (config == null)
@ -66,6 +62,11 @@ public class Tunnel extends BaseObservable implements Comparable<Tunnel> {
return CompletableFuture.completedFuture(config);
}
@Override
public String getKey() {
return name;
}
@Bindable
public Instant getLastStateChange() {
return lastStateChange;

View File

@ -1,10 +0,0 @@
package com.wireguard.android.model;
import com.wireguard.android.databinding.ObservableTreeMap;
/**
* Created by samuel on 12/19/17.
*/
public class TunnelCollection extends ObservableTreeMap<String, Tunnel> {
}

View File

@ -8,6 +8,8 @@ import com.wireguard.android.backend.Backend;
import com.wireguard.android.configStore.ConfigStore;
import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.android.util.KeyedObservableList;
import com.wireguard.android.util.SortedKeyedObservableArrayList;
import com.wireguard.config.Config;
import java.util.Collections;
@ -35,7 +37,8 @@ public final class TunnelManager {
private final Backend backend;
private final ConfigStore configStore;
private final SharedPreferences preferences;
private final TunnelCollection tunnels = new TunnelCollection();
private final KeyedObservableList<String, Tunnel> tunnels =
new SortedKeyedObservableArrayList<>();
@Inject
public TunnelManager(final Backend backend, final ConfigStore configStore,
@ -47,7 +50,7 @@ public final class TunnelManager {
private Tunnel add(final String name, final Config config) {
final Tunnel tunnel = new Tunnel(backend, configStore, name, config);
tunnels.put(name, tunnel);
tunnels.add(tunnel);
return tunnel;
}
@ -71,13 +74,13 @@ public final class TunnelManager {
return backend.setState(tunnel, State.DOWN)
.thenCompose(x -> configStore.delete(tunnel.getName()))
.thenAccept(x -> {
tunnels.remove(tunnel.getName());
tunnels.remove(tunnel);
if (tunnel.getName().equals(preferences.getString(KEY_PRIMARY_TUNNEL, null)))
preferences.edit().remove(KEY_PRIMARY_TUNNEL).apply();
});
}
public TunnelCollection getTunnels() {
public KeyedObservableList<String, Tunnel> getTunnels() {
return tunnels;
}
@ -105,7 +108,7 @@ public final class TunnelManager {
}
public CompletionStage<Void> saveState() {
final Set<String> runningTunnels = StreamSupport.stream(tunnels.values())
final Set<String> runningTunnels = StreamSupport.stream(tunnels)
.filter(tunnel -> tunnel.getState() == State.UP)
.map(Tunnel::getName)
.collect(Collectors.toUnmodifiableSet());

View File

@ -5,19 +5,23 @@ import android.preference.ListPreference;
import android.util.AttributeSet;
import com.wireguard.android.Application;
import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.TunnelManager;
import java.util.Set;
import java9.util.stream.StreamSupport;
/**
* ListPreference that is automatically filled with the list of configurations.
* ListPreference that is automatically filled with the list of tunnels.
*/
public class TunnelListPreference extends ListPreference {
public TunnelListPreference(final Context context, final AttributeSet attrs,
final int defStyleAttr, final int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final Set<String> entrySet = Application.getComponent().getTunnelManager().getTunnels().keySet();
final CharSequence[] entries = entrySet.toArray(new CharSequence[entrySet.size()]);
final TunnelManager tunnelManager = Application.getComponent().getTunnelManager();
final CharSequence[] entries = StreamSupport.stream(tunnelManager.getTunnels())
.map(Tunnel::getName)
.toArray(String[]::new);
setEntries(entries);
setEntryValues(entries);
}

View File

@ -4,13 +4,15 @@
<data>
<import type="com.wireguard.android.model.Tunnel" />
<variable
name="fragment"
type="com.wireguard.android.fragment.TunnelListFragment" />
<variable
name="tunnels"
type="com.wireguard.android.model.TunnelCollection" />
type="com.wireguard.android.util.KeyedObservableList&lt;String, Tunnel&gt;" />
</data>
<com.commonsware.cwac.crossport.design.widget.CoordinatorLayout

View File

@ -4,11 +4,13 @@
<data>
<import type="com.wireguard.android.model.Tunnel" />
<import type="com.wireguard.android.model.Tunnel.State" />
<variable
name="collection"
type="com.wireguard.android.model.TunnelCollection" />
type="com.wireguard.android.util.KeyedObservableList&lt;String, Tunnel&gt;" />
<variable
name="key"