cleanup: split autofill service into functions

This commit is contained in:
Matthew Wong 2015-11-04 16:56:12 -05:00
parent f783ad84ee
commit 54a05daa82
2 changed files with 55 additions and 40 deletions

View file

@ -65,6 +65,7 @@ public class AutofillService extends AccessibilityService {
serviceConnection.bindToService(); serviceConnection.bindToService();
settings = PreferenceManager.getDefaultSharedPreferences(this); settings = PreferenceManager.getDefaultSharedPreferences(this);
} }
// TODO change search/search results (just use first result) // TODO change search/search results (just use first result)
@Override @Override
public void onAccessibilityEvent(AccessibilityEvent event) { public void onAccessibilityEvent(AccessibilityEvent event) {
@ -84,30 +85,17 @@ public class AutofillService extends AccessibilityService {
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")) {
// the default keyboard showing/hiding is a window state changed event dismissDialog(event);
// on Android 5+ we can use getWindows() to determine when the original window is not visible
// on Android 4.3 we have to use window state changed events and filter out the keyboard ones
// there may be other exceptions...
boolean dismiss;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
dismiss = !getWindows().contains(window);
} else {
dismiss = !(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& event.getPackageName().toString().contains("inputmethod"));
}
if (dismiss && dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
return; return;
} }
if (dialog != null && dialog.isShowing()) { if (dialog != null && dialog.isShowing()) {
// if the view was clicked, the click event follows the focus event // the current dialog must belong to this window; ignore clicks on this password field
// since the focus event was already handled, ignore click event // why handle clicks at all then? some cases e.g. Paypal there is no initial focus event
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) { if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
return; return;
} }
// if past this point, a new dialog will be created, so dismiss the existing // if it was not a click, the field was refocused or another field was focused; recreate
dialog.dismiss(); dialog.dismiss();
} }
@ -119,7 +107,7 @@ public class AutofillService extends AccessibilityService {
info = event.getSource(); info = event.getSource();
// save the dialog's corresponding window so we can use getWindows() above to check whether dismiss // save the dialog's corresponding window so we can use getWindows() in dismissDialog
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window = info.getWindow(); window = info.getWindow();
} }
@ -134,10 +122,37 @@ public class AutofillService extends AccessibilityService {
} }
final String appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString(); final String appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString();
getMatchingPassword(appName, info.getPackageName().toString());
if (items.isEmpty()) {
return;
}
showDialog(appName);
}
// dismiss the dialog if the window has changed
private void dismissDialog(AccessibilityEvent event) {
// the default keyboard showing/hiding is a window state changed event
// on Android 5+ we can use getWindows() to determine when the original window is not visible
// on Android 4.3 we have to use window state changed events and filter out the keyboard ones
// there may be other exceptions...
boolean dismiss;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
dismiss = !getWindows().contains(window);
} else {
dismiss = !(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
&& event.getPackageName().toString().contains("inputmethod"));
}
if (dismiss && dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
}
private void getMatchingPassword(String appName, String packageName) {
// if autofill_default is checked and prefs.getString DNE, 'Automatically match with password'/"first" otherwise "never" // if autofill_default is checked and prefs.getString DNE, 'Automatically match with password'/"first" otherwise "never"
String defValue = settings.getBoolean("autofill_default", true) ? "/first" : "/never"; String defValue = settings.getBoolean("autofill_default", true) ? "/first" : "/never";
SharedPreferences prefs = getSharedPreferences("autofill", Context.MODE_PRIVATE); SharedPreferences prefs = getSharedPreferences("autofill", Context.MODE_PRIVATE);
String preference = prefs.getString(event.getPackageName().toString(), defValue); String preference = prefs.getString(packageName, defValue);
switch (preference) { switch (preference) {
case "/first": case "/first":
if (!PasswordRepository.isInitialized()) { if (!PasswordRepository.isInitialized()) {
@ -146,6 +161,7 @@ public class AutofillService extends AccessibilityService {
items = recursiveFilter(appName, null); items = recursiveFilter(appName, null);
break; break;
case "/never": case "/never":
items.clear();
return; return;
default: default:
if (!PasswordRepository.isInitialized()) { if (!PasswordRepository.isInitialized()) {
@ -156,10 +172,25 @@ public class AutofillService extends AccessibilityService {
items = new ArrayList<>(); items = new ArrayList<>();
items.add(PasswordItem.newPassword(file.getName(), file, PasswordRepository.getRepositoryDirectory(this))); items.add(PasswordItem.newPassword(file.getName(), file, PasswordRepository.getRepositoryDirectory(this)));
} }
if (items.isEmpty()) {
return;
} }
private ArrayList<PasswordItem> recursiveFilter(String filter, File dir) {
ArrayList<PasswordItem> items = new ArrayList<>();
ArrayList<PasswordItem> passwordItems = dir == null ?
PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(this)) :
PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(this));
for (PasswordItem item : passwordItems) {
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
items.addAll(recursiveFilter(filter, item.getFile()));
}
if (item.toString().toLowerCase().contains(filter.toLowerCase())) {
items.add(item);
}
}
return items;
}
private void showDialog(final String appName) {
if (dialog == null) { if (dialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog); AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog);
builder.setNegativeButton(R.string.dialog_cancel, null); builder.setNegativeButton(R.string.dialog_cancel, null);
@ -191,22 +222,6 @@ public class AutofillService extends AccessibilityService {
dialog.show(); dialog.show();
} }
private ArrayList<PasswordItem> recursiveFilter(String filter, File dir) {
ArrayList<PasswordItem> items = new ArrayList<>();
ArrayList<PasswordItem> passwordItems = dir == null ?
PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(this)) :
PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(this));
for (PasswordItem item : passwordItems) {
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
items.addAll(recursiveFilter(filter, item.getFile()));
}
if (item.toString().toLowerCase().contains(filter.toLowerCase())) {
items.add(item);
}
}
return items;
}
@Override @Override
public void onInterrupt() { public void onInterrupt() {

View file

@ -1,7 +1,7 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/autofill_description" android:description="@string/autofill_description"
android:accessibilityEventTypes="typeViewFocused|typeViewClicked|typeWindowStateChanged" android:accessibilityEventTypes="typeViewFocused|typeViewClicked|typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility" android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows"
android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100" android:notificationTimeout="100"
android:canRetrieveWindowContent="true" android:canRetrieveWindowContent="true"