TunnelDetailFragment now restores state correctly after process death

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Eric Kuck 2018-07-25 19:30:34 -05:00 committed by Jason A. Donenfeld
parent 62d8beff96
commit 9652fe99df
7 changed files with 55 additions and 32 deletions

View File

@ -13,7 +13,6 @@ import android.support.annotation.Nullable;
import com.wireguard.android.Application; import com.wireguard.android.Application;
import com.wireguard.android.model.Tunnel; import com.wireguard.android.model.Tunnel;
import com.wireguard.android.model.TunnelManager;
import java.util.Objects; import java.util.Objects;
@ -39,15 +38,17 @@ public abstract class BaseActivity extends ThemeChangeAwareActivity {
@Override @Override
protected void onCreate(@Nullable final Bundle savedInstanceState) { protected void onCreate(@Nullable final Bundle savedInstanceState) {
// Restore the saved tunnel if there is one; otherwise grab it from the arguments. // Restore the saved tunnel if there is one; otherwise grab it from the arguments.
String savedTunnelName = null; final String savedTunnelName;
if (savedInstanceState != null) if (savedInstanceState != null)
savedTunnelName = savedInstanceState.getString(KEY_SELECTED_TUNNEL); savedTunnelName = savedInstanceState.getString(KEY_SELECTED_TUNNEL);
else if (getIntent() != null) else if (getIntent() != null)
savedTunnelName = getIntent().getStringExtra(KEY_SELECTED_TUNNEL); savedTunnelName = getIntent().getStringExtra(KEY_SELECTED_TUNNEL);
if (savedTunnelName != null) { else
final TunnelManager tunnelManager = Application.getTunnelManager(); savedTunnelName = null;
selectedTunnel = tunnelManager.getTunnels().get(savedTunnelName);
} if (savedTunnelName != null)
Application.getTunnelManager().getTunnels()
.thenAccept(tunnels -> setSelectedTunnel(tunnels.get(savedTunnelName)));
// The selected tunnel must be set before the superclass method recreates fragments. // The selected tunnel must be set before the superclass method recreates fragments.
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);

View File

@ -240,10 +240,13 @@ public final class GoBackend implements Backend {
@Override @Override
public void onDestroy() { public void onDestroy() {
for (final Tunnel tunnel : Application.getTunnelManager().getTunnels()) { Application.getTunnelManager().getTunnels().thenAccept(tunnels -> {
if (tunnel != null && tunnel.getState() != State.DOWN) for (final Tunnel tunnel : tunnels) {
tunnel.setState(State.DOWN); if (tunnel != null && tunnel.getState() != State.DOWN)
} tunnel.setState(State.DOWN);
}
});
vpnService = vpnService.newIncompleteFuture(); vpnService = vpnService.newIncompleteFuture();
super.onDestroy(); super.onDestroy();
} }

View File

@ -313,7 +313,7 @@ public class TunnelListFragment extends BaseFragment {
} }
binding.setFragment(this); binding.setFragment(this);
binding.setTunnels(Application.getTunnelManager().getTunnels()); Application.getTunnelManager().getTunnels().thenAccept(binding::setTunnels);
binding.setRowConfigurationHandler((ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TunnelListItemBinding, Tunnel>) (binding, tunnel, position) -> { binding.setRowConfigurationHandler((ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TunnelListItemBinding, Tunnel>) (binding, tunnel, position) -> {
binding.setFragment(this); binding.setFragment(this);
binding.getRoot().setOnClickListener(clicked -> { binding.getRoot().setOnClickListener(clicked -> {
@ -341,25 +341,29 @@ public class TunnelListFragment extends BaseFragment {
public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.menu_action_delete: case R.id.menu_action_delete:
final Collection<Tunnel> tunnelsToDelete = new ArrayList<>(); final Iterable<Integer> copyCheckedItems = new HashSet<>(checkedItems);
for (final Integer position : checkedItems) { Application.getTunnelManager().getTunnels().thenAccept(tunnels -> {
tunnelsToDelete.add(Application.getTunnelManager().getTunnels().get(position)); final Collection<Tunnel> tunnelsToDelete = new ArrayList<>();
} for (final Integer position : copyCheckedItems)
tunnelsToDelete.add(tunnels.get(position));
final CompletableFuture[] futures = StreamSupport.stream(tunnelsToDelete) final CompletableFuture[] futures = StreamSupport.stream(tunnelsToDelete)
.map(Tunnel::delete) .map(Tunnel::delete)
.toArray(CompletableFuture[]::new); .toArray(CompletableFuture[]::new);
CompletableFuture.allOf(futures) CompletableFuture.allOf(futures)
.thenApply(x -> futures.length) .thenApply(x -> futures.length)
.whenComplete(TunnelListFragment.this::onTunnelDeletionFinished); .whenComplete(TunnelListFragment.this::onTunnelDeletionFinished);
});
checkedItems.clear(); checkedItems.clear();
mode.finish(); mode.finish();
return true; return true;
case R.id.menu_action_select_all: case R.id.menu_action_select_all:
for (int i = 0; i < Application.getTunnelManager().getTunnels().size(); ++i) { Application.getTunnelManager().getTunnels().thenAccept(tunnels -> {
setItemChecked(i, true); for (int i = 0; i < tunnels.size(); ++i) {
} setItemChecked(i, true);
}
});
return true; return true;
default: default:
return false; return false;

View File

@ -19,7 +19,6 @@ import com.wireguard.android.configStore.ConfigStore;
import com.wireguard.android.model.Tunnel.State; import com.wireguard.android.model.Tunnel.State;
import com.wireguard.android.model.Tunnel.Statistics; import com.wireguard.android.model.Tunnel.Statistics;
import com.wireguard.android.util.ExceptionLoggers; import com.wireguard.android.util.ExceptionLoggers;
import com.wireguard.android.util.ObservableKeyedList;
import com.wireguard.android.util.ObservableSortedKeyedArrayList; import com.wireguard.android.util.ObservableSortedKeyedArrayList;
import com.wireguard.android.util.ObservableSortedKeyedList; import com.wireguard.android.util.ObservableSortedKeyedList;
import com.wireguard.config.Config; import com.wireguard.config.Config;
@ -47,6 +46,7 @@ public final class TunnelManager extends BaseObservable {
private static final String KEY_RUNNING_TUNNELS = "enabled_configs"; private static final String KEY_RUNNING_TUNNELS = "enabled_configs";
private final ConfigStore configStore; private final ConfigStore configStore;
private final CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> completableTunnels = new CompletableFuture<>();
private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR); private final ObservableSortedKeyedList<String, Tunnel> tunnels = new ObservableSortedKeyedArrayList<>(COMPARATOR);
@Nullable private Tunnel lastUsedTunnel; @Nullable private Tunnel lastUsedTunnel;
private boolean haveLoaded; private boolean haveLoaded;
@ -121,8 +121,8 @@ public final class TunnelManager extends BaseObservable {
.thenApply(tunnel::onStatisticsChanged); .thenApply(tunnel::onStatisticsChanged);
} }
public ObservableKeyedList<String, Tunnel> getTunnels() { public CompletableFuture<ObservableSortedKeyedList<String, Tunnel>> getTunnels() {
return tunnels; return completableTunnels;
} }
public void onCreate() { public void onCreate() {
@ -152,6 +152,8 @@ public final class TunnelManager extends BaseObservable {
f.completeExceptionally(t); f.completeExceptionally(t);
} }
}); });
completableTunnels.complete(tunnels);
} }
public void refreshTunnelStates() { public void refreshTunnelStates() {
@ -285,10 +287,12 @@ public final class TunnelManager extends BaseObservable {
final String tunnelName = intent.getStringExtra("tunnel"); final String tunnelName = intent.getStringExtra("tunnel");
if (tunnelName == null) if (tunnelName == null)
return; return;
final Tunnel tunnel = manager.getTunnels().get(tunnelName); manager.getTunnels().thenAccept(tunnels -> {
if (tunnel == null) final Tunnel tunnel = tunnels.get(tunnelName);
return; if (tunnel == null)
manager.setTunnelState(tunnel, state); return;
manager.setTunnelState(tunnel, state);
});
} }
} }
} }

View File

@ -48,7 +48,10 @@ public class ZipExporterPreference extends Preference {
} }
private void exportZip() { private void exportZip() {
final List<Tunnel> tunnels = new ArrayList<>(Application.getTunnelManager().getTunnels()); Application.getTunnelManager().getTunnels().thenAccept(this::exportZip);
}
private void exportZip(final List<Tunnel> tunnels) {
final List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size()); final List<CompletableFuture<Config>> futureConfigs = new ArrayList<>(tunnels.size());
for (final Tunnel tunnel : tunnels) for (final Tunnel tunnel : tunnels)
futureConfigs.add(tunnel.getConfigAsync().toCompletableFuture()); futureConfigs.add(tunnel.getConfigAsync().toCompletableFuture());

View File

@ -5,6 +5,8 @@
package com.wireguard.util; package com.wireguard.util;
import android.support.annotation.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -18,8 +20,10 @@ public interface KeyedList<K, E extends Keyed<? extends K>> extends List<E> {
boolean containsKey(K key); boolean containsKey(K key);
@Nullable
E get(K key); E get(K key);
@Nullable
E getLast(K key); E getLast(K key);
int indexOfKey(K key); int indexOfKey(K key);

View File

@ -5,6 +5,8 @@
package com.wireguard.util; package com.wireguard.util;
import android.support.annotation.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Set; import java.util.Set;
@ -17,10 +19,12 @@ import java.util.Set;
public interface SortedKeyedList<K, E extends Keyed<? extends K>> extends KeyedList<K, E> { public interface SortedKeyedList<K, E extends Keyed<? extends K>> extends KeyedList<K, E> {
Comparator<? super K> comparator(); Comparator<? super K> comparator();
@Nullable
K firstKey(); K firstKey();
Set<K> keySet(); Set<K> keySet();
@Nullable
K lastKey(); K lastKey();
Collection<E> values(); Collection<E> values();