mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-08-31 22:35:17 +00:00
Fill in field immediately after user unlocks
This commit is contained in:
@@ -7,7 +7,7 @@ import android.os.Bundle;
|
|||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
// blank activity started by service for calling startIntentSenderForResult
|
||||||
public class AutofillActivity extends AppCompatActivity {
|
public class AutofillActivity extends AppCompatActivity {
|
||||||
public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
|
public static final int REQUEST_CODE_DECRYPT_AND_VERIFY = 9913;
|
||||||
private boolean bound = false;
|
private boolean bound = false;
|
||||||
@@ -27,9 +27,9 @@ public class AutofillActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
finish();
|
finish(); // go back to the password field app
|
||||||
if (resultCode == RESULT_OK) {
|
if (resultCode == RESULT_OK) {
|
||||||
AutofillService.getService().decryptAndVerify();
|
AutofillService.setUnlockOK(); // report the result to service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -40,29 +40,40 @@ public class AutofillService extends AccessibilityService {
|
|||||||
private OpenPgpServiceConnection serviceConnection;
|
private OpenPgpServiceConnection serviceConnection;
|
||||||
private SharedPreferences settings;
|
private SharedPreferences settings;
|
||||||
private AccessibilityNodeInfo info; // the original source of the event (the edittext field)
|
private AccessibilityNodeInfo info; // the original source of the event (the edittext field)
|
||||||
private ArrayList<PasswordItem> items;
|
private ArrayList<PasswordItem> items; // password choices
|
||||||
private static AutofillService service;
|
private AlertDialog dialog;
|
||||||
|
private static boolean unlockOK = false; // if openkeychain user interaction was successful
|
||||||
|
private static CharSequence packageName;
|
||||||
|
private static boolean ignoreActionFocus = false;
|
||||||
|
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
public static final String TAG = "Keychain";
|
public static final String TAG = "Keychain";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setUnlockOK() { unlockOK = true; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onServiceConnected() {
|
protected void onServiceConnected() {
|
||||||
super.onServiceConnected();
|
super.onServiceConnected();
|
||||||
serviceConnection = new OpenPgpServiceConnection(AutofillService.this, "org.sufficientlysecure.keychain");
|
serviceConnection = new OpenPgpServiceConnection(AutofillService.this, "org.sufficientlysecure.keychain");
|
||||||
serviceConnection.bindToService();
|
serviceConnection.bindToService();
|
||||||
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
service = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AutofillService getService() {
|
|
||||||
return service;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||||
if (!event.isPassword() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||||
|
&& event.getPackageName().equals(packageName) && unlockOK) {
|
||||||
|
decryptAndVerify();
|
||||||
|
}
|
||||||
|
if (!event.isPassword()
|
||||||
|
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2
|
||||||
|
|| (dialog != null && dialog.isShowing())
|
||||||
|
|| event.getPackageName().equals("org.sufficientlysecure.keychain")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ignoreActionFocus) {
|
||||||
|
ignoreActionFocus = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
info = event.getSource();
|
info = event.getSource();
|
||||||
@@ -74,29 +85,23 @@ public class AutofillService extends AccessibilityService {
|
|||||||
applicationInfo = null;
|
applicationInfo = null;
|
||||||
}
|
}
|
||||||
String appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString();
|
String appName = (applicationInfo != null ? packageManager.getApplicationLabel(applicationInfo) : "").toString();
|
||||||
if (appName.equals("OpenKeychain")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
items = recursiveFilter(appName, null);
|
items = recursiveFilter(appName, null);
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ArrayList<CharSequence> itemNames = new ArrayList<>();
|
|
||||||
for (PasswordItem item : items) {
|
if (dialog == null) {
|
||||||
itemNames.add(item.toString());
|
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Light_Dialog_Alert);
|
||||||
|
builder.setNegativeButton("Cancel", null);
|
||||||
|
builder.setView(R.layout.autofill_layout);
|
||||||
|
dialog = builder.create();
|
||||||
|
dialog.setTitle("Fill with Password Store");
|
||||||
|
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
|
||||||
|
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||||
|
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
||||||
}
|
}
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Light_Dialog_Alert);
|
|
||||||
builder.setNegativeButton("Cancel", null);
|
|
||||||
builder.setView(R.layout.autofill_layout);
|
|
||||||
final AlertDialog dialog = builder.create();
|
|
||||||
|
|
||||||
dialog.setTitle("Fill with Password Store");
|
|
||||||
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
|
|
||||||
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
|
|
||||||
dialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
((Button) dialog.findViewById(R.id.button)).setText(itemNames.get(0).toString());
|
((Button) dialog.findViewById(R.id.button)).setText(items.get(0).getName());
|
||||||
dialog.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
|
dialog.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@@ -131,6 +136,8 @@ public class AutofillService extends AccessibilityService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void decryptAndVerify() {
|
public void decryptAndVerify() {
|
||||||
|
unlockOK = false;
|
||||||
|
packageName = info.getPackageName();
|
||||||
Intent data = new Intent();
|
Intent data = new Intent();
|
||||||
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
@@ -147,16 +154,16 @@ 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
|
||||||
|
// but this will open another dialog...hack to ignore this
|
||||||
|
ignoreActionFocus = true;
|
||||||
|
info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
// if the user focused on something else, take focus back
|
|
||||||
// but this will open another dialog...
|
|
||||||
info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
|
args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
|
||||||
passContent[0]);
|
passContent[0]);
|
||||||
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
|
info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
|
||||||
} else {
|
} else {
|
||||||
info.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
|
|
||||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
ClipData clip = ClipData.newPlainText("autofill_pm", passContent[0]);
|
ClipData clip = ClipData.newPlainText("autofill_pm", passContent[0]);
|
||||||
clipboard.setPrimaryClip(clip);
|
clipboard.setPrimaryClip(clip);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<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"
|
android:accessibilityEventTypes="typeViewFocused|typeWindowStateChanged"
|
||||||
android:accessibilityFlags="flagDefault"
|
android:accessibilityFlags="flagDefault"
|
||||||
android:accessibilityFeedbackType="feedbackGeneric"
|
android:accessibilityFeedbackType="feedbackGeneric"
|
||||||
android:notificationTimeout="100"
|
android:notificationTimeout="100"
|
||||||
|
Reference in New Issue
Block a user