From 4a672fc05d4b40b2e169bdf7b6212f86c3789de0 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 28 Nov 2017 20:14:47 -0600 Subject: [PATCH] ConfigListFragment: Use a floating action menu Signed-off-by: Jason A. Donenfeld --- app/build.gradle | 1 + .../com/wireguard/android/ConfigActivity.java | 24 +---- .../wireguard/android/ConfigListFragment.java | 102 +++++++++++++----- .../res/drawable/fab_label_background.xml | 10 ++ .../main/res/layout/config_list_fragment.xml | 46 ++++++-- app/src/main/res/menu/config_list.xml | 15 --- app/src/main/res/values/strings.xml | 3 +- app/src/main/res/values/styles.xml | 8 ++ 8 files changed, 137 insertions(+), 72 deletions(-) create mode 100644 app/src/main/res/drawable/fab_label_background.xml delete mode 100644 app/src/main/res/menu/config_list.xml create mode 100644 app/src/main/res/values/styles.xml diff --git a/app/build.gradle b/app/build.gradle index 11f9edaf..ed84813b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,5 +22,6 @@ android { } dependencies { + implementation 'com.getbase:floatingactionbutton:1.10.1' implementation fileTree(dir: 'libs', include: ['*.jar']) } diff --git a/app/src/main/java/com/wireguard/android/ConfigActivity.java b/app/src/main/java/com/wireguard/android/ConfigActivity.java index 24284eb1..b92b34e3 100644 --- a/app/src/main/java/com/wireguard/android/ConfigActivity.java +++ b/app/src/main/java/com/wireguard/android/ConfigActivity.java @@ -9,7 +9,6 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; -import com.wireguard.android.backends.VpnService; import com.wireguard.config.Config; /** @@ -18,7 +17,6 @@ import com.wireguard.config.Config; public class ConfigActivity extends BaseConfigActivity { private static final String KEY_EDITOR_STATE = "editorState"; - private static final int REQUEST_IMPORT = 1; private static final String TAG_DETAIL = "detail"; private static final String TAG_EDIT = "edit"; private static final String TAG_LIST = "list"; @@ -133,18 +131,11 @@ public class ConfigActivity extends BaseConfigActivity { } } - @Override - public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { - if (requestCode == REQUEST_IMPORT) { - if (resultCode == RESULT_OK) - VpnService.getInstance().importFrom(data.getData()); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - @Override public void onBackPressed() { + final ConfigListFragment listFragment = (ConfigListFragment) fragments.get(TAG_LIST); + if (listFragment.isVisible() && listFragment.tryCollapseMenu()) + return; super.onBackPressed(); // The visible fragment is now the one that was on top of the back stack, if there was one. if (isEditing()) @@ -200,19 +191,10 @@ public class ConfigActivity extends BaseConfigActivity { // The back arrow in the action bar should act the same as the back button. onBackPressed(); return true; - case R.id.menu_action_add: - startActivity(new Intent(this, AddActivity.class)); - return true; case R.id.menu_action_edit: // Try to make the editing fragment visible. setIsEditing(true); return true; - case R.id.menu_action_import: - final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - startActivityForResult(intent, REQUEST_IMPORT); - return true; case R.id.menu_action_save: // This menu item is handled by the editing fragment. return false; diff --git a/app/src/main/java/com/wireguard/android/ConfigListFragment.java b/app/src/main/java/com/wireguard/android/ConfigListFragment.java index 1067dc73..4b0432aa 100644 --- a/app/src/main/java/com/wireguard/android/ConfigListFragment.java +++ b/app/src/main/java/com/wireguard/android/ConfigListFragment.java @@ -1,12 +1,15 @@ package com.wireguard.android; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; @@ -26,28 +29,49 @@ import java.util.List; */ public class ConfigListFragment extends BaseConfigFragment { - private ListView listView; + private static final int REQUEST_IMPORT = 1; + + private ConfigListFragmentBinding binding; + + @Override + public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { + if (requestCode == REQUEST_IMPORT) { + if (resultCode == Activity.RESULT_OK) + VpnService.getInstance().importFrom(data.getData()); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - } - - @Override - public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - inflater.inflate(R.menu.config_list, menu); } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup parent, final Bundle savedInstanceState) { - final ConfigListFragmentBinding binding = - ConfigListFragmentBinding.inflate(inflater, parent, false); + binding = ConfigListFragmentBinding.inflate(inflater, parent, false); binding.setConfigs(VpnService.getInstance().getConfigs()); - listView = binding.configList; - listView.setMultiChoiceModeListener(new ConfigListModeListener()); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + binding.addFromFile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View view) { + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + startActivityForResult(intent, REQUEST_IMPORT); + binding.addMenu.collapse(); + } + }); + binding.addFromScratch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View view) { + startActivity(new Intent(getActivity(), AddActivity.class)); + binding.addMenu.collapse(); + } + }); + binding.configList.setMultiChoiceModeListener(new ConfigListModeListener()); + binding.configList.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(final AdapterView parent, final View view, final int position, final long id) { @@ -55,16 +79,29 @@ public class ConfigListFragment extends BaseConfigFragment { setCurrentConfig(config); } }); - listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { + binding.configList.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(final AdapterView parent, final View view, final int position, final long id) { setConfigChecked(null); - listView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); - listView.setItemChecked(position, true); + binding.configList.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); + binding.configList.setItemChecked(position, true); return true; } }); + binding.configList.setOnTouchListener(new View.OnTouchListener() { + @Override + @SuppressLint("ClickableViewAccessibility") + public boolean onTouch(final View view, final MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN && + binding.configList.pointToPosition((int) event.getX(), (int) event.getY()) + == ListView.INVALID_POSITION) { + binding.addMenu.collapse(); + return true; + } + return false; + } + }); binding.executePendingBindings(); setConfigChecked(getCurrentConfig()); return binding.getRoot(); @@ -75,31 +112,38 @@ public class ConfigListFragment extends BaseConfigFragment { final BaseConfigActivity activity = ((BaseConfigActivity) getActivity()); if (activity != null) activity.setCurrentConfig(config); - if (listView != null) + if (binding != null) setConfigChecked(config); } @Override public void onDestroyView() { super.onDestroyView(); - listView = null; + binding = null; } private void setConfigChecked(final Config config) { if (config != null) { - @SuppressWarnings("unchecked") - final ObservableMapAdapter adapter = - (ObservableMapAdapter) listView.getAdapter(); + @SuppressWarnings("unchecked") final ObservableMapAdapter adapter = + (ObservableMapAdapter) binding.configList.getAdapter(); final int position = adapter.getPosition(config.getName()); if (position >= 0) - listView.setItemChecked(position, true); + binding.configList.setItemChecked(position, true); } else { - final int position = listView.getCheckedItemPosition(); + final int position = binding.configList.getCheckedItemPosition(); if (position >= 0) - listView.setItemChecked(position, false); + binding.configList.setItemChecked(position, false); } } + public boolean tryCollapseMenu() { + if (binding != null && binding.addMenu.isExpanded()) { + binding.addMenu.collapse(); + return true; + } + return false; + } + private class ConfigListModeListener implements AbsListView.MultiChoiceModeListener { private final List configsToRemove = new LinkedList<>(); @@ -124,11 +168,11 @@ public class ConfigListFragment extends BaseConfigFragment { public void onItemCheckedStateChanged(final ActionMode mode, final int position, final long id, final boolean checked) { if (checked) - configsToRemove.add((Config) listView.getItemAtPosition(position)); + configsToRemove.add((Config) binding.configList.getItemAtPosition(position)); else - configsToRemove.remove(listView.getItemAtPosition(position)); + configsToRemove.remove(binding.configList.getItemAtPosition(position)); final int count = configsToRemove.size(); - final Resources resources = listView.getContext().getResources(); + final Resources resources = binding.getRoot().getContext().getResources(); mode.setTitle(resources.getQuantityString(R.plurals.list_delete_title, count, count)); } @@ -141,10 +185,10 @@ public class ConfigListFragment extends BaseConfigFragment { @Override public void onDestroyActionMode(final ActionMode mode) { configsToRemove.clear(); - listView.post(new Runnable() { + binding.configList.post(new Runnable() { @Override public void run() { - listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); + binding.configList.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE); // Restore the previous selection (before entering the action mode). setConfigChecked(getCurrentConfig()); } diff --git a/app/src/main/res/drawable/fab_label_background.xml b/app/src/main/res/drawable/fab_label_background.xml new file mode 100644 index 00000000..c0315fd6 --- /dev/null +++ b/app/src/main/res/drawable/fab_label_background.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/app/src/main/res/layout/config_list_fragment.xml b/app/src/main/res/layout/config_list_fragment.xml index 7e8304e6..b3c48175 100644 --- a/app/src/main/res/layout/config_list_fragment.xml +++ b/app/src/main/res/layout/config_list_fragment.xml @@ -10,11 +10,45 @@ type="com.wireguard.android.bindings.ObservableSortedMap<String, com.wireguard.config.Config>" /> - + android:layout_height="match_parent"> + + + + + + + + + + + diff --git a/app/src/main/res/menu/config_list.xml b/app/src/main/res/menu/config_list.xml deleted file mode 100644 index 0e94e7ff..00000000 --- a/app/src/main/res/menu/config_list.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4f548672..4687ec49 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,8 +4,9 @@ %d configuration selected %d configurations selected - Add empty config New WireGuard configuration + Add from file + Add from scratch Add peer Addresses Allowed IPs diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..11f3e544 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + +