diff options
-rw-r--r-- | app/src/main/java/com/wireguard/android/ConfigListFragment.java | 107 | ||||
-rw-r--r-- | app/src/main/res/drawable/ic_action_delete.xml | 9 | ||||
-rw-r--r-- | app/src/main/res/menu/config_list_delete.xml | 9 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 5 |
4 files changed, 120 insertions, 10 deletions
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<Config> 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 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48dp" + android:height="48dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" + android:fillColor="#FFFFFF"/> +</vector> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/menu_action_delete" + android:alphabeticShortcut="d" + android:icon="@drawable/ic_action_delete" + android:showAsAction="always" + android:title="@string/delete" /> +</menu> 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 @@ <?xml version="1.0" encoding="utf-8"?> <resources> + <plurals name="list_delete_title"> + <item quantity="one">%d configuration selected</item> + <item quantity="other">%d configurations selected</item> + </plurals> <string name="app_name">WireGuard</string> <string name="config_name">Configuration name</string> <string name="connected">Connected</string> @@ -19,4 +23,5 @@ <string name="public_key">Public key</string> <string name="save">Save</string> <string name="settings">Settings</string> + <string name="delete">Delete</string> </resources> |