ConfigListFragment: Implement config selection and removal

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Samuel Holland 2017-08-16 00:55:44 -05:00
parent f1d97a585a
commit 73217a098a
4 changed files with 120 additions and 10 deletions

View File

@ -1,21 +1,31 @@
package com.wireguard.android; package com.wireguard.android;
import android.content.res.Resources;
import android.databinding.ObservableArrayMap;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import android.view.ActionMode;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ListView; import android.widget.ListView;
import com.wireguard.android.databinding.ConfigListFragmentBinding; import com.wireguard.android.databinding.ConfigListFragmentBinding;
import com.wireguard.config.Config; import com.wireguard.config.Config;
import java.util.LinkedList;
import java.util.List;
/** /**
* Fragment containing the list of known WireGuard configurations. * Fragment containing the list of known WireGuard configurations.
*/ */
public class ConfigListFragment extends BaseConfigFragment { public class ConfigListFragment extends BaseConfigFragment {
private ListView listView;
@Override @Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup parent, public View onCreateView(final LayoutInflater inflater, final ViewGroup parent,
@ -23,7 +33,9 @@ public class ConfigListFragment extends BaseConfigFragment {
final ConfigListFragmentBinding binding = final ConfigListFragmentBinding binding =
ConfigListFragmentBinding.inflate(inflater, parent, false); ConfigListFragmentBinding.inflate(inflater, parent, false);
binding.setConfigs(VpnService.getInstance().getConfigs()); 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() { listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(final AdapterView<?> parent, final View view, public void onItemClick(final AdapterView<?> parent, final View view,
@ -36,18 +48,15 @@ public class ConfigListFragment extends BaseConfigFragment {
@Override @Override
public boolean onItemLongClick(final AdapterView<?> parent, final View view, public boolean onItemLongClick(final AdapterView<?> parent, final View view,
final int position, final long id) { final int position, final long id) {
final Config config = (Config) parent.getItemAtPosition(position); setConfigChecked(null);
final VpnService service = VpnService.getInstance(); listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL);
if (config == null || service == null) listView.setItemChecked(position, true);
return false;
if (config.isEnabled())
service.disable(config.getName());
else
service.enable(config.getName());
return true; return true;
} }
}); });
return binding.getRoot(); binding.executePendingBindings();
setConfigChecked(getCurrentConfig());
return root;
} }
@Override @Override
@ -57,5 +66,83 @@ public class ConfigListFragment extends BaseConfigFragment {
final BaseConfigActivity activity = ((BaseConfigActivity) getActivity()); final BaseConfigActivity activity = ((BaseConfigActivity) getActivity());
if (activity != null && activity.getCurrentConfig() != config) if (activity != null && activity.getCurrentConfig() != config)
activity.setCurrentConfig(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;
}
} }
} }

View File

@ -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>

View File

@ -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>

View File

@ -1,5 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <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="app_name">WireGuard</string>
<string name="config_name">Configuration name</string> <string name="config_name">Configuration name</string>
<string name="connected">Connected</string> <string name="connected">Connected</string>
@ -19,4 +23,5 @@
<string name="public_key">Public key</string> <string name="public_key">Public key</string>
<string name="save">Save</string> <string name="save">Save</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="delete">Delete</string>
</resources> </resources>