Add websites to autofill preferences

This commit is contained in:
Matthew Wong 2015-12-30 00:15:08 -05:00
parent 5cb380bf47
commit 8c884bcba8
7 changed files with 167 additions and 61 deletions

View file

@ -16,6 +16,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView; import android.widget.ListView;
import android.widget.RadioButton; import android.widget.RadioButton;
import android.widget.RadioGroup; import android.widget.RadioGroup;
@ -39,18 +40,28 @@ public class AutofillFragment extends DialogFragment {
final AutofillPreferenceActivity callingActivity = (AutofillPreferenceActivity) getActivity(); final AutofillPreferenceActivity callingActivity = (AutofillPreferenceActivity) getActivity();
LayoutInflater inflater = callingActivity.getLayoutInflater(); LayoutInflater inflater = callingActivity.getLayoutInflater();
// if... hide textview
final View view = inflater.inflate(R.layout.fragment_autofill, null); final View view = inflater.inflate(R.layout.fragment_autofill, null);
builder.setView(view); builder.setView(view);
final String packageName = getArguments().getString("packageName"); String packageName = getArguments().getString("packageName", "");
String appName = getArguments().getString("appName"); String appName = getArguments().getString("appName", "");
final boolean isWebsite = appName.equals(packageName);
// set the dialog icon and title or webName editText
String iconPackageName;
if (!isWebsite) {
iconPackageName = packageName;
builder.setTitle(appName); builder.setTitle(appName);
view.findViewById(R.id.webName).setVisibility(View.GONE);
} else {
iconPackageName = "com.android.browser";
builder.setTitle("Website");
((EditText) view.findViewById(R.id.webName)).setText(packageName);
}
try { try {
// since we can't (easily?) pass the drawable as an argument builder.setIcon(callingActivity.getPackageManager().getApplicationIcon(iconPackageName));
builder.setIcon(callingActivity.getPackageManager().getApplicationIcon(packageName));
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -76,10 +87,15 @@ public class AutofillFragment extends DialogFragment {
} }
}); });
// if... autofill_web // set the existing preference, if any
SharedPreferences prefs SharedPreferences prefs;
= getActivity().getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); String preference;
String preference = prefs.getString(packageName, ""); if (!isWebsite) {
prefs = getActivity().getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE);
} else {
prefs = getActivity().getApplicationContext().getSharedPreferences("autofill_web", Context.MODE_PRIVATE);
}
preference = prefs.getString(packageName, "");
switch (preference) { switch (preference) {
case "": case "":
((RadioButton) view.findViewById(R.id.use_default)).toggle(); ((RadioButton) view.findViewById(R.id.use_default)).toggle();
@ -108,20 +124,43 @@ public class AutofillFragment extends DialogFragment {
}; };
view.findViewById(R.id.matchButton).setOnClickListener(matchPassword); view.findViewById(R.id.matchButton).setOnClickListener(matchPassword);
// write to preferences when OK clicked
final SharedPreferences.Editor editor = prefs.edit(); final SharedPreferences.Editor editor = prefs.edit();
builder.setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() { builder.setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
String key;
String packageName = getArguments().getString("packageName", "");
if (!isWebsite) {
key = packageName;
} else {
key = ((EditText) view.findViewById(R.id.webName)).getText().toString();
// if key.equals("") show error
// if new packageName/appName/website name/website title/key
// is different than old, remove the old one. Basically,
// "edit" the old one.
if (!key.equals(packageName) && !packageName.equals("")) {
editor.remove(packageName);
if (callingActivity.recyclerAdapter != null) {
if (callingActivity.recyclerAdapter.getPosition(packageName) != -1) {
callingActivity.recyclerAdapter.removeWebsite(packageName);
}
}
}
}
RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.autofill_radiogroup); RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.autofill_radiogroup);
switch (radioGroup.getCheckedRadioButtonId()) { switch (radioGroup.getCheckedRadioButtonId()) {
case R.id.use_default: case R.id.use_default:
editor.remove(packageName); editor.remove(key);
break; break;
case R.id.first: case R.id.first:
editor.putString(packageName, "/first"); editor.putString(key, "/first");
break; break;
case R.id.never: case R.id.never:
editor.putString(packageName, "/never"); editor.putString(key, "/never");
break; break;
default: default:
StringBuilder paths = new StringBuilder(); StringBuilder paths = new StringBuilder();
@ -131,14 +170,36 @@ public class AutofillFragment extends DialogFragment {
paths.append("\n"); paths.append("\n");
} }
} }
editor.putString(packageName, paths.toString()); editor.putString(key, paths.toString());
} }
editor.apply(); editor.apply();
// if recyclerAdapter has not loaded yet, there is no need to notifyItemChanged // if recyclerAdapter has not loaded yet, there is no need to notify
if (callingActivity.recyclerAdapter != null) { if (callingActivity.recyclerAdapter != null) {
int position = callingActivity.recyclerAdapter.getPosition(packageName); int position;
if (!isWebsite) {
String appName = getArguments().getString("appName", "");
position = callingActivity.recyclerAdapter.getPosition(appName);
callingActivity.recyclerAdapter.notifyItemChanged(position); callingActivity.recyclerAdapter.notifyItemChanged(position);
} else {
String appName = ((EditText) view.findViewById(R.id.webName)).getText().toString();
position = callingActivity.recyclerAdapter.getPosition(appName);
switch (radioGroup.getCheckedRadioButtonId()) {
// remove if existed, else do nothing
case R.id.use_default:
if (position != -1) {
callingActivity.recyclerAdapter.removeWebsite(appName);
}
break;
// change if existed, else add
default:
if (position != -1) {
callingActivity.recyclerAdapter.notifyItemChanged(position);
} else {
callingActivity.recyclerAdapter.addWebsite(appName);
}
}
}
} }
} }
}); });

