183 lines
7.2 KiB
Java
183 lines
7.2 KiB
Java
package org.julianfamily.keepassgo;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Intent;
|
|
import android.os.Bundle;
|
|
import android.text.Editable;
|
|
import android.text.TextWatcher;
|
|
import android.util.TypedValue;
|
|
import android.view.Gravity;
|
|
import android.view.View;
|
|
import android.view.autofill.AutofillId;
|
|
import android.view.autofill.AutofillManager;
|
|
import android.view.autofill.AutofillValue;
|
|
import android.widget.AdapterView;
|
|
import android.widget.ArrayAdapter;
|
|
import android.widget.EditText;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ListView;
|
|
import android.widget.TextView;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
|
|
import android.service.autofill.Dataset;
|
|
|
|
public final class KeePassGOAutofillPickerActivity extends Activity {
|
|
static final String EXTRA_TARGET = "org.julianfamily.keepassgo.extra.AUTOFILL_TARGET";
|
|
static final String EXTRA_PACKAGE_NAME = "org.julianfamily.keepassgo.extra.AUTOFILL_PACKAGE";
|
|
static final String EXTRA_USERNAME_ID = "org.julianfamily.keepassgo.extra.USERNAME_ID";
|
|
static final String EXTRA_PASSWORD_ID = "org.julianfamily.keepassgo.extra.PASSWORD_ID";
|
|
|
|
private final List<AutofillCacheStore.Entry> allEntries = new ArrayList<>();
|
|
private final List<AutofillCacheStore.Entry> visibleEntries = new ArrayList<>();
|
|
private ArrayAdapter<String> adapter;
|
|
private String matchTarget = "";
|
|
private String packageName = "";
|
|
private AutofillId usernameID;
|
|
private AutofillId passwordID;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
|
|
Intent intent = getIntent();
|
|
matchTarget = intent.getStringExtra(EXTRA_TARGET);
|
|
packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
|
|
usernameID = intent.getParcelableExtra(EXTRA_USERNAME_ID, AutofillId.class);
|
|
passwordID = intent.getParcelableExtra(EXTRA_PASSWORD_ID, AutofillId.class);
|
|
|
|
LinearLayout root = new LinearLayout(this);
|
|
root.setOrientation(LinearLayout.VERTICAL);
|
|
int padding = dp(16);
|
|
root.setPadding(padding, padding, padding, padding);
|
|
|
|
TextView title = new TextView(this);
|
|
title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
|
|
title.setGravity(Gravity.START);
|
|
title.setPadding(0, 0, 0, dp(8));
|
|
title.setText(packageName == null || packageName.trim().isEmpty() ? "Search KeePassGO" : "Search KeePassGO for " + packageName);
|
|
root.addView(title, new LinearLayout.LayoutParams(
|
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
));
|
|
|
|
EditText search = new EditText(this);
|
|
search.setHint("Search entries");
|
|
root.addView(search, new LinearLayout.LayoutParams(
|
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
));
|
|
|
|
allEntries.clear();
|
|
allEntries.addAll(AutofillCacheStore.chooserCandidates(this, matchTarget));
|
|
visibleEntries.clear();
|
|
visibleEntries.addAll(allEntries);
|
|
|
|
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, labelsFor(visibleEntries));
|
|
ListView list = new ListView(this);
|
|
list.setAdapter(adapter);
|
|
list.setOnItemClickListener(this::onEntrySelected);
|
|
|
|
TextView empty = new TextView(this);
|
|
empty.setPadding(0, dp(16), 0, 0);
|
|
empty.setText("No KeePassGO entries are available for autofill.");
|
|
empty.setVisibility(allEntries.isEmpty() ? View.VISIBLE : View.GONE);
|
|
list.setVisibility(allEntries.isEmpty() ? View.GONE : View.VISIBLE);
|
|
root.addView(empty, new LinearLayout.LayoutParams(
|
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
LinearLayout.LayoutParams.WRAP_CONTENT
|
|
));
|
|
LinearLayout.LayoutParams listParams = new LinearLayout.LayoutParams(
|
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
|
0
|
|
);
|
|
listParams.weight = 1f;
|
|
listParams.topMargin = dp(12);
|
|
root.addView(list, listParams);
|
|
|
|
search.addTextChangedListener(new TextWatcher() {
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
filterEntries(s == null ? "" : s.toString());
|
|
}
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
}
|
|
});
|
|
|
|
setContentView(root);
|
|
}
|
|
|
|
private void filterEntries(String rawQuery) {
|
|
String query = rawQuery == null ? "" : rawQuery.trim().toLowerCase(Locale.US);
|
|
visibleEntries.clear();
|
|
if (query.isEmpty()) {
|
|
visibleEntries.addAll(allEntries);
|
|
} else {
|
|
for (AutofillCacheStore.Entry entry : allEntries) {
|
|
if (entryLabel(entry).toLowerCase(Locale.US).contains(query)) {
|
|
visibleEntries.add(entry);
|
|
}
|
|
}
|
|
}
|
|
adapter.clear();
|
|
adapter.addAll(labelsFor(visibleEntries));
|
|
adapter.notifyDataSetChanged();
|
|
}
|
|
|
|
private void onEntrySelected(AdapterView<?> parent, View view, int position, long id) {
|
|
if (position < 0 || position >= visibleEntries.size() || passwordID == null) {
|
|
setResult(Activity.RESULT_CANCELED);
|
|
finish();
|
|
return;
|
|
}
|
|
AutofillCacheStore.Entry entry = visibleEntries.get(position);
|
|
if (matchTarget != null && matchTarget.startsWith("androidapp://")) {
|
|
AutofillBindingStore.rememberBinding(this, matchTarget, entry.id);
|
|
}
|
|
|
|
Dataset.Builder dataset = new Dataset.Builder();
|
|
dataset.setId(entry.id);
|
|
if (usernameID != null && entry.username != null && !entry.username.isEmpty()) {
|
|
dataset.setValue(usernameID, AutofillValue.forText(entry.username));
|
|
}
|
|
dataset.setValue(passwordID, AutofillValue.forText(entry.password));
|
|
|
|
Intent result = new Intent();
|
|
result.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, dataset.build());
|
|
setResult(Activity.RESULT_OK, result);
|
|
finish();
|
|
}
|
|
|
|
private static List<String> labelsFor(List<AutofillCacheStore.Entry> entries) {
|
|
List<String> labels = new ArrayList<>(entries.size());
|
|
for (AutofillCacheStore.Entry entry : entries) {
|
|
labels.add(entryLabel(entry));
|
|
}
|
|
return labels;
|
|
}
|
|
|
|
private static String entryLabel(AutofillCacheStore.Entry entry) {
|
|
StringBuilder label = new StringBuilder();
|
|
label.append(entry.title == null || entry.title.trim().isEmpty() ? "Untitled entry" : entry.title.trim());
|
|
if (entry.username != null && !entry.username.trim().isEmpty()) {
|
|
label.append(" (").append(entry.username.trim()).append(")");
|
|
}
|
|
if (entry.path != null && !entry.path.isEmpty()) {
|
|
label.append(" • ").append(String.join(" / ", entry.path));
|
|
}
|
|
return label.toString();
|
|
}
|
|
|
|
private int dp(int value) {
|
|
return Math.round(value * getResources().getDisplayMetrics().density);
|
|
}
|
|
}
|