From 73217a098a264e3da8497b2c3696c8816004ca64 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 16 Aug 2017 00:55:44 -0500 Subject: [PATCH] ConfigListFragment: Implement config selection and removal Signed-off-by: Jason A. Donenfeld --- .../wireguard/android/ConfigListFragment.java | 107 ++++++++++++++++-- .../main/res/drawable/ic_action_delete.xml | 9 ++ app/src/main/res/menu/config_list_delete.xml | 9 ++ app/src/main/res/values/strings.xml | 5 + 4 files changed, 120 insertions(+), 10 deletions(-) create mode 100644 app/src/main/res/drawable/ic_action_delete.xml create mode 100644 app/src/main/res/menu/config_list_delete.xml diff --git a/app/src/main/java/com/wireguard/android/ConfigListFragment.java b/app/src/main/java/com/wireguard/android/ConfigListFragment.java index 9dbf77d9..c5c9dbf7 100644 --- a/app/src/main/java/com/wireguard/android/ConfigListFragment.java +++ b/app/src/main/java/com/wireguard/android/ConfigListFragment.java @@ -1,21 +1,31 @@ package com.wireguard.android; +import android.content.res.Resources; +import android.databinding.ObservableArrayMap; import android.os.Bundle; import android.util.Log; +import android.view.ActionMode; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ListView; import com.wireguard.android.databinding.ConfigListFragmentBinding; import com.wireguard.config.Config; +import java.util.LinkedList; +import java.util.List; + /** * Fragment containing the list of known WireGuard configurations. */ public class ConfigListFragment extends BaseConfigFragment { + private ListView listView; @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup parent, @@ -23,7 +33,9 @@ public class ConfigListFragment extends BaseConfigFragment { final ConfigListFragmentBinding binding = ConfigListFragmentBinding.inflate(inflater, parent, false); binding.setConfigs(VpnService.getInstance().getConfigs()); - final ListView listView = binding.getRoot().findViewById(R.id.config_list); + final View root = binding.getRoot(); + listView = root.findViewById(R.id.config_list); + listView.setMultiChoiceModeListener(new ConfigListModeListener()); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View view, @@ -36,18 +48,15 @@ public class ConfigListFragment extends BaseConfigFragment { @Override public boolean onItemLongClick(final AdapterView parent, final View view, final int position, final long id) { - final Config config = (Config) parent.getItemAtPosition(position); - final VpnService service = VpnService.getInstance(); - if (config == null || service == null) - return false; - if (config.isEnabled()) - service.disable(config.getName()); - else - service.enable(config.getName()); + setConfigChecked(null); + listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); + listView.setItemChecked(position, true); return true; } }); - return binding.getRoot(); + binding.executePendingBindings(); + setConfigChecked(getCurrentConfig()); + return root; } @Override @@ -57,5 +66,83 @@ public class ConfigListFragment extends BaseConfigFragment { final BaseConfigActivity activity = ((BaseConfigActivity) getActivity()); if (activity != null && activity.getCurrentConfig() != config) activity.setCurrentConfig(config); + if (listView != null) + setConfigChecked(config); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + listView = null; + } + + private void setConfigChecked(final Config config) { + if (config != null) { + final int position = VpnService.getInstance().getConfigs().indexOfKey(config.getName()); + if (position >= 0) + listView.setItemChecked(position, true); + } else { + final int position = listView.getCheckedItemPosition(); + if (position >= 0) + listView.setItemChecked(position, false); + } + } + + private class ConfigListModeListener implements AbsListView.MultiChoiceModeListener { + private final List configsToRemove = new LinkedList<>(); + + @Override + public boolean onActionItemClicked(final ActionMode mode, final MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_action_delete: + // Ensure an unmanaged config is never the current config. + if (configsToRemove.contains(getCurrentConfig())) + setCurrentConfig(null); + for (final Config config : configsToRemove) + VpnService.getInstance().remove(config.getName()); + configsToRemove.clear(); + mode.finish(); + return true; + default: + return false; + } + } + + @Override + public void onItemCheckedStateChanged(final ActionMode mode, final int position, + final long id, final boolean checked) { + if (checked) + configsToRemove.add((Config) listView.getItemAtPosition(position)); + else + configsToRemove.remove(listView.getItemAtPosition(position)); + final int count = configsToRemove.size(); + final Resources resources = listView.getContext().getResources(); + mode.setTitle(resources.getQuantityString(R.plurals.list_delete_title, count, count)); + } + + @Override + public boolean onCreateActionMode(final ActionMode mode, final Menu menu) { + mode.getMenuInflater().inflate(R.menu.config_list_delete, menu); + return true; + } + + @Override + public void onDestroyActionMode(final ActionMode mode) { + configsToRemove.clear(); + listView.post(new Runnable() { + @Override + public void run() { + listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); + // Restore the previous selection (before entering the action mode). + setConfigChecked(getCurrentConfig()); + } + }); + } + + @Override + public boolean onPrepareActionMode(final ActionMode mode, final Menu menu) { + configsToRemove.clear(); + return false; + } } } diff --git a/app/src/main/res/drawable/ic_action_delete.xml b/app/src/main/res/drawable/ic_action_delete.xml new file mode 100644 index 00000000..fe6b85f7 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/menu/config_list_delete.xml b/app/src/main/res/menu/config_list_delete.xml new file mode 100644 index 00000000..7896d522 --- /dev/null +++ b/app/src/main/res/menu/config_list_delete.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ab82e7fe..bba997e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,5 +1,9 @@ + + %d configuration selected + %d configurations selected + WireGuard Configuration name Connected @@ -19,4 +23,5 @@ Public key Save Settings + Delete