mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-09-01 14:55:19 +00:00
search for webview recursively (& search files instead of passworditems)
This commit is contained in:
committed by
Matthew Wong
parent
b78465b744
commit
28379439de
@@ -1,5 +1,6 @@
|
|||||||
package com.zeapo.pwdstore.autofill;
|
package com.zeapo.pwdstore.autofill;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.accessibilityservice.AccessibilityService;
|
import android.accessibilityservice.AccessibilityService;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
@@ -10,11 +11,10 @@ import android.content.Intent;
|
|||||||
import android.content.SharedPreferences;
|
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.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.provider.Settings;
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
@@ -38,6 +38,7 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class AutofillService extends AccessibilityService {
|
public class AutofillService extends AccessibilityService {
|
||||||
@@ -69,17 +70,32 @@ public class AutofillService extends AccessibilityService {
|
|||||||
// 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) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SYSTEM_ALERT_WINDOW)
|
||||||
|
== PackageManager.PERMISSION_DENIED) {
|
||||||
|
// may need a way to request the permission but only activities can, so by notification?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if returning to the source app from a successful AutofillActivity
|
// if returning to the source app from a successful AutofillActivity
|
||||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||||
&& event.getPackageName().equals(packageName) && resultData != null) {
|
&& event.getPackageName().equals(packageName) && resultData != null) {
|
||||||
bindDecryptAndVerify();
|
bindDecryptAndVerify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// need to see if window has a WebView every time, so future events are sent?
|
||||||
|
AccessibilityNodeInfo source = event.getSource();
|
||||||
|
if (source == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
searchWebView(source);
|
||||||
|
|
||||||
// nothing to do if not password field focus, android version, or field is keychain app
|
// nothing to do if not password field focus, android version, or field is keychain app
|
||||||
if (!event.isPassword()
|
if (!event.isPassword()
|
||||||
|
|| event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
|
||||||
|| 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")) {
|
||||||
dismissDialog(event);
|
dismissDialog(event);
|
||||||
|
source.recycle(); // is this necessary???
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,6 +103,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
// the current dialog must belong to this window; ignore clicks on this password field
|
// the current dialog must belong to this window; ignore clicks on this password field
|
||||||
// why handle clicks at all then? some cases e.g. Paypal there is no initial focus 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) {
|
||||||
|
source.recycle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// if it was not a click, the field was refocused or another field was focused; recreate
|
// if it was not a click, the field was refocused or another field was focused; recreate
|
||||||
@@ -96,20 +113,13 @@ public class AutofillService extends AccessibilityService {
|
|||||||
// ignore the ACTION_FOCUS from decryptAndVerify otherwise dialog will appear after Fill
|
// ignore the ACTION_FOCUS from decryptAndVerify otherwise dialog will appear after Fill
|
||||||
if (ignoreActionFocus) {
|
if (ignoreActionFocus) {
|
||||||
ignoreActionFocus = false;
|
ignoreActionFocus = false;
|
||||||
|
source.recycle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to request permission before attempting to draw dialog
|
// we are now going to attempt to fill, save AccessibilityNodeInfo for later in decryptAndVerify
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
// (there should be a proper way to do this, although this seems to work 90% of the time)
|
||||||
&& !Settings.canDrawOverlays(this)) {
|
info = source;
|
||||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
|
||||||
Uri.parse("package:" + getApplicationContext().getPackageName()));
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
startActivity(intent);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info = event.getSource();
|
|
||||||
|
|
||||||
// save the dialog's corresponding window so we can use getWindows() in dismissDialog
|
// 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) {
|
||||||
@@ -126,7 +136,7 @@ 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());
|
setMatchingPasswords(appName, info.getPackageName().toString());
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -134,6 +144,24 @@ public class AutofillService extends AccessibilityService {
|
|||||||
showDialog(appName);
|
showDialog(appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean searchWebView(AccessibilityNodeInfo source) {
|
||||||
|
for (int i = 0; i < source.getChildCount(); i++) {
|
||||||
|
AccessibilityNodeInfo u = source.getChild(i);
|
||||||
|
if (u == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// this is not likely to always work
|
||||||
|
if (u.getContentDescription() != null && u.getContentDescription().equals("Web View")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (searchWebView(u)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
u.recycle();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// dismiss the dialog if the window has changed
|
// dismiss the dialog if the window has changed
|
||||||
private void dismissDialog(AccessibilityEvent event) {
|
private void dismissDialog(AccessibilityEvent event) {
|
||||||
// the default keyboard showing/hiding is a window state changed event
|
// the default keyboard showing/hiding is a window state changed event
|
||||||
@@ -152,7 +180,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getMatchingPassword(String appName, String packageName) {
|
private void setMatchingPasswords(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);
|
||||||
@@ -162,7 +190,10 @@ public class AutofillService extends AccessibilityService {
|
|||||||
if (!PasswordRepository.isInitialized()) {
|
if (!PasswordRepository.isInitialized()) {
|
||||||
PasswordRepository.initialize(this);
|
PasswordRepository.initialize(this);
|
||||||
}
|
}
|
||||||
items = recursiveFilter(appName, null);
|
items = new ArrayList<>();
|
||||||
|
for (File file : searchPasswords(PasswordRepository.getRepositoryDirectory(this), appName)) {
|
||||||
|
items.add(PasswordItem.newPassword(file.getName(), file, PasswordRepository.getRepositoryDirectory(this)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "/never":
|
case "/never":
|
||||||
items.clear();
|
items.clear();
|
||||||
@@ -178,17 +209,24 @@ public class AutofillService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ArrayList<PasswordItem> recursiveFilter(String filter, File dir) {
|
private ArrayList<File> searchPasswords(File path, String appName) {
|
||||||
ArrayList<PasswordItem> items = new ArrayList<>();
|
ArrayList<File> passList
|
||||||
ArrayList<PasswordItem> passwordItems = dir == null ?
|
= PasswordRepository.getFilesList(path);
|
||||||
PasswordRepository.getPasswords(PasswordRepository.getRepositoryDirectory(this)) :
|
|
||||||
PasswordRepository.getPasswords(dir, PasswordRepository.getRepositoryDirectory(this));
|
if (passList.size() == 0) return new ArrayList<>();
|
||||||
for (PasswordItem item : passwordItems) {
|
|
||||||
if (item.getType() == PasswordItem.TYPE_CATEGORY) {
|
ArrayList<File> items = new ArrayList<>();
|
||||||
items.addAll(recursiveFilter(filter, item.getFile()));
|
|
||||||
}
|
for (File file : passList) {
|
||||||
if (item.toString().toLowerCase().contains(filter.toLowerCase())) {
|
if (file.isFile()) {
|
||||||
items.add(item);
|
if (file.toString().toLowerCase().contains(appName.toLowerCase())) {
|
||||||
|
items.add(file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ignore .git directory
|
||||||
|
if (file.getName().equals(".git"))
|
||||||
|
continue;
|
||||||
|
items.addAll(searchPasswords(file, appName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
@@ -281,6 +319,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
|
|
||||||
// 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
|
||||||
|
// & need to ensure performAction correct (i.e. what is info now?)
|
||||||
ignoreActionFocus = info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
ignoreActionFocus = info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
@@ -302,6 +341,7 @@ public class AutofillService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
info.recycle();
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (UnsupportedEncodingException e) {
|
||||||
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
Log.e(Constants.TAG, "UnsupportedEncodingException", e);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user