View file

@ -65,6 +65,7 @@ public class AutofillPreferenceActivity extends AppCompatActivity {
fab.setOnClickListener(new View.OnClickListener() { fab.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
showDialog("", "");
} }
}); });
} }
@ -83,17 +84,17 @@ public class AutofillPreferenceActivity extends AppCompatActivity {
List<AutofillRecyclerAdapter.AppInfo> allApps = new ArrayList<>(); List<AutofillRecyclerAdapter.AppInfo> allApps = new ArrayList<>();
for (ResolveInfo app : allAppsResolveInfo) { for (ResolveInfo app : allAppsResolveInfo) {
allApps.add(new AutofillRecyclerAdapter.AppInfo(app.loadLabel(pm).toString() allApps.add(new AutofillRecyclerAdapter.AppInfo(app.activityInfo.packageName
, app.activityInfo.packageName, app.loadIcon(pm))); , app.loadLabel(pm).toString(), app.loadIcon(pm)));
} }
SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE);
Map<String, ?> prefsMap = prefs.getAll(); Map<String, ?> prefsMap = prefs.getAll();
for (String key : prefsMap.keySet()) { for (String key : prefsMap.keySet()) {
try { try {
allApps.add(new AutofillRecyclerAdapter.AppInfo(null, key, pm.getApplicationIcon("com.android.browser"))); allApps.add(new AutofillRecyclerAdapter.AppInfo(key, key, pm.getApplicationIcon("com.android.browser")));
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
allApps.add(new AutofillRecyclerAdapter.AppInfo(null, key, null)); allApps.add(new AutofillRecyclerAdapter.AppInfo(key, key, null));
} }
} }
@ -108,7 +109,7 @@ public class AutofillPreferenceActivity extends AppCompatActivity {
recyclerView.setAdapter(recyclerAdapter); recyclerView.setAdapter(recyclerAdapter);
Bundle extras = getIntent().getExtras(); Bundle extras = getIntent().getExtras();
if (extras != null) { if (extras != null) {
recyclerView.scrollToPosition(recyclerAdapter.getPosition(extras.getString("packageName"))); recyclerView.scrollToPosition(recyclerAdapter.getPosition(extras.getString("appName")));
} }
} }
} }

