mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-09-01 23:05:33 +00:00
Autofill per-app settings dialog opens on click on suggestion or on existing app
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
package com.zeapo.pwdstore.autofill;
|
package com.zeapo.pwdstore.autofill;
|
||||||
|
|
||||||
|
import android.app.DialogFragment;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -33,7 +34,7 @@ public class AutofillActivity extends AppCompatActivity {
|
|||||||
private boolean bound = false;
|
private boolean bound = false;
|
||||||
|
|
||||||
private RecyclerView recyclerView;
|
private RecyclerView recyclerView;
|
||||||
private AutofillRecyclerAdapter recyclerAdapter;
|
AutofillRecyclerAdapter recyclerAdapter; // let fragment have access
|
||||||
private RecyclerView.LayoutManager layoutManager;
|
private RecyclerView.LayoutManager layoutManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -41,6 +42,7 @@ public class AutofillActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
Bundle extras = intent.getExtras();
|
Bundle extras = intent.getExtras();
|
||||||
|
|
||||||
// if called by service just for startIntentSenderForResult
|
// if called by service just for startIntentSenderForResult
|
||||||
if (extras != null) {
|
if (extras != null) {
|
||||||
try {
|
try {
|
||||||
@@ -64,21 +66,26 @@ public class AutofillActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
// apps for which the user has custom settings should be in the recycler
|
// apps for which the user has custom settings should be in the recycler
|
||||||
final PackageManager pm = getPackageManager();
|
final PackageManager pm = getPackageManager();
|
||||||
final List<ApplicationInfo> allApps = pm.getInstalledApplications(0);
|
|
||||||
SharedPreferences prefs
|
SharedPreferences prefs
|
||||||
= getSharedPreferences("autofill", Context.MODE_PRIVATE);
|
= getSharedPreferences("autofill", Context.MODE_PRIVATE);
|
||||||
Map<String, ?> prefApps = prefs.getAll();
|
Map<String, ?> prefApps = prefs.getAll();
|
||||||
ArrayList<ApplicationInfo> apps = new ArrayList<>();
|
ArrayList<ApplicationInfo> apps = new ArrayList<>();
|
||||||
for (ApplicationInfo applicationInfo : allApps) {
|
for (String packageName : prefApps.keySet()) {
|
||||||
if (prefApps.containsKey(applicationInfo.packageName)) {
|
try {
|
||||||
apps.add(applicationInfo);
|
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);
|
recyclerView.setAdapter(recyclerAdapter);
|
||||||
|
|
||||||
|
// show the search bar by default but don't open the keyboard
|
||||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||||
final SearchView searchView = (SearchView) findViewById(R.id.app_search);
|
final SearchView searchView = (SearchView) findViewById(R.id.app_search);
|
||||||
|
searchView.clearFocus();
|
||||||
|
|
||||||
// create search suggestions of apps with icons & names
|
// create search suggestions of apps with icons & names
|
||||||
final SimpleCursorAdapter.ViewBinder viewBinder = new SimpleCursorAdapter.ViewBinder() {
|
final SimpleCursorAdapter.ViewBinder viewBinder = new SimpleCursorAdapter.ViewBinder() {
|
||||||
@@ -97,6 +104,8 @@ public class AutofillActivity extends AppCompatActivity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final List<ApplicationInfo> allApps = pm.getInstalledApplications(0);
|
||||||
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onQueryTextSubmit(String query) {
|
public boolean onQueryTextSubmit(String query) {
|
||||||
@@ -108,7 +117,9 @@ public class AutofillActivity extends AppCompatActivity {
|
|||||||
// should be a better/faster way to do this?
|
// should be a better/faster way to do this?
|
||||||
MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_id", "package", "label"});
|
MatrixCursor matrixCursor = new MatrixCursor(new String[]{"_id", "package", "label"});
|
||||||
for (ApplicationInfo applicationInfo : allApps) {
|
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)});
|
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");
|
setTitle("Autofill Apps");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,11 @@
|
|||||||
package com.zeapo.pwdstore.autofill;
|
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.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -16,29 +20,40 @@ import java.util.ArrayList;
|
|||||||
public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder> {
|
public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecyclerAdapter.ViewHolder> {
|
||||||
private ArrayList<ApplicationInfo> apps;
|
private ArrayList<ApplicationInfo> apps;
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
|
private AutofillActivity activity;
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||||
public View view;
|
public View view;
|
||||||
public TextView name;
|
public TextView name;
|
||||||
|
public TextView secondary;
|
||||||
public ImageView icon;
|
public ImageView icon;
|
||||||
|
public String packageName;
|
||||||
|
|
||||||
public ViewHolder(View view) {
|
public ViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
this.view = view;
|
this.view = view;
|
||||||
name = (TextView) view.findViewById(R.id.app_name);
|
name = (TextView) view.findViewById(R.id.app_name);
|
||||||
|
secondary = (TextView) view.findViewById(R.id.secondary_text);
|
||||||
icon = (ImageView) view.findViewById(R.id.app_icon);
|
icon = (ImageView) view.findViewById(R.id.app_icon);
|
||||||
view.setOnClickListener(this);
|
view.setOnClickListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
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<ApplicationInfo> apps, PackageManager pm) {
|
public AutofillRecyclerAdapter(ArrayList<ApplicationInfo> apps, PackageManager pm, AutofillActivity activity) {
|
||||||
this.apps = apps;
|
this.apps = apps;
|
||||||
this.pm = pm;
|
this.pm = pm;
|
||||||
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -52,11 +67,45 @@ public class AutofillRecyclerAdapter extends RecyclerView.Adapter<AutofillRecycl
|
|||||||
public void onBindViewHolder(AutofillRecyclerAdapter.ViewHolder holder, int position) {
|
public void onBindViewHolder(AutofillRecyclerAdapter.ViewHolder holder, int position) {
|
||||||
ApplicationInfo app = apps.get(position);
|
ApplicationInfo app = apps.get(position);
|
||||||
holder.name.setText(pm.getApplicationLabel(app));
|
holder.name.setText(pm.getApplicationLabel(app));
|
||||||
|
SharedPreferences prefs
|
||||||
|
= activity.getApplicationContext().getSharedPreferences("autofill", Context.MODE_PRIVATE);
|
||||||
|
String preference = prefs.getString(app.packageName, "first");
|
||||||
|
switch (preference) {
|
||||||
|
case "first":
|
||||||
|
holder.secondary.setText("Automatically match with password");
|
||||||
|
break;
|
||||||
|
case "never":
|
||||||
|
holder.secondary.setText("Never autofill");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
holder.secondary.setText("Match with " + preference);
|
||||||
|
break;
|
||||||
|
}
|
||||||
holder.icon.setImageDrawable(pm.getApplicationIcon(app));
|
holder.icon.setImageDrawable(pm.getApplicationIcon(app));
|
||||||
|
holder.packageName = app.packageName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return apps.size();
|
return apps.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean contains(String packageName) {
|
||||||
|
for (ApplicationInfo app : apps) {
|
||||||
|
if (app.packageName.equals(packageName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -68,9 +68,13 @@ public class AutofillService extends AccessibilityService {
|
|||||||
&& event.getPackageName().equals(packageName) && unlockOK) {
|
&& event.getPackageName().equals(packageName) && unlockOK) {
|
||||||
decryptAndVerify();
|
decryptAndVerify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nothing to do if not password field focus, android version, or field is keychain app
|
||||||
if (!event.isPassword()
|
if (!event.isPassword()
|
||||||
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2
|
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2
|
||||||
|| event.getPackageName().equals("org.sufficientlysecure.keychain")) {
|
|| event.getPackageName().equals("org.sufficientlysecure.keychain")) {
|
||||||
|
// dismiss dialog if WINDOW_STATE_CHANGED unless the keyboard caused it
|
||||||
|
// there may be other exceptions...
|
||||||
if (!(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
if (!(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||||
&& event.getPackageName().toString().contains("com.android.inputmethod"))
|
&& event.getPackageName().toString().contains("com.android.inputmethod"))
|
||||||
&& dialog != null && dialog.isShowing()) {
|
&& dialog != null && dialog.isShowing()) {
|
||||||
@@ -78,14 +82,19 @@ public class AutofillService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if past this point, a new dialog will be created, so dismiss the existing
|
||||||
if (dialog != null && dialog.isShowing()) {
|
if (dialog != null && dialog.isShowing()) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore the ACTION_FOCUS from decryptAndVerify
|
// ignore the ACTION_FOCUS from decryptAndVerify
|
||||||
if (ignoreActionFocus) {
|
if (ignoreActionFocus) {
|
||||||
ignoreActionFocus = false;
|
ignoreActionFocus = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the app name and find a corresponding password
|
||||||
info = event.getSource();
|
info = event.getSource();
|
||||||
PackageManager packageManager = getPackageManager();
|
PackageManager packageManager = getPackageManager();
|
||||||
ApplicationInfo applicationInfo;
|
ApplicationInfo applicationInfo;
|
||||||
@@ -109,7 +118,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
decryptAndVerify();
|
decryptAndVerify();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setNeutralButton("Match", new DialogInterface.OnClickListener() {
|
builder.setNeutralButton("Settings", new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
|
||||||
@@ -121,7 +130,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
dialog.setTitle(items.get(0).getName());
|
dialog.setTitle(items.get(0).toString());
|
||||||
dialog.show();
|
dialog.show();
|
||||||
dialog.getWindow().setLayout(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
|
dialog.getWindow().setLayout(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
|
||||||
}
|
}
|
||||||
@@ -202,6 +211,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
case OpenPgpApi.RESULT_CODE_SUCCESS: {
|
||||||
try {
|
try {
|
||||||
String[] passContent = os.toString("UTF-8").split("\n");
|
String[] passContent = os.toString("UTF-8").split("\n");
|
||||||
|
|
||||||
// if the user focused on something else, take focus back
|
// if the user focused on something else, take focus back
|
||||||
// but this will open another dialog...hack to ignore this
|
// but this will open another dialog...hack to ignore this
|
||||||
ignoreActionFocus = info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
ignoreActionFocus = info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
||||||
@@ -215,6 +225,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
ClipData clip = ClipData.newPlainText("autofill_pm", passContent[0]);
|
ClipData clip = ClipData.newPlainText("autofill_pm", passContent[0]);
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
info.performAction(AccessibilityNodeInfo.ACTION_PASTE);
|
info.performAction(AccessibilityNodeInfo.ACTION_PASTE);
|
||||||
|
|
||||||
clip = ClipData.newPlainText("autofill_pm", "MyPasswordIsDaBest!");
|
clip = ClipData.newPlainText("autofill_pm", "MyPasswordIsDaBest!");
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
if (settings.getBoolean("clear_clipboard_20x", false)) {
|
if (settings.getBoolean("clear_clipboard_20x", false)) {
|
||||||
|
@@ -2,13 +2,15 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:focusable="true"
|
||||||
|
android:focusableInTouchMode="true">
|
||||||
|
|
||||||
<SearchView
|
<SearchView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/app_search"
|
android:id="@+id/app_search"
|
||||||
android:queryHint="Add an app to configure"
|
android:queryHint="Add an app to change its autofill setting"
|
||||||
android:iconifiedByDefault="false"/>
|
android:iconifiedByDefault="false"/>
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<android.support.v7.widget.RecyclerView
|
||||||
|
52
app/src/main/res/layout/fragment_autofill.xml
Normal file
52
app/src/main/res/layout/fragment_autofill.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin">
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/autofill_radiogroup"
|
||||||
|
>
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Automatically match with password"
|
||||||
|
android:id="@+id/first"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:checked="false"/>
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Match with..."
|
||||||
|
android:id="@+id/match"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:checked="false"
|
||||||
|
android:layout_marginTop="8dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/matched"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:editable="false"/>
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Never autofill"
|
||||||
|
android:id="@+id/never"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:checked="false"
|
||||||
|
android:layout_marginTop="8dp"/>
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@@ -74,7 +74,7 @@
|
|||||||
<PreferenceCategory android:title="Autofill">
|
<PreferenceCategory android:title="Autofill">
|
||||||
<Preference
|
<Preference
|
||||||
android:key="autofill_apps"
|
android:key="autofill_apps"
|
||||||
android:summary="Configure behaviour of autofill for specific apps."
|
android:summary="Customize autofill settings for specific apps."
|
||||||
android:title="Autofill Apps"/>
|
android:title="Autofill Apps"/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user