diff options
author | Samuel Holland <samuel@sholland.org> | 2018-01-07 21:50:43 -0600 |
---|---|---|
committer | Samuel Holland <samuel@sholland.org> | 2018-01-07 21:50:43 -0600 |
commit | 1fd9547f6a57edb45c517b17baf62fa66af20cb6 (patch) | |
tree | a69557955b34c6f4b9ac515d6655918362262583 /app/src/main/java/com/wireguard/android/model/TunnelManager.java | |
parent | 38b2aafce8f5fcaeadd3a7e970f41345243a2b56 (diff) |
TunnelManager/QuickTileService: Remember last used tunnel
This is actually a bit simpler than having a manually-selected "primary"
tunnel, and is hopefully easier for the user.
Signed-off-by: Samuel Holland <samuel@sholland.org>
Diffstat (limited to 'app/src/main/java/com/wireguard/android/model/TunnelManager.java')
-rw-r--r-- | app/src/main/java/com/wireguard/android/model/TunnelManager.java | 100 |
1 files changed, 82 insertions, 18 deletions
diff --git a/app/src/main/java/com/wireguard/android/model/TunnelManager.java b/app/src/main/java/com/wireguard/android/model/TunnelManager.java index 8b7ef208..09c33b2d 100644 --- a/app/src/main/java/com/wireguard/android/model/TunnelManager.java +++ b/app/src/main/java/com/wireguard/android/model/TunnelManager.java @@ -1,9 +1,12 @@ package com.wireguard.android.model; import android.content.SharedPreferences; +import android.databinding.BaseObservable; +import android.databinding.Bindable; import android.support.annotation.NonNull; import com.wireguard.android.Application.ApplicationScope; +import com.wireguard.android.BR; import com.wireguard.android.backend.Backend; import com.wireguard.android.configStore.ConfigStore; import com.wireguard.android.model.Tunnel.State; @@ -14,7 +17,6 @@ import com.wireguard.android.util.ObservableKeyedList; import com.wireguard.android.util.ObservableSortedKeyedArrayList; import com.wireguard.config.Config; -import java.util.Collections; import java.util.Comparator; import java.util.Set; @@ -31,10 +33,10 @@ import java9.util.stream.StreamSupport; */ @ApplicationScope -public final class TunnelManager { - public static final String KEY_PRIMARY_TUNNEL = "primary_config"; +public final class TunnelManager extends BaseObservable { private static final Comparator<String> COMPARATOR = Comparators.<String>thenComparing( String.CASE_INSENSITIVE_ORDER, Comparators.naturalOrder()); + private static final String KEY_LAST_USED_TUNNEL = "last_used_tunnel"; private static final String KEY_RESTORE_ON_BOOT = "restore_on_boot"; private static final String KEY_RUNNING_TUNNELS = "enabled_configs"; private static final String TAG = TunnelManager.class.getSimpleName(); @@ -45,6 +47,7 @@ public final class TunnelManager { private final SharedPreferences preferences; private final ObservableKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR); + private Tunnel lastUsedTunnel; @Inject public TunnelManager(final AsyncWorker asyncWorker, final Backend backend, @@ -73,16 +76,38 @@ public final class TunnelManager { } CompletionStage<Void> delete(final Tunnel tunnel) { + final State originalState = tunnel.getState(); + final boolean wasLastUsed = tunnel == lastUsedTunnel; + // Make sure nothing touches the tunnel. + if (wasLastUsed) + setLastUsedTunnel(null); + tunnels.remove(tunnel); return asyncWorker.runAsync(() -> { - backend.setState(tunnel, State.DOWN); - configStore.delete(tunnel.getName()); - }).thenAccept(x -> { - if (tunnel.getName().equals(preferences.getString(KEY_PRIMARY_TUNNEL, null))) - preferences.edit().remove(KEY_PRIMARY_TUNNEL).apply(); - tunnels.remove(tunnel); + if (originalState == State.UP) + backend.setState(tunnel, State.DOWN); + try { + configStore.delete(tunnel.getName()); + } catch (final Exception e) { + if (originalState == State.UP) + backend.setState(tunnel, originalState); + // Re-throw the exception to fail the completion. + throw e; + } + }).whenComplete((x, e) -> { + if (e == null) + return; + // Failure, put the tunnel back. + tunnels.add(tunnel); + if (wasLastUsed) + setLastUsedTunnel(tunnel); }); } + @Bindable + public Tunnel getLastUsedTunnel() { + return lastUsedTunnel; + } + CompletionStage<Config> getTunnelConfig(final Tunnel tunnel) { final CompletionStage<Config> completion = asyncWorker.supplyAsync(() -> configStore.load(tunnel.getName())); @@ -117,6 +142,9 @@ public final class TunnelManager { private void onTunnelsLoaded(final Set<String> present, final Set<String> running) { for (final String name : present) addToList(name, null, running.contains(name) ? State.UP : State.DOWN); + final String lastUsedName = preferences.getString(KEY_LAST_USED_TUNNEL, null); + if (lastUsedName != null) + setLastUsedTunnel(tunnels.get(lastUsedName)); } CompletionStage<Tunnel> rename(final Tunnel tunnel, final String name) { @@ -127,21 +155,42 @@ public final class TunnelManager { return CompletableFuture.failedFuture(new IllegalArgumentException(message)); } final State originalState = tunnel.getState(); + final boolean wasLastUsed = tunnel == lastUsedTunnel; + // Make sure nothing touches the tunnel. + if (wasLastUsed) + setLastUsedTunnel(null); + tunnels.remove(tunnel); return asyncWorker.supplyAsync(() -> { - backend.setState(tunnel, State.DOWN); + if (originalState == State.UP) + backend.setState(tunnel, State.DOWN); final Config newConfig = configStore.create(name, tunnel.getConfig()); final Tunnel newTunnel = new Tunnel(this, name, newConfig, State.DOWN); - if (originalState == State.UP) { - backend.setState(newTunnel, originalState); - newTunnel.onStateChanged(originalState); + try { + if (originalState == State.UP) + backend.setState(newTunnel, originalState); + configStore.delete(tunnel.getName()); + } catch (final Exception e) { + // Clean up. + configStore.delete(name); + if (originalState == State.UP) + backend.setState(tunnel, originalState); + // Re-throw the exception to fail the completion. + throw e; } - configStore.delete(tunnel.getName()); return newTunnel; }).whenComplete((newTunnel, e) -> { - if (e != null) - return; - tunnels.remove(tunnel); - tunnels.add(newTunnel); + if (e == null) { + // Success, add the new tunnel. + newTunnel.onStateChanged(originalState); + tunnels.add(newTunnel); + if (wasLastUsed) + setLastUsedTunnel(newTunnel); + } else { + // Failure, put the old tunnel back. + tunnels.add(tunnel); + if (wasLastUsed) + setLastUsedTunnel(tunnel); + } }); } @@ -166,6 +215,17 @@ public final class TunnelManager { return CompletableFuture.completedFuture(null); } + private void setLastUsedTunnel(final Tunnel tunnel) { + if (tunnel == lastUsedTunnel) + return; + lastUsedTunnel = tunnel; + notifyPropertyChanged(BR.lastUsedTunnel); + if (tunnel != null) + preferences.edit().putString(KEY_LAST_USED_TUNNEL, tunnel.getName()).apply(); + else + preferences.edit().remove(KEY_LAST_USED_TUNNEL).apply(); + } + CompletionStage<Config> setTunnelConfig(final Tunnel tunnel, final Config config) { final CompletionStage<Config> completion = asyncWorker.supplyAsync(() -> { final Config appliedConfig = backend.applyConfig(tunnel, config); @@ -179,6 +239,10 @@ public final class TunnelManager { final CompletionStage<State> completion = asyncWorker.supplyAsync(() -> backend.setState(tunnel, state)); completion.thenAccept(tunnel::onStateChanged); + completion.thenAccept(newState -> { + if (newState == State.UP) + setLastUsedTunnel(tunnel); + }); return completion; } } |