View file

@ -21,7 +21,7 @@ import java.util.List;
public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder> { public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder> {
private SortedList<AppInfo> apps; private SortedList<AppInfo> apps;
private ArrayList<AppInfo> allApps; private ArrayList<AppInfo> allApps; // for filtering, maintain a list of all
private PackageManager pm; private PackageManager pm;
private AutofillPreferenceActivity activity; private AutofillPreferenceActivity activity;
@ -31,6 +31,7 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
public TextView secondary; public TextView secondary;
public ImageView icon; public ImageView icon;
public String packageName; public String packageName;
public String appName;
public ViewHolder(View view) { public ViewHolder(View view) {
super(view); super(view);
@ -43,39 +44,46 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
@Override @Override
public void onClick(View v) { public void onClick(View v) {
activity.showDialog(packageName, name.getText().toString()); activity.showDialog(packageName, appName);
} }
} }
public static class AppInfo { public static class AppInfo {
public String label;
public String packageName; public String packageName;
public String appName;
public Drawable icon; public Drawable icon;
public AppInfo(String label, String packageName, Drawable icon) { public AppInfo(String packageName, String appName, Drawable icon) {
this.label = label;
this.packageName = packageName; this.packageName = packageName;
this.appName = appName;
this.icon = icon; this.icon = icon;
} }
@Override
public boolean equals(Object o) {
return o != null && o instanceof AppInfo && this.appName.equals(((AppInfo) o).appName);
}
} }
public AutofillRecyclerAdapter(List<AppInfo> allApps, final PackageManager pm public AutofillRecyclerAdapter(List<AppInfo> allApps, final PackageManager pm
, AutofillPreferenceActivity activity) { , AutofillPreferenceActivity activity) {
SortedList.Callback<AppInfo> callback = new SortedListAdapterCallback<AppInfo>(this) { SortedList.Callback<AppInfo> callback = new SortedListAdapterCallback<AppInfo>(this) {
// don't take into account secondary text. This is good enough
// for the limited add/remove usage for websites
@Override @Override
public int compare(AppInfo o1, AppInfo o2) { public int compare(AppInfo o1, AppInfo o2) {
return o1.label.toLowerCase().compareTo(o2.label.toLowerCase()); return o1.appName.toLowerCase().compareTo(o2.appName.toLowerCase());
} }
@Override @Override
public boolean areContentsTheSame(AppInfo oldItem, AppInfo newItem) { public boolean areContentsTheSame(AppInfo oldItem, AppInfo newItem) {
return oldItem.label.equals(newItem.label); return oldItem.appName.equals(newItem.appName);
} }
@Override @Override
public boolean areItemsTheSame(AppInfo item1, AppInfo item2) { public boolean areItemsTheSame(AppInfo item1, AppInfo item2) {
return item1.packageName.equals(item2.packageName); return item1.appName.equals(item2.appName);
} }
}; };
this.apps = new SortedList<>(AppInfo.class, callback); this.apps = new SortedList<>(AppInfo.class, callback);
@ -96,15 +104,20 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
public void onBindViewHolder(AutofillRecyclerAdapter.ViewHolder holder, int position) { public void onBindViewHolder(AutofillRecyclerAdapter.ViewHolder holder, int position) {
AppInfo app = apps.get(position); AppInfo app = apps.get(position);
holder.packageName = app.packageName; holder.packageName = app.packageName;
holder.appName = app.appName;
holder.icon.setImageDrawable(app.icon); holder.icon.setImageDrawable(app.icon);
holder.name.setText(app.label); holder.name.setText(app.appName);
holder.secondary.setVisibility(View.VISIBLE); holder.secondary.setVisibility(View.VISIBLE);
holder.view.setBackgroundResource(R.color.grey_white_1000); holder.view.setBackgroundResource(R.color.grey_white_1000);
SharedPreferences prefs SharedPreferences prefs;
= activity.getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE); if (!app.appName.equals(app.packageName)) {
prefs = activity.getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE);
} else {
prefs = activity.getApplicationContext().getSharedPreferences("autofill_web", Context.MODE_PRIVATE);
}
String preference = prefs.getString(holder.packageName, ""); String preference = prefs.getString(holder.packageName, "");
switch (preference) { switch (preference) {
case "": case "":
@ -118,7 +131,8 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
holder.secondary.setText(R.string.autofill_apps_never); holder.secondary.setText(R.string.autofill_apps_never);
break; break;
default: default:
holder.secondary.setText("Match with " + preference.split("\n")[0]); holder.secondary.setText(R.string.autofill_apps_match);
holder.secondary.append(" " + preference.split("\n")[0]);
if ((preference.trim().split("\n").length - 1) > 0) { if ((preference.trim().split("\n").length - 1) > 0) {
holder.secondary.append(" and " holder.secondary.append(" and "
+ (preference.trim().split("\n").length - 1) + " more"); + (preference.trim().split("\n").length - 1) + " more");
@ -132,13 +146,24 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
return apps.size(); return apps.size();
} }
public int getPosition(String packageName) { public int getPosition(String appName) {
for (int i = 0; i < apps.size(); i++) { return apps.indexOf(new AppInfo(null, appName, null));
if (apps.get(i).packageName.equals(packageName)) {
return i;
} }
public void addWebsite(String appName) {
Drawable icon = null;
try {
icon = activity.getPackageManager().getApplicationIcon("com.android.browser");
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} }
return -1; apps.add(new AppInfo(appName, appName, icon));
allApps.add(new AppInfo(appName, appName, icon));
}
public void removeWebsite(String appName) {
apps.remove(new AppInfo(null, appName, null));
allApps.remove(new AppInfo(null, appName, null)); // compare with equals
} }
public void filter(String s) { public void filter(String s) {
@ -148,7 +173,7 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
} }
apps.beginBatchedUpdates(); apps.beginBatchedUpdates();
for (AppInfo app : allApps) { for (AppInfo app : allApps) {
if (app.label.toLowerCase().contains(s.toLowerCase())) { if (app.appName.toLowerCase().contains(s.toLowerCase())) {
apps.add(app); apps.add(app);
} else { } else {
apps.remove(app); apps.remove(app);

View file

@ -146,16 +146,13 @@ public class AutofillService extends AccessibilityService {
setMatchingPasswords(appName, info.getPackageName().toString()); setMatchingPasswords(appName, info.getPackageName().toString());
} else { } else {
appName = webViewTitle; appName = setMatchingPasswordsWeb(webViewTitle);
setMatchingPasswordsWeb(webViewTitle);
} }
if (items.isEmpty()) { // show anyway preference? if (items.isEmpty()) { // show anyway preference?
return; return;
} }
showDialog(appName); showDialog(appName);
} }
private String searchWebView(AccessibilityNodeInfo source) { private String searchWebView(AccessibilityNodeInfo source) {
@ -220,13 +217,14 @@ public class AutofillService extends AccessibilityService {
} }
} }
private void setMatchingPasswordsWeb(String webViewTitle) { // return key for opening its Settings. Otherwise just use the title
private String setMatchingPasswordsWeb(String webViewTitle) {
SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences("autofill_web", Context.MODE_PRIVATE);
Map<String, ?> prefsMap = prefs.getAll(); Map<String, ?> prefsMap = prefs.getAll();
for (String key : prefsMap.keySet()) { for (String key : prefsMap.keySet()) {
if (webViewTitle.toLowerCase().contains(key.toLowerCase())) { if (webViewTitle.toLowerCase().contains(key.toLowerCase())) {
getPreferredPasswords(prefs.getString(key, "")); getPreferredPasswords(prefs.getString(key, ""));
return; return key;
} }
} }
// possible user-defined match not found, try default setting // possible user-defined match not found, try default setting
@ -238,6 +236,7 @@ public class AutofillService extends AccessibilityService {
} else { } else {
items.clear(); items.clear();
} }
return webViewTitle;
} }
// Put the newline separated list of passwords from the SharedPreferences // Put the newline separated list of passwords from the SharedPreferences
@ -295,11 +294,16 @@ public class AutofillService extends AccessibilityService {
// the user will have to return to the app themselves. // the user will have to return to the app themselves.
Intent intent = new Intent(AutofillService.this, AutofillPreferenceActivity.class); 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.FLAG_ACTIVITY_CLEAR_TASK);
if (webViewTitle == null) {
intent.putExtra("packageName", info.getPackageName()); intent.putExtra("packageName", info.getPackageName());
} else {
intent.putExtra("packageName", appName);
}
intent.putExtra("appName", appName); intent.putExtra("appName", appName);
startActivity(intent); startActivity(intent);
} }
}); });
CharSequence itemNames[] = new CharSequence[items.size()]; CharSequence itemNames[] = new CharSequence[items.size()];
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
itemNames[i] = items.get(i).getName().replace(".gpg", ""); itemNames[i] = items.get(i).getName().replace(".gpg", "");
@ -312,12 +316,13 @@ public class AutofillService extends AccessibilityService {
bindDecryptAndVerify(); bindDecryptAndVerify();
} }
}); });
dialog = builder.create(); dialog = builder.create();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
// arbitrary non-annoying size // arbitrary non-annoying size
int height = 160; int height = 144;
if (items.size() > 1) { if (items.size() > 1) {
height += 48; height += 48;
} }

