From 2d7c37d379ae48fa3ff464032ad4cb1759b1dc31 Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Fri, 31 Jul 2015 09:00:06 -0400 Subject: Add per-app settings page --- app/src/main/res/xml/preference.xml | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'app/src/main/res/xml/preference.xml') diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml index e4071c96..3a8c4fdf 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -70,6 +70,14 @@ android:summary="@string/pref_recursive_filter_hint" android:title="@string/pref_recursive_filter" /> + + + + + Date: Sun, 9 Aug 2015 16:04:09 -0400 Subject: Autofill per-app settings dialog opens on click on suggestion or on existing app --- .../zeapo/pwdstore/autofill/AutofillActivity.java | 49 +++++++++++-- .../zeapo/pwdstore/autofill/AutofillFragment.java | 82 ++++++++++++++++++++++ .../pwdstore/autofill/AutofillRecyclerAdapter.java | 53 +++++++++++++- .../zeapo/pwdstore/autofill/AutofillService.java | 15 +++- app/src/main/res/layout/autofill_recycler_view.xml | 6 +- app/src/main/res/layout/fragment_autofill.xml | 52 ++++++++++++++ app/src/main/res/xml/preference.xml | 2 +- 7 files changed, 245 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java create mode 100644 app/src/main/res/layout/fragment_autofill.xml (limited to 'app/src/main/res/xml/preference.xml') diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java index 5c42ab8d..77a44c7a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillActivity.java @@ -1,5 +1,6 @@ package com.zeapo.pwdstore.autofill; +import android.app.DialogFragment; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; @@ -33,7 +34,7 @@ public class AutofillActivity extends AppCompatActivity { private boolean bound = false; private RecyclerView recyclerView; - private AutofillRecyclerAdapter recyclerAdapter; + AutofillRecyclerAdapter recyclerAdapter; // let fragment have access private RecyclerView.LayoutManager layoutManager; @Override @@ -41,6 +42,7 @@ public class AutofillActivity extends AppCompatActivity { super.onCreate(savedInstanceState); Intent intent = getIntent(); Bundle extras = intent.getExtras(); + // if called by service just for startIntentSenderForResult if (extras != null) { try { @@ -64,21 +66,26 @@ public class AutofillActivity extends AppCompatActivity { // apps for which the user has custom settings should be in the recycler final PackageManager pm = getPackageManager(); - final List allApps = pm.getInstalledApplications(0); SharedPreferences prefs = getSharedPreferences("autofill", Context.MODE_PRIVATE); Map prefApps = prefs.getAll(); ArrayList apps = new ArrayList<>(); - for (ApplicationInfo applicationInfo : allApps) { - if (prefApps.containsKey(applicationInfo.packageName)) { - apps.add(applicationInfo); + 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(); } } - recyclerAdapter = new AutofillRecyclerAdapter(apps, pm); + recyclerAdapter = new AutofillRecyclerAdapter(apps, 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(); // create search suggestions of apps with icons & names final SimpleCursorAdapter.ViewBinder viewBinder = new SimpleCursorAdapter.ViewBinder() { @@ -97,6 +104,8 @@ public class AutofillActivity extends AppCompatActivity { return true; } }; + + final List allApps = pm.getInstalledApplications(0); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String query) { @@ -108,7 +117,9 @@ public class AutofillActivity extends AppCompatActivity { // should be a better/faster way to do this? MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_id", "package", "label"}); for (ApplicationInfo applicationInfo : allApps) { - if (applicationInfo.loadLabel(pm).toString().toLowerCase().contains(newText.toLowerCase())) { + // exclude apps that already have settings; the search is just for adding + if (applicationInfo.loadLabel(pm).toString().toLowerCase().contains(newText.toLowerCase()) + && !recyclerAdapter.contains(applicationInfo.packageName)) { matrixCursor.addRow(new Object[]{0, applicationInfo.packageName, applicationInfo.loadLabel(pm)}); } } @@ -121,6 +132,30 @@ public class AutofillActivity extends AppCompatActivity { } }); + searchView.setOnSuggestionListener(new SearchView.OnSuggestionListener() { + @Override + public boolean onSuggestionSelect(int position) { + return false; + } + + @Override + public boolean onSuggestionClick(int position) { + Cursor cursor = searchView.getSuggestionsAdapter().getCursor(); + String packageName = cursor.getString(1); + String appName = cursor.getString(2); + + // similar to what happens in ViewHolder.onClick but position -1 + DialogFragment df = new AutofillFragment(); + Bundle args = new Bundle(); + args.putString("packageName", packageName); + args.putString("appName", appName); + args.putInt("position", -1); + df.setArguments(args); + df.show(getFragmentManager(), "autofill_dialog"); + return false; + } + }); + setTitle("Autofill Apps"); } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java new file mode 100644 index 00000000..e9aa86f0 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java @@ -0,0 +1,82 @@ +package com.zeapo.pwdstore.autofill; + +import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.RadioButton; +import android.widget.RadioGroup; + +import com.zeapo.pwdstore.R; + +public class AutofillFragment extends DialogFragment { + + public AutofillFragment() { + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // this fragment is only created from the settings page (AutofillActivity) + // need to interact with the recyclerAdapter which is a member of activity + final AutofillActivity callingActivity = (AutofillActivity) getActivity(); + LayoutInflater inflater = callingActivity.getLayoutInflater(); + final View view = inflater.inflate(R.layout.fragment_autofill, null); + + builder.setView(view); + + final String packageName = getArguments().getString("packageName"); + String appName = getArguments().getString("appName"); + + builder.setTitle(appName); + SharedPreferences prefs + = getActivity().getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); + String preference = prefs.getString(packageName, "first"); + switch (preference) { + case "first": + ((RadioButton) view.findViewById(R.id.first)).toggle(); + break; + case "never": + ((RadioButton) view.findViewById(R.id.never)).toggle(); + break; + default: + ((RadioButton) view.findViewById(R.id.match)).toggle(); + ((EditText) view.findViewById(R.id.matched)).setText(preference); + } + + final SharedPreferences.Editor editor = prefs.edit(); + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.autofill_radiogroup); + switch (radioGroup.getCheckedRadioButtonId()) { + case R.id.first: + editor.putString(packageName, "first"); + break; + case R.id.never: + editor.putString(packageName, "never"); + break; + default: + EditText matched = (EditText) view.findViewById(R.id.matched); + String path = matched.getText().toString(); + editor.putString(packageName, path); + } + editor.apply(); + int position = getArguments().getInt("position"); + if (position == -1) { + callingActivity.recyclerAdapter.add(packageName); + } else { + callingActivity.recyclerAdapter.notifyItemChanged(position); + } + } + }); + builder.setNegativeButton("Cancel", null); + return builder.create(); + } +} 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 b59223bd..eff94c51 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java @@ -1,7 +1,11 @@ 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.pm.PackageManager; +import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -16,29 +20,40 @@ import java.util.ArrayList; public class AutofillRecyclerAdapter extends RecyclerView.Adapter { private ArrayList apps; private PackageManager pm; + private AutofillActivity activity; public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { public View view; public TextView name; + public TextView secondary; public ImageView icon; + public String packageName; public ViewHolder(View view) { super(view); this.view = view; name = (TextView) view.findViewById(R.id.app_name); + secondary = (TextView) view.findViewById(R.id.secondary_text); icon = (ImageView) view.findViewById(R.id.app_icon); view.setOnClickListener(this); } @Override public void onClick(View v) { - + DialogFragment df = new AutofillFragment(); + Bundle args = new Bundle(); + args.putString("packageName", packageName); + args.putString("appName", name.getText().toString()); + args.putInt("position", getAdapterPosition()); + df.setArguments(args); + df.show(activity.getFragmentManager(), "autofill_dialog"); } } - public AutofillRecyclerAdapter(ArrayList apps, PackageManager pm) { + public AutofillRecyclerAdapter(ArrayList apps, PackageManager pm, AutofillActivity activity) { this.apps = apps; this.pm = pm; + this.activity = activity; } @Override @@ -52,11 +67,45 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter + android:layout_height="match_parent" + android:focusable="true" + android:focusableInTouchMode="true"> + + + + + + + + + + + + + + + \ 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 3a8c4fdf..538f0387 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -74,7 +74,7 @@ -- cgit v1.2.3 From d42c526b648030ee251d9136b8d924395b74bb9c Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Tue, 11 Aug 2015 13:34:14 -0400 Subject: passwordfragment changes --- .../main/java/com/zeapo/pwdstore/PasswordFragment.java | 6 +++++- app/src/main/java/com/zeapo/pwdstore/PasswordStore.java | 15 +++++++++++++++ .../com/zeapo/pwdstore/autofill/AutofillFragment.java | 13 ++++++++++++- .../zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java | 2 -- app/src/main/res/xml/preference.xml | 2 +- 5 files changed, 33 insertions(+), 5 deletions(-) (limited to 'app/src/main/res/xml/preference.xml') diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java index 72636043..1daa4f90 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.java @@ -112,7 +112,11 @@ public class PasswordFragment extends Fragment{ ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); } else { - ((PasswordStore) getActivity()).decryptPassword(item); + if (getArguments().getBoolean("matchWith", false)) { + ((PasswordStore) getActivity()).matchPasswordWithApp(item); + } else { + ((PasswordStore) getActivity()).decryptPassword(item); + } } } diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java index e51ef201..39d918c8 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.java @@ -289,6 +289,12 @@ public class PasswordStore extends AppCompatActivity { Bundle args = new Bundle(); args.putString("Path", PasswordRepository.getWorkTree().getAbsolutePath()); + // if the activity was started from the autofill settings, the + // intent is to match a clicked pwd with app. pass this to fragment + if (getIntent().getBooleanExtra("matchWith", false)) { + args.putBoolean("matchWith", true); + } + plist.setArguments(args); fragmentTransaction.addToBackStack("passlist"); @@ -531,4 +537,13 @@ public class PasswordStore extends AppCompatActivity { }) .show(); } + + public void matchPasswordWithApp(PasswordItem item) { + String path = item.getFile().getAbsolutePath(); + path = path.replace(PasswordRepository.getWorkTree() + "/", "").replace(".gpg", ""); + Intent data = new Intent(); + data.putExtra("path", path); + setResult(RESULT_OK, data); + finish(); + } } 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 b85dec6a..3e00edfb 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java @@ -1,5 +1,6 @@ package com.zeapo.pwdstore.autofill; +import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.support.v7.app.AlertDialog; @@ -18,6 +19,7 @@ import com.zeapo.pwdstore.PasswordStore; import com.zeapo.pwdstore.R; public class AutofillFragment extends DialogFragment { + private static final int MATCH_WITH = 777; public AutofillFragment() { } @@ -57,7 +59,8 @@ public class AutofillFragment extends DialogFragment { public void onClick(View v) { // TODO figure out UI for this Intent intent = new Intent(getActivity(), PasswordStore.class); - startActivity(intent); + intent.putExtra("matchWith", true); + startActivityForResult(intent, MATCH_WITH); } }; view.findViewById(R.id.match).setOnClickListener(matchPassword); @@ -92,4 +95,12 @@ public class AutofillFragment extends DialogFragment { builder.setNegativeButton("Cancel", null); return builder.create(); } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + ((EditText) getDialog().findViewById(R.id.matched)).setText(data.getStringExtra("path")); + } + + } } 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 26680152..1b5e63b7 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java @@ -1,11 +1,9 @@ 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.pm.PackageManager; -import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml index 538f0387..9a42bb3e 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -75,7 +75,7 @@ + android:title="Per-app settings"/> -- cgit v1.2.3 From b75190fcd7926c3994f8191996661c19b7b12466 Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Tue, 11 Aug 2015 18:32:16 -0400 Subject: Add 'automatically match by default' setting; respect it & per-app settings when creating autofill dialog --- .../zeapo/pwdstore/autofill/AutofillFragment.java | 9 +++++++-- .../autofill/AutofillPreferenceActivity.java | 4 ++++ .../pwdstore/autofill/AutofillRecyclerAdapter.java | 7 ++++++- .../com/zeapo/pwdstore/autofill/AutofillService.java | 20 ++++++++++++++++++-- app/src/main/res/xml/preference.xml | 5 +++++ 5 files changed, 40 insertions(+), 5 deletions(-) (limited to 'app/src/main/res/xml/preference.xml') 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 3e00edfb..ea4b0a2f 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java @@ -3,6 +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.support.v7.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; @@ -39,9 +40,14 @@ public class AutofillFragment extends DialogFragment { String appName = getArguments().getString("appName"); builder.setTitle(appName); + + // 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, "first"); + String preference = prefs.getString(packageName, defValue); switch (preference) { case "first": ((RadioButton) view.findViewById(R.id.first)).toggle(); @@ -57,7 +63,6 @@ public class AutofillFragment extends DialogFragment { View.OnClickListener matchPassword = new View.OnClickListener() { @Override public void onClick(View v) { - // TODO figure out UI for this Intent intent = new Intent(getActivity(), PasswordStore.class); intent.putExtra("matchWith", true); startActivityForResult(intent, MATCH_WITH); 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 89d3474e..10684f45 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillPreferenceActivity.java @@ -8,6 +8,7 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.database.MatrixCursor; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -100,6 +101,7 @@ public class AutofillPreferenceActivity extends AppCompatActivity { @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())) { @@ -147,5 +149,7 @@ public class AutofillPreferenceActivity extends AppCompatActivity { args.putInt("position", recyclerAdapter.getPosition(packageName)); df.setArguments(args); df.show(getFragmentManager(), "autofill_dialog"); + + // TODO if called from dialog 'Settings' button, should activity finish at OK? } } 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 1b5e63b7..bd2835e5 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.preference.PreferenceManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -59,9 +60,13 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter(); + items.add(PasswordItem.newPassword(file.getName(), file)); + } if (items.isEmpty()) { return; } diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml index 9a42bb3e..44e38916 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -76,6 +76,11 @@ android:key="autofill_apps" android:summary="Customize autofill settings for specific apps." android:title="Per-app settings"/> + -- cgit v1.2.3 From ec07e1eea69a69e9579ddfb44fdbee3535bcf6ad Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Thu, 13 Aug 2015 15:18:47 -0400 Subject: Preference activity overhaul --- .../zeapo/pwdstore/autofill/AutofillFragment.java | 31 +++-- .../autofill/AutofillPreferenceActivity.java | 122 ++++++------------- .../pwdstore/autofill/AutofillRecyclerAdapter.java | 132 ++++----------------- .../zeapo/pwdstore/autofill/AutofillService.java | 3 +- app/src/main/res/layout/autofill_recycler_view.xml | 11 +- app/src/main/res/layout/autofill_row_layout.xml | 5 +- app/src/main/res/layout/fragment_autofill.xml | 15 ++- app/src/main/res/menu/autofill_preference.xml | 12 ++ app/src/main/res/xml/preference.xml | 2 +- 9 files changed, 108 insertions(+), 225 deletions(-) create mode 100644 app/src/main/res/menu/autofill_preference.xml (limited to 'app/src/main/res/xml/preference.xml') 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 allApps = getPackageManager().queryIntentActivities(intent, 0); final PackageManager pm = getPackageManager(); - SharedPreferences prefs - = getSharedPreferences("autofill", Context.MODE_PRIVATE); - Map prefApps = prefs.getAll(); - ArrayList 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() { + Collections.sort(allApps, new Comparator() { @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 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 { - private ArrayList apps; + private ArrayList apps; private PackageManager pm; private AutofillPreferenceActivity activity; - private final Set 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 apps, PackageManager pm, AutofillPreferenceActivity activity) { + public AutofillRecyclerAdapter(ArrayList 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 - - + android:layout_height="match_parent"> + android:id="@+id/secondary_text" + android:textColor="@color/grey_600"/> 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" > + @@ -29,7 +36,7 @@ android:id="@+id/match" android:layout_gravity="center_vertical" android:checked="false" - android:layout_marginTop="8dp"/> + /> + /> 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 @@ + + + + \ 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 @@ -- cgit v1.2.3 From ebe1f831e7659b63b76c2b39498b68d1fec61673 Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Fri, 14 Aug 2015 16:51:39 -0400 Subject: strings & enable service preference --- .../java/com/zeapo/pwdstore/UserPreference.java | 51 +++++++++++++++++++++- .../zeapo/pwdstore/autofill/AutofillFragment.java | 5 ++- .../pwdstore/autofill/AutofillRecyclerAdapter.java | 4 +- .../zeapo/pwdstore/autofill/AutofillService.java | 6 +-- app/src/main/res/layout/autofill_row_layout.xml | 2 - app/src/main/res/layout/fragment_autofill.xml | 8 ++-- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values/strings.xml | 15 ++++++- app/src/main/res/xml/preference.xml | 14 +++--- 9 files changed, 85 insertions(+), 22 deletions(-) (limited to 'app/src/main/res/xml/preference.xml') diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.java b/app/src/main/java/com/zeapo/pwdstore/UserPreference.java index cb2d2881..283d5f47 100644 --- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.java +++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.java @@ -1,17 +1,22 @@ package com.zeapo.pwdstore; -import android.app.AlertDialog; +import android.accessibilityservice.AccessibilityServiceInfo; import android.app.DialogFragment; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; +import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; +import android.provider.Settings; +import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; +import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.google.common.base.Function; @@ -33,6 +38,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; +import java.util.List; import java.util.Set; public class UserPreference extends AppCompatActivity { @@ -192,6 +198,30 @@ public class UserPreference extends AppCompatActivity { return true; } }); + + findPreference("autofill_enable").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + new AlertDialog.Builder(callingActivity). + setTitle(R.string.pref_autofill_enable_title). + setMessage(R.string.pref_autofill_enable_msg). + setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); + startActivity(intent); + } + }). + setNegativeButton(R.string.dialog_cancel,new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ((CheckBoxPreference) findPreference("autofill_enable")) + .setChecked(((UserPreference) getActivity()).isServiceEnabled()); + } + }).show(); + return false; + } + }); } @Override @@ -199,6 +229,10 @@ public class UserPreference extends AppCompatActivity { super.onStart(); final SharedPreferences sharedPreferences = getPreferenceManager().getSharedPreferences(); findPreference("ssh_see_key").setEnabled(sharedPreferences.getBoolean("use_generated_key", false)); + + // see if the autofill service is enabled and check the preference accordingly + ((CheckBoxPreference) findPreference("autofill_enable")) + .setChecked(((UserPreference) getActivity()).isServiceEnabled()); } } @@ -278,6 +312,21 @@ public class UserPreference extends AppCompatActivity { sshKey.close(); } + // Returns whether the autofill service is enabled + private boolean isServiceEnabled() { + AccessibilityManager am = (AccessibilityManager) this + .getSystemService(Context.ACCESSIBILITY_SERVICE); + List runningServices = am + .getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC); + for (AccessibilityServiceInfo service : runningServices) { + if ("com.zeapo.pwdstore/.autofill.AutofillService".equals(service.getId())) { + return true; + } + } + return false; + } + + protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK) { 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 c2a30ffe..8cf58206 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillFragment.java @@ -68,6 +68,7 @@ public class AutofillFragment extends DialogFragment { View.OnClickListener matchPassword = new View.OnClickListener() { @Override public void onClick(View v) { + ((RadioButton) view.findViewById(R.id.match)).toggle(); Intent intent = new Intent(getActivity(), PasswordStore.class); intent.putExtra("matchWith", true); startActivityForResult(intent, MATCH_WITH); @@ -77,7 +78,7 @@ public class AutofillFragment extends DialogFragment { view.findViewById(R.id.matched).setOnClickListener(matchPassword); final SharedPreferences.Editor editor = prefs.edit(); - builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + builder.setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.autofill_radiogroup); @@ -105,7 +106,7 @@ public class AutofillFragment extends DialogFragment { } } }); - builder.setNegativeButton("Cancel", null); + builder.setNegativeButton(R.string.dialog_cancel, null); return builder.create(); } 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 01916650..8971c0af 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/AutofillRecyclerAdapter.java @@ -105,10 +105,10 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter - - diff --git a/app/src/main/res/layout/fragment_autofill.xml b/app/src/main/res/layout/fragment_autofill.xml index e5534f41..af2b7017 100644 --- a/app/src/main/res/layout/fragment_autofill.xml +++ b/app/src/main/res/layout/fragment_autofill.xml @@ -16,7 +16,7 @@ @@ -24,7 +24,7 @@ @@ -32,7 +32,7 @@ Zpráva : \n Rekurzivní filtrování Rekurzivní hledání hesel v aktuálním adresáři. + Zaplnit schránku 20krát Uložit dvacet náhodných textů do schránky namísto pouze jednoho. Užitečné pro telefony Samsug, které nabízejí funkci historie schránky. - Zaplnit schránku 20krát Generovat diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa5358e2..447139fb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -117,8 +117,14 @@ Message : \n Recursive filtering Recursively find passwords of the current directory. + Enable autofill + Tap OK to go to Accessibility settings. Once there, tap Password Store under Services then tap the switch in the top right to turn it on or off. + Per-app settings + Customize autofill settings for specific apps. + Automatically match by default + Default to \'Automatically match\' for apps without custom settings. Otherwise, \'Never match.\' + Clear clipboard 20 times Store nonsense in the clipboard 20 times instead of just once. Useful on Samsung phones that feature clipboard history. - Clear clipboard 20 times Generate @@ -155,5 +161,10 @@ "Category: " - Auto-fills password fields in apps. Only works for Android versions 4.3 and up. Does not rely on the clipboard for Android versions 5.0 and up. + Autofills password fields in apps. Only works for Android versions 4.3 and up. Does not rely on the clipboard for Android versions 5.0 and up. + Fill + Use default setting + Automatically match + Match with… + Never match diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml index e8ba80cc..1c977992 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -72,15 +72,19 @@ + + android:summary="@string/pref_autofill_apps_hint" + android:title="@string/pref_autofill_apps_title"/> + android:summary="@string/pref_autofill_default_hint" + android:title="@string/pref_autofill_default_title"/> @@ -88,6 +92,6 @@ android:defaultValue="false" android:key="clear_clipboard_20x" android:summary="@string/pref_clear_clipboard_hint" - android:title="@string/pref_clear_clipboard" /> + android:title="@string/pref_clear_clipboard_title" /> \ No newline at end of file -- cgit v1.2.3 From a73e8625f55e869ba0d74bfa370178c54be3662d Mon Sep 17 00:00:00 2001 From: Matthew Wong Date: Sun, 16 Aug 2015 16:58:03 -0400 Subject: Settings dependent on service enabled --- app/src/main/res/xml/preference.xml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'app/src/main/res/xml/preference.xml') diff --git a/app/src/main/res/xml/preference.xml b/app/src/main/res/xml/preference.xml index 1c977992..639fdfe1 100644 --- a/app/src/main/res/xml/preference.xml +++ b/app/src/main/res/xml/preference.xml @@ -77,10 +77,12 @@ android:key="autofill_enable" android:title="@string/pref_autofill_enable_title"/>