diff options
author | Matthew Wong <wongma@protonmail.ch> | 2015-08-13 15:18:47 -0400 |
---|---|---|
committer | Matthew Wong <wongma@protonmail.ch> | 2015-08-14 17:36:52 -0400 |
commit | ec07e1eea69a69e9579ddfb44fdbee3535bcf6ad (patch) | |
tree | 216b48cfe0460064b9a42bde130ea3e13a3a07d5 | |
parent | 29b92f4a6d1dacac3e0a1154972e2659b3a536be (diff) |
Preference activity overhaul
9 files changed, 108 insertions, 225 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java index ea4b0a2f..c2a30ffe 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java @@ -3,7 +3,7 @@ package com.zeapo.pwdstore.autofill; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; -import android.preference.PreferenceManager; +import android.content.pm.PackageManager; import android.support.v7.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; @@ -40,15 +40,20 @@ public class AutofillFragment extends DialogFragment { String appName = getArguments().getString("appName"); builder.setTitle(appName); + try { + // since we can't (easily?) pass the drawable as an argument + builder.setIcon(callingActivity.getPackageManager().getApplicationIcon(packageName)); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } - // when an app is added for the first time, the radio button selection should reflect - // the autofill_default setting: hence, defValue - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(callingActivity); - String defValue = settings.getBoolean("autofill_default", true) ? "first" : "never"; SharedPreferences prefs = getActivity().getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); - String preference = prefs.getString(packageName, defValue); + String preference = prefs.getString(packageName, ""); switch (preference) { + case "": + ((RadioButton) view.findViewById(R.id.use_default)).toggle(); + break; case "first": ((RadioButton) view.findViewById(R.id.first)).toggle(); break; @@ -77,6 +82,9 @@ public class AutofillFragment extends DialogFragment { public void onClick(DialogInterface dialog, int which) { RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.autofill_radiogroup); switch (radioGroup.getCheckedRadioButtonId()) { + case R.id.use_default: + editor.remove(packageName); + break; case R.id.first: editor.putString(packageName, "first"); break; @@ -90,10 +98,10 @@ public class AutofillFragment extends DialogFragment { } editor.apply(); int position = getArguments().getInt("position"); - if (position == -1) { - callingActivity.recyclerAdapter.add(packageName); - } else { - callingActivity.recyclerAdapter.notifyItemChanged(position); + callingActivity.recyclerAdapter.notifyItemChanged(position); + + if (getArguments().getBoolean("finish")) { + callingActivity.finish(); } } }); @@ -105,7 +113,8 @@ public class AutofillFragment extends DialogFragment { public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { ((EditText) getDialog().findViewById(R.id.matched)).setText(data.getStringExtra("path")); + } else { + ((RadioButton) getDialog().findViewById(R.id.use_default)).toggle(); } - } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java index abf1f320..91131956 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java @@ -1,22 +1,17 @@ package com.zeapo.pwdstore.autofill; import android.app.DialogFragment; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; +import android.content.Intent; import android.content.pm.PackageManager; -import android.database.Cursor; -import android.database.MatrixCursor; +import android.content.pm.ResolveInfo; import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.SearchView; -import android.widget.SimpleCursorAdapter; -import android.widget.TextView; +import android.support.v7.widget.SearchView; +import android.view.Menu; +import android.view.MenuItem; import com.zeapo.pwdstore.R; @@ -24,7 +19,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.Map; public class AutofillPreferenceActivity extends AppCompatActivity { @@ -43,102 +37,62 @@ public class AutofillPreferenceActivity extends AppCompatActivity { layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); - - // apps for which the user has custom settings should be in the recycler + + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + List<ResolveInfo> allApps = getPackageManager().queryIntentActivities(intent, 0); final PackageManager pm = getPackageManager(); - SharedPreferences prefs - = getSharedPreferences("autofill", Context.MODE_PRIVATE); - Map<String, ?> prefApps = prefs.getAll(); - ArrayList<ApplicationInfo> apps = new ArrayList<>(); - for (String packageName : prefApps.keySet()) { - try { - apps.add(pm.getApplicationInfo(packageName, 0)); - } catch (PackageManager.NameNotFoundException e) { - // remove invalid entries (from uninstalled apps?) - SharedPreferences.Editor editor = prefs.edit(); - editor.remove(packageName).apply(); - } - } - Collections.sort(apps, new Comparator<ApplicationInfo>() { + Collections.sort(allApps, new Comparator<ResolveInfo>() { @Override - public int compare(ApplicationInfo lhs, ApplicationInfo rhs) { + public int compare(ResolveInfo lhs, ResolveInfo rhs) { return lhs.loadLabel(pm).toString().compareTo(rhs.loadLabel(pm).toString()); } }); - recyclerAdapter = new AutofillRecyclerAdapter(apps, pm, this); + recyclerAdapter = new AutofillRecyclerAdapter(new ArrayList<>(allApps), pm, this); recyclerView.setAdapter(recyclerAdapter); - // show the search bar by default but don't open the keyboard - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - final SearchView searchView = (SearchView) findViewById(R.id.app_search); - searchView.clearFocus(); + setTitle("Autofill Apps"); - // create search suggestions of apps with icons & names - final SimpleCursorAdapter.ViewBinder viewBinder = new SimpleCursorAdapter.ViewBinder() { - @Override - public boolean setViewValue(View view, Cursor cursor, int columnIndex) { - if (view instanceof TextView) { - ((TextView) view).setText(cursor.getString(columnIndex)); - } else if (view instanceof ImageView) { - try { - ((ImageView) view).setImageDrawable(pm.getApplicationIcon(cursor.getString(columnIndex))); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - return false; - } - } - return true; - } - }; + Bundle extras = getIntent().getExtras(); + if (extras != null) { + recyclerView.scrollToPosition(recyclerAdapter.getPosition(extras.getString("packageName"))); + showDialog(extras.getString("packageName"), extras.getString("appName")); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.autofill_preference, menu); + MenuItem searchItem = menu.findItem(R.id.action_search); + SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); - final List<ApplicationInfo> allApps = pm.getInstalledApplications(0); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override - public boolean onQueryTextSubmit(String query) { - return false; + public boolean onQueryTextSubmit(String s) { + return true; } @Override - public boolean onQueryTextChange(String newText) { - // should be a better/faster way to do this? - // TODO do this async probably. it lags. - MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_id", "package", "label"}); - for (ApplicationInfo applicationInfo : allApps) { - if (applicationInfo.loadLabel(pm).toString().toLowerCase().contains(newText.toLowerCase())) { - matrixCursor.addRow(new Object[]{0, applicationInfo.packageName, applicationInfo.loadLabel(pm)}); - } - } - SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(AutofillPreferenceActivity.this - , R.layout.app_list_item, matrixCursor, new String[]{"package", "label"} - , new int[]{android.R.id.icon1, android.R.id.text1}, 0); - simpleCursorAdapter.setViewBinder(viewBinder); - searchView.setSuggestionsAdapter(simpleCursorAdapter); - return false; + public boolean onQueryTextChange(String s) { + return true; } }); - searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { + // When using the support library, the setOnActionExpandListener() method is + // static and accepts the MenuItem object as an argument + MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() { @Override - public boolean onSuggestionSelect(int position) { - return false; + public boolean onMenuItemActionCollapse(MenuItem item) { + return true; } @Override - public boolean onSuggestionClick(int position) { - Cursor cursor = searchView.getSuggestionsAdapter().getCursor(); - String packageName = cursor.getString(1); - String appName = cursor.getString(2); - showDialog(packageName, appName); + public boolean onMenuItemActionExpand(MenuItem item) { return true; } }); - - setTitle("Autofill Apps"); - - Bundle extras = getIntent().getExtras(); - if (extras != null) { - showDialog(extras.getString("packageName"), extras.getString("appName")); - } + return super.onCreateOptionsMenu(menu); } public void showDialog(String packageName, String appName) { diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java index 14530879..7c735f18 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java @@ -2,14 +2,11 @@ package com.zeapo.pwdstore.autofill; import android.content.Context; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.preference.PreferenceManager; +import android.content.pm.ResolveInfo; import android.support.v7.view.ActionMode; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; @@ -18,19 +15,14 @@ import android.widget.TextView; import com.zeapo.pwdstore.R; import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.Set; -import java.util.TreeSet; public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder> { - private ArrayList<ApplicationInfo> apps; + private ArrayList<ResolveInfo> apps; private PackageManager pm; private AutofillPreferenceActivity activity; - private final Set<Integer> selectedItems; private ActionMode actionMode; - public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public View view; public TextView name; public TextView secondary; @@ -44,38 +36,19 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl secondary = (TextView) view.findViewById(R.id.secondary_text); icon = (ImageView) view.findViewById(R.id.app_icon); view.setOnClickListener(this); - view.setOnLongClickListener(this); } @Override public void onClick(View v) { - if (actionMode != null) { - toggleSelection(getAdapterPosition(), view); - if (selectedItems.isEmpty()) { - actionMode.finish(); - } - } else { - activity.showDialog(packageName, name.getText().toString()); - } + activity.showDialog(packageName, name.getText().toString()); } - @Override - public boolean onLongClick(View v) { - if (actionMode != null) { - return false; - } - toggleSelection(getAdapterPosition(), view); - // Start the CAB using the ActionMode.Callback - actionMode = activity.startSupportActionMode(actionModeCallback); - return true; - } } - public AutofillRecyclerAdapter(ArrayList<ApplicationInfo> apps, PackageManager pm, AutofillPreferenceActivity activity) { + public AutofillRecyclerAdapter(ArrayList<ResolveInfo> apps, PackageManager pm, AutofillPreferenceActivity activity) { this.apps = apps; this.pm = pm; this.activity = activity; - this.selectedItems = new TreeSet<>(Collections.reverseOrder()); } @Override @@ -87,30 +60,33 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl @Override public void onBindViewHolder(AutofillRecyclerAdapter.ViewHolder holder, int position) { - ApplicationInfo app = apps.get(position); - holder.name.setText(pm.getApplicationLabel(app)); + ResolveInfo app = apps.get(position); + holder.name.setText(app.loadLabel(pm)); + holder.icon.setImageDrawable(app.loadIcon(pm)); + holder.packageName = app.activityInfo.packageName; + + holder.secondary.setVisibility(View.VISIBLE); + holder.view.setBackgroundResource(R.color.grey_white_1000); - // it shouldn't be possible for prefs.getString to not find the app...use defValue anyway - SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(activity); - String defValue = settings.getBoolean("autofill_default", true) ? "first" : "never"; SharedPreferences prefs = activity.getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); - String preference = prefs.getString(app.packageName, defValue); + String preference = prefs.getString(holder.packageName, ""); switch (preference) { + case "": + holder.secondary.setVisibility(View.GONE); + // "android:windowBackground" + holder.view.setBackgroundResource(R.color.indigo_50); + break; case "first": - holder.secondary.setText("Automatically match with password"); + holder.secondary.setText("Automatically match"); break; case "never": - holder.secondary.setText("Never autofill"); + holder.secondary.setText("Never match"); break; default: holder.secondary.setText("Match with " + preference); break; } - holder.icon.setImageDrawable(pm.getApplicationIcon(app)); - holder.packageName = app.packageName; - - holder.view.setSelected(selectedItems.contains(position)); } @Override @@ -118,79 +94,13 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl return apps.size(); } - public void add(String packageName) { - try { - ApplicationInfo app = pm.getApplicationInfo(packageName, 0); - this.apps.add(app); - notifyItemInserted(apps.size()); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - } - public int getPosition(String packageName) { for (int i = 0; i < apps.size(); i++) { - if (apps.get(i).packageName.equals(packageName)) { + if (apps.get(i).activityInfo.packageName.equals(packageName)) { return i; } } return -1; } - private ActionMode.Callback actionModeCallback = new ActionMode.Callback() { - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - mode.getMenuInflater().inflate(R.menu.context_pass, menu); - return true; - } - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - return false; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_delete_password: - // don't ask for confirmation - for (int position : selectedItems) { - remove(position); - } - mode.finish(); // Action picked, so close the CAB - return true; - default: - return false; - } - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - for (Iterator it = selectedItems.iterator(); it.hasNext();) { - // need the setSelected line in onBind - notifyItemChanged((Integer) it.next()); - it.remove(); - } - actionMode = null; - } - }; - - public void remove(int position) { - ApplicationInfo applicationInfo = this.apps.get(position); - SharedPreferences prefs - = activity.getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); - prefs.edit().remove(applicationInfo.packageName).apply(); - - this.apps.remove(position); - this.notifyItemRemoved(position); - } - - public void toggleSelection(int position, View view) { - if (!selectedItems.remove(position)) { - selectedItems.add(position); - view.setSelected(true); - } else { - view.setSelected(false); - } - } } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java index bb3b17f2..1b8ff740 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillService.java @@ -156,8 +156,9 @@ public class AutofillService extends AccessibilityService { builder.setNeutralButton("Settings", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + // the user will have to return to the app themselves. Intent intent = new Intent(AutofillService.this, AutofillPreferenceActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName", info.getPackageName()); intent.putExtra("appName", appName); startActivity(intent); diff --git a/app/src/main/res/layout/autofill_recycler_view.xml b/app/src/main/res/layout/autofill_recycler_view.xml index 836cbcf5..27afa77d 100644 --- a/app/src/main/res/layout/autofill_recycler_view.xml +++ b/app/src/main/res/layout/autofill_recycler_view.xml @@ -2,16 +2,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent" - android:focusable="true" - android:focusableInTouchMode="true"> - - <SearchView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/app_search" - android:queryHint="Add an app to change its setting" - android:iconifiedByDefault="false"/> + android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/autofill_recycler" diff --git a/app/src/main/res/layout/autofill_row_layout.xml b/app/src/main/res/layout/autofill_row_layout.xml index 354f9181..190c651f 100644 --- a/app/src/main/res/layout/autofill_row_layout.xml +++ b/app/src/main/res/layout/autofill_row_layout.xml @@ -31,15 +31,14 @@ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="App" android:id="@+id/app_name"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" - android:text="The matched file.gpg/Don't ask" - android:id="@+id/secondary_text"/> + android:id="@+id/secondary_text" + android:textColor="@color/grey_600"/> </LinearLayout> diff --git a/app/src/main/res/layout/fragment_autofill.xml b/app/src/main/res/layout/fragment_autofill.xml index 6ad44a0d..e5534f41 100644 --- a/app/src/main/res/layout/fragment_autofill.xml +++ b/app/src/main/res/layout/fragment_autofill.xml @@ -13,11 +13,18 @@ android:layout_height="wrap_content" android:id="@+id/autofill_radiogroup" > + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Use default setting" + android:id="@+id/use_default" + android:layout_gravity="center_vertical" + android:checked="false"/> <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Automatically match with password" + android:text="Automatically match" android:id="@+id/first" android:layout_gravity="center_vertical" android:checked="false"/> @@ -29,7 +36,7 @@ android:id="@+id/match" android:layout_gravity="center_vertical" android:checked="false" - android:layout_marginTop="8dp"/> + /> <EditText android:layout_width="match_parent" @@ -41,11 +48,11 @@ <RadioButton android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="Never autofill" + android:text="Never match" android:id="@+id/never" android:layout_gravity="center_vertical" android:checked="false" - android:layout_marginTop="8dp"/> + /> </RadioGroup> diff --git a/app/src/main/res/menu/autofill_preference.xml b/app/src/main/res/menu/autofill_preference.xml new file mode 100644 index 00000000..77ce95f4 --- /dev/null +++ b/app/src/main/res/menu/autofill_preference.xml @@ -0,0 +1,12 @@ +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:pwstore="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:context=".pwdstore.autofill.AutofillPreferenceActivity"> + <item + android:id="@+id/action_search" + android:icon="@drawable/ic_action_search" + android:title="@string/action_search" + pwstore:actionViewClass="android.support.v7.widget.SearchView" + pwstore:showAsAction="ifRoom|collapseActionView"/> + +</menu>
\ No newline at end of file diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml index 44e38916..e8ba80cc 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -79,7 +79,7 @@ <CheckBoxPreference android:defaultValue="true" android:key="autofill_default" - android:summary="Default to 'Automatically match with password' for apps without custom settings." + android:summary="Default to 'Automatically match' for apps without custom settings. Otherwise, 'Never match.'" android:title="Automatically match by default"/> </PreferenceCategory> |