View file

@ -9,6 +9,19 @@
android:paddingRight="24dp" android:paddingRight="24dp"
android:paddingTop="20dp"> android:paddingTop="20dp">
<android.support.design.widget.TextInputLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintTextAppearance="@style/TextAppearance.AppCompat">
<EditText
android:id="@+id/webName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/autofill_webname_hint"
android:inputType="textPersonName"/>
</android.support.design.widget.TextInputLayout>
<RadioGroup <RadioGroup
android:id="@+id/autofill_radiogroup" android:id="@+id/autofill_radiogroup"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -167,6 +167,6 @@
<string name="autofill_fill">Vyplnit</string> <string name="autofill_fill">Vyplnit</string>
<string name="autofill_apps_default">Použít výchozí nastavení</string> <string name="autofill_apps_default">Použít výchozí nastavení</string>
<string name="autofill_apps_first">Automaticky spárovat</string> <string name="autofill_apps_first">Automaticky spárovat</string>
<string name="autofill_apps_match_ellipsis">Spárovat s</string> <string name="autofill_apps_match_ellipsis">Spárovat s</string>
<string name="autofill_apps_never">Nikdy nepárovat</string> <string name="autofill_apps_never">Nikdy nepárovat</string>
</resources> </resources>

View file

@ -167,6 +167,7 @@
<string name="autofill_fill">Fill</string> <string name="autofill_fill">Fill</string>
<string name="autofill_apps_default">Use default setting</string> <string name="autofill_apps_default">Use default setting</string>
<string name="autofill_apps_first">Automatically match</string> <string name="autofill_apps_first">Automatically match</string>
<string name="autofill_apps_match_ellipsis">Match with</string> <string name="autofill_apps_match">Match with</string>
<string name="autofill_apps_never">Never match</string> <string name="autofill_apps_never">Never match</string>
<string name="autofill_webname_hint">Name</string>
</resources> </resources>