diff options
author | Samuel Holland <samuel@sholland.org> | 2017-11-28 20:14:47 -0600 |
---|---|---|
committer | Samuel Holland <samuel@sholland.org> | 2017-11-28 20:14:47 -0600 |
commit | 4a672fc05d4b40b2e169bdf7b6212f86c3789de0 (patch) | |
tree | 132e6a235a61b8f816223eacc9983b5a0be947d6 | |
parent | 19f008955963015abbaf0c9fd88492d0eb0a6ace (diff) |
ConfigListFragment: Use a floating action menu
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r-- | app/build.gradle | 1 | ||||
-rw-r--r-- | app/src/main/java/com/wireguard/android/ConfigActivity.java | 24 | ||||
-rw-r--r-- | app/src/main/java/com/wireguard/android/ConfigListFragment.java | 100 | ||||
-rw-r--r-- | app/src/main/res/drawable/fab_label_background.xml | 10 | ||||
-rw-r--r-- | app/src/main/res/layout/config_list_fragment.xml | 46 | ||||
-rw-r--r-- | app/src/main/res/menu/config_list.xml | 15 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 3 | ||||
-rw-r--r-- | app/src/main/res/values/styles.xml | 8 |
8 files changed, 136 insertions, 71 deletions
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"; @@ -134,17 +132,10 @@ 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 onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); + 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 onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { - inflater.inflate(R.menu.config_list, menu); + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); } @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,29 +112,36 @@ 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<String, Config> adapter = - (ObservableMapAdapter<String, Config>) listView.getAdapter(); + @SuppressWarnings("unchecked") final ObservableMapAdapter<String, Config> adapter = + (ObservableMapAdapter<String, Config>) 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 { @@ -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 @@ +<?xml version="1.0" encoding="utf-8"?> +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="4dp" /> + <padding + android:bottom="4dp" + android:left="8dp" + android:right="8dp" + android:top="4dp" /> + <solid android:color="?android:attr/colorPrimary" /> +</shape> 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>" /> </data> - <ListView - android:id="@+id/config_list" + <RelativeLayout android:layout_width="match_parent" - android:layout_height="match_parent" - android:choiceMode="singleChoice" - app:items="@{configs}" - app:layout="@{@layout/config_list_item}" /> + android:layout_height="match_parent"> + + <ListView + android:id="@+id/config_list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:choiceMode="singleChoice" + app:items="@{configs}" + app:layout="@{@layout/config_list_item}" /> + + <com.getbase.floatingactionbutton.FloatingActionsMenu + android:id="@+id/add_menu" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_marginBottom="8dp" + android:layout_marginEnd="8dp" + app:fab_labelStyle="@style/fab_label" + app:fab_labelsPosition="left"> + + <com.getbase.floatingactionbutton.FloatingActionButton + android:id="@+id/add_from_file" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:fab_icon="@drawable/ic_action_save" + app:fab_size="mini" + app:fab_title="@string/add_from_file" /> + + <com.getbase.floatingactionbutton.FloatingActionButton + android:id="@+id/add_from_scratch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:fab_icon="@drawable/ic_action_edit" + app:fab_size="mini" + app:fab_title="@string/add_from_scratch" /> + </com.getbase.floatingactionbutton.FloatingActionsMenu> + + </RelativeLayout> </layout> 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 @@ -<?xml version="1.0" encoding="utf-8"?> -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item - android:id="@+id/menu_action_add" - android:alphabeticShortcut="n" - android:icon="@drawable/ic_action_add" - android:showAsAction="always" - android:title="@string/add" /> - <item - android:id="@+id/menu_action_import" - android:alphabeticShortcut="o" - android:icon="@drawable/ic_action_open" - android:showAsAction="ifRoom" - android:title="@string/import_config" /> -</menu> 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 @@ <item quantity="one">%d configuration selected</item> <item quantity="other">%d configurations selected</item> </plurals> - <string name="add">Add empty config</string> <string name="add_activity_title">New WireGuard configuration</string> + <string name="add_from_file">Add from file</string> + <string name="add_from_scratch">Add from scratch</string> <string name="add_peer">Add peer</string> <string name="addresses">Addresses</string> <string name="allowed_ips">Allowed IPs</string> 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + + <style name="fab_label" parent="android:TextAppearance.DeviceDefault.Inverse"> + <item name="android:background">@drawable/fab_label_background</item> + </style> + +</resources> |