aboutsummaryrefslogtreecommitdiff
path: root/input/autofill/AutofillFramework
diff options
context:
space:
mode:
authorDouglas Sigelbaum <sigelbaum@google.com>2017-12-13 23:57:13 -0800
committerDouglas Sigelbaum <sigelbaum@google.com>2018-01-22 15:11:53 -0800
commitec1e19bd26615d25a38e7a8f289ca12aa68cabe6 (patch)
tree86c01da76e1c8ac10a533a73bbbd12decbf42251 /input/autofill/AutofillFramework
parent591f74c8fbea899f7590351bc48141641266349b (diff)
downloadandroid-ec1e19bd26615d25a38e7a8f289ca12aa68cabe6.tar.gz
Autofill sample: proof of concept for manual longpress.
Bug: 71907097 Test: Manual Change-Id: Ic05356100c6fd23bcf5a1915507f2f28343c1dab
Diffstat (limited to 'input/autofill/AutofillFramework')
-rw-r--r--input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java4
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml11
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java11
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java23
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java345
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java160
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java34
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java19
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java12
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java23
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java19
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java39
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java56
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java21
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java2
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java106
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java63
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java4
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHeuristics.java (renamed from input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHints.java)9
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java56
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java47
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java12
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml15
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml50
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml35
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml64
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_manual_activity.xml39
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/values/colors.xml17
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml17
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml23
-rw-r--r--input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml28
31 files changed, 1218 insertions, 146 deletions
diff --git a/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java
index e8a501d2..99d618ea 100644
--- a/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java
+++ b/input/autofill/AutofillFramework/afservice/src/androidTest/java/com/example/android/autofill/service/data/source/local/AutofillDaoTest.java
@@ -77,8 +77,8 @@ public class AutofillDaoTest {
.sort(Comparator.comparing(FilledAutofillField::getFieldType));
// When inserting a page's autofill fields.
- mDatabase.autofillDao().saveAutofillDataset(mDataset);
- mDatabase.autofillDao().saveFilledAutofillFields(
+ mDatabase.autofillDao().insertAutofillDataset(mDataset);
+ mDatabase.autofillDao().insertFilledAutofillFields(
datasetWithFilledAutofillFields.filledAutofillFields);
// Represents all hints of all fields on page.
diff --git a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml
index 1407bc06..f5d9a6d1 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml
@@ -30,6 +30,17 @@
android:name=".AuthActivity"
android:taskAffinity=".AuthActivity"
android:label="@string/authentication_name" />
+
+ <activity
+ android:name=".ManualActivity"
+ android:taskAffinity=".ManualActivity"
+ android:label="@string/manual_name" />
+
+ <activity
+ android:name=".ManualFieldPickerActivity"
+ android:taskAffinity=".ManualActivity"
+ android:label="@string/manual_field_picker_name" />
+
<!-- Including launcher icon for Autofill Settings to convenience. Not necessary for a
real service. -->
<activity
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java
index 284769dd..e1660b7c 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AuthActivity.java
@@ -43,7 +43,7 @@ import com.example.android.autofill.service.data.source.local.LocalAutofillDataS
import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.settings.MyPreferences;
import com.example.android.autofill.service.util.AppExecutors;
import com.google.gson.GsonBuilder;
@@ -149,9 +149,10 @@ public class AuthActivity extends AppCompatActivity {
AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
ClientParser clientParser = new ClientParser(structure);
mReplyIntent = new Intent();
- mLocalAutofillDataSource.getFieldTypeByAutofillHints(new DataCallback<HashMap<String, FieldTypeWithHints>>() {
+ mLocalAutofillDataSource.getFieldTypeByAutofillHints(
+ new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() {
@Override
- public void onLoaded(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint) {
+ public void onLoaded(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
ClientViewMetadataBuilder builder = new ClientViewMetadataBuilder(clientParser,
fieldTypesByAutofillHint);
mClientViewMetadata = builder.buildClientViewMetadata();
@@ -174,7 +175,7 @@ public class AuthActivity extends AppCompatActivity {
}
private void fetchDatasetAndSetIntent(
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint, String datasetName) {
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint, String datasetName) {
mLocalAutofillDataSource.getAutofillDataset(mClientViewMetadata.getAllHints(),
datasetName, new DataCallback<DatasetWithFilledAutofillFields>() {
@Override
@@ -196,7 +197,7 @@ public class AuthActivity extends AppCompatActivity {
}
private void fetchAllDatasetsAndSetIntent(
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint) {
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
mLocalAutofillDataSource.getAutofillDatasets(mClientViewMetadata.getAllHints(),
new DataCallback<List<DatasetWithFilledAutofillFields>>() {
@Override
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java
index cd4231d1..b937cc3b 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/AutofillHints.java
@@ -19,7 +19,7 @@ import android.support.annotation.NonNull;
import com.example.android.autofill.service.model.FakeData;
import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.model.FilledAutofillField;
import java.util.ArrayList;
@@ -27,7 +27,6 @@ import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
-import java.util.UUID;
import static com.example.android.autofill.service.util.Util.logd;
import static com.example.android.autofill.service.util.Util.logw;
@@ -46,10 +45,11 @@ public final class AutofillHints {
private AutofillHints() {
}
- public static FilledAutofillField generateFakeField(FieldTypeWithHints fieldTypeWithHints,
- int seed, String datasetId) {
- FakeData fakeData = fieldTypeWithHints.fieldType.getFakeData();
- String fieldTypeName = fieldTypeWithHints.fieldType.getTypeName();
+ public static FilledAutofillField generateFakeField(
+ FieldTypeWithHeuristics fieldTypeWithHeuristics, String packageName, int seed,
+ String datasetId) {
+ FakeData fakeData = fieldTypeWithHeuristics.fieldType.getFakeData();
+ String fieldTypeName = fieldTypeWithHeuristics.fieldType.getTypeName();
String text = null;
Long date = null;
Boolean toggle = null;
@@ -66,20 +66,19 @@ public final class AutofillHints {
date = Calendar.getInstance().getTimeInMillis();
}
}
- FilledAutofillField filledAutofillField = new FilledAutofillField(datasetId, fieldTypeName,
- text, date, toggle);
- boolean isNull = filledAutofillField.isNull();
+ FilledAutofillField filledAutofillField = new FilledAutofillField(datasetId,
+ packageName, fieldTypeName, text, date, toggle);
return filledAutofillField;
}
public static String getFieldTypeNameFromAutofillHints(
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
@NonNull List<String> hints) {
return getFieldTypeNameFromAutofillHints(fieldTypesByAutofillHint, hints, PARTITION_ALL);
}
public static String getFieldTypeNameFromAutofillHints(
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
@NonNull List<String> hints, int partition) {
List<String> fieldTypeNames = removePrefixes(hints)
.stream()
@@ -88,7 +87,7 @@ public final class AutofillHints {
.filter(Objects::nonNull)
.filter((fieldTypeWithHints) ->
matchesPartition(fieldTypeWithHints.fieldType.getPartition(), partition))
- .map(FieldTypeWithHints::getFieldType).map(FieldType::getTypeName)
+ .map(FieldTypeWithHeuristics::getFieldType).map(FieldType::getTypeName)
.collect(toList());
if (fieldTypeNames != null && fieldTypeNames.size() > 0) {
return fieldTypeNames.get(0);
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java
new file mode 100644
index 00000000..26d307f5
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualActivity.java
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.autofill.service;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.assist.AssistStructure;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.autofill.service.data.ClientViewMetadata;
+import com.example.android.autofill.service.data.ClientViewMetadataBuilder;
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.adapter.DatasetAdapter;
+import com.example.android.autofill.service.data.adapter.ResponseAdapter;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.AutofillDataset;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.settings.MyPreferences;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.google.common.collect.ImmutableList;
+import com.google.gson.GsonBuilder;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE;
+import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
+import static com.example.android.autofill.service.util.Util.logd;
+
+/**
+ * When the user long-presses on an autofillable field and selects "Autofill", this activity is
+ * launched to allow the user to select the dataset.
+ */
+public class ManualActivity extends AppCompatActivity {
+
+ private static final int RC_SELECT_FIELD = 1;
+
+ // Unique id for dataset intents.
+ private static int sDatasetPendingIntentId = 0;
+
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+ private DatasetAdapter mDatasetAdapter;
+ private ResponseAdapter mResponseAdapter;
+ private ClientViewMetadata mClientViewMetadata;
+ private String mPackageName;
+ private Intent mReplyIntent;
+ private MyPreferences mPreferences;
+ private List<DatasetWithFilledAutofillFields> mAllDatasets;
+ private RecyclerView mRecyclerView;
+
+ public static IntentSender getManualIntentSenderForResponse(Context context) {
+ final Intent intent = new Intent(context, ManualActivity.class);
+ return PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender();
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.multidataset_service_manual_activity);
+ SharedPreferences sharedPreferences =
+ getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(this,
+ defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
+ autofillDao, new AppExecutors());
+ mPackageName = getPackageName();
+ mPreferences = MyPreferences.getInstance(this);
+ mRecyclerView = findViewById(R.id.suggestionsList);
+ mRecyclerView.addItemDecoration(new DividerItemDecoration(this, VERTICAL));
+ mLocalAutofillDataSource.getAllAutofillDatasets(
+ new DataCallback<List<DatasetWithFilledAutofillFields>>() {
+ @Override
+ public void onLoaded(List<DatasetWithFilledAutofillFields> datasets) {
+ mAllDatasets = datasets;
+ buildAdapter();
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ private void buildAdapter() {
+ List<String> datasetIds = new ArrayList<>();
+ List<String> datasetNames = new ArrayList<>();
+ List<List<String>> allFieldTypes = new ArrayList<>();
+ for (DatasetWithFilledAutofillFields dataset : mAllDatasets) {
+ String datasetName = dataset.autofillDataset.getDatasetName();
+ String datasetId = dataset.autofillDataset.getId();
+ List<String> fieldTypes = new ArrayList<>();
+ for (FilledAutofillField filledAutofillField : dataset.filledAutofillFields) {
+ fieldTypes.add(filledAutofillField.getFieldTypeName());
+ }
+ datasetIds.add(datasetId);
+ datasetNames.add(datasetName);
+ allFieldTypes.add(fieldTypes);
+ }
+ AutofillDatasetsAdapter adapter = new AutofillDatasetsAdapter(datasetIds, datasetNames,
+ allFieldTypes, this);
+ mRecyclerView.setAdapter(adapter);
+ }
+
+ @Override
+ public void finish() {
+ if (mReplyIntent != null) {
+ setResult(RESULT_OK, mReplyIntent);
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+ super.finish();
+ }
+
+ private void onFieldSelected(FilledAutofillField field, FieldType fieldType) {
+ DatasetWithFilledAutofillFields datasetWithFilledAutofillFields = new DatasetWithFilledAutofillFields();
+ String newDatasetId = UUID.randomUUID().toString();
+ FilledAutofillField copyOfField = new FilledAutofillField(newDatasetId, getPackageName(),
+ field.getFieldTypeName(), field.getTextValue(), field.getDateValue(),
+ field.getToggleValue());
+ String datasetName = "dataset-manual";
+ AutofillDataset autofillDataset = new AutofillDataset(newDatasetId, datasetName);
+ datasetWithFilledAutofillFields.filledAutofillFields = ImmutableList.of(copyOfField);
+ datasetWithFilledAutofillFields.autofillDataset = autofillDataset;
+ Intent intent = getIntent();
+ AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
+ ClientParser clientParser = new ClientParser(structure);
+ mReplyIntent = new Intent();
+ mLocalAutofillDataSource.getFieldTypeByAutofillHints(
+ new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() {
+ @Override
+ public void onLoaded(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
+ ClientViewMetadataBuilder builder = new ClientViewMetadataBuilder(clientParser,
+ fieldTypesByAutofillHint);
+ mClientViewMetadata = builder.buildClientViewMetadata();
+ mDatasetAdapter = new DatasetAdapter(clientParser);
+ mResponseAdapter = new ResponseAdapter(ManualActivity.this,
+ mClientViewMetadata, mPackageName, mDatasetAdapter);
+ FillResponse fillResponse = mResponseAdapter.buildResponseForFocusedNode(
+ datasetName, field, fieldType);
+ setResponseIntent(fillResponse);
+ finish();
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode != RC_SELECT_FIELD || resultCode != RESULT_OK) {
+ logd("Ignoring requestCode == %d | resultCode == %d", requestCode,
+ resultCode);
+ return;
+ }
+ String datasetId = data.getStringExtra(ManualFieldPickerActivity.EXTRA_SELECTED_FIELD_DATASET_ID);
+ String fieldTypeName = data.getStringExtra(ManualFieldPickerActivity.EXTRA_SELECTED_FIELD_TYPE_NAME);
+ mLocalAutofillDataSource.getFilledAutofillField(datasetId, fieldTypeName, new DataCallback<FilledAutofillField>() {
+ @Override
+ public void onLoaded(FilledAutofillField field) {
+ mLocalAutofillDataSource.getFieldType(field.getFieldTypeName(), new DataCallback<FieldType>() {
+ @Override
+ public void onLoaded(FieldType fieldType) {
+ onFieldSelected(field, fieldType);
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+
+ private void updateHeuristics() {
+// TODO: update heuristics in data source; something like:
+// mLocalAutofillDataSource.getAutofillDataset(mClientViewMetadata.getAllHints(),
+// datasetName, new DataCallback<DatasetWithFilledAutofillFields>() {
+// @Override
+// public void onLoaded(DatasetWithFilledAutofillFields dataset) {
+// String datasetName = dataset.autofillDataset.getDatasetName();
+// RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+// mPackageName, datasetName);
+// setDatasetIntent(mDatasetAdapter.buildDataset(fieldTypesByAutofillHint,
+// dataset, remoteViews));
+// finish();
+// }
+//
+// @Override
+// public void onDataNotAvailable(String msg, Object... params) {
+// logw(msg, params);
+// finish();
+// }
+// });
+ }
+
+ private void setResponseIntent(FillResponse fillResponse) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);
+ }
+
+ private void setDatasetIntent(Dataset dataset) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
+ }
+
+ /**
+ * Adapter for the {@link RecyclerView} that holds a list of datasets.
+ */
+ private static class AutofillDatasetsAdapter extends RecyclerView.Adapter<DatasetViewHolder> {
+
+ private final List<String> mDatasetIds;
+ private final List<String> mDatasetNames;
+ private final List<List<String>> mFieldTypes;
+ private final Activity mActivity;
+
+ AutofillDatasetsAdapter(List<String> datasetIds, List<String> datasetNames,
+ List<List<String>> fieldTypes, Activity activity) {
+ mDatasetIds = datasetIds;
+ mDatasetNames = datasetNames;
+ mFieldTypes = fieldTypes;
+ mActivity = activity;
+ }
+
+ @Override
+ public DatasetViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return DatasetViewHolder.newInstance(parent, mActivity);
+ }
+
+ @Override
+ public void onBindViewHolder(final DatasetViewHolder holder, final int position) {
+ holder.bind(mDatasetIds.get(position), mDatasetNames.get(position),
+ mFieldTypes.get(position));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mDatasetNames.size();
+ }
+ }
+
+ /**
+ * Contains views needed in each row of the list of datasets.
+ */
+ private static class DatasetViewHolder extends RecyclerView.ViewHolder {
+ private final View mRootView;
+ private final TextView mDatasetNameText;
+ private final TextView mFieldTypesText;
+ private final Activity mActivity;
+
+ public DatasetViewHolder(View itemView, Activity activity) {
+ super(itemView);
+ mRootView = itemView;
+ mDatasetNameText = itemView.findViewById(R.id.datasetName);
+ mFieldTypesText = itemView.findViewById(R.id.fieldTypes);
+ mActivity = activity;
+ }
+
+ public static DatasetViewHolder newInstance(ViewGroup parent, Activity activity) {
+ return new DatasetViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.dataset_suggestion, parent, false), activity);
+ }
+
+ public void bind(String datasetId, String datasetName, List<String> fieldTypes) {
+ mDatasetNameText.setText(datasetName);
+ String firstFieldType = null;
+ String secondFieldType = null;
+ int numOfFieldTypes = 0;
+ if (fieldTypes != null) {
+ numOfFieldTypes = fieldTypes.size();
+ if (numOfFieldTypes > 0) {
+ firstFieldType = fieldTypes.get(0);
+ }
+ if (numOfFieldTypes > 1) {
+ secondFieldType = fieldTypes.get(1);
+ }
+ }
+ String fieldTypesString;
+ if (numOfFieldTypes == 1) {
+ fieldTypesString = "Contains data for " + firstFieldType + ".";
+ } else if (numOfFieldTypes == 2) {
+ fieldTypesString = "Contains data for " + firstFieldType + " and " + secondFieldType + ".";
+ } else if (numOfFieldTypes > 2) {
+ fieldTypesString = "Contains data for " + firstFieldType + ", " + secondFieldType + ", and more.";
+ } else {
+ fieldTypesString = "Ignore: Contains no data.";
+ }
+ mFieldTypesText.setText(fieldTypesString);
+ mRootView.setOnClickListener((view) -> {
+ Intent intent = ManualFieldPickerActivity.getIntent(mActivity, datasetId);
+ mActivity.startActivityForResult(intent, RC_SELECT_FIELD);
+ });
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java
new file mode 100644
index 00000000..608ac953
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/ManualFieldPickerActivity.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.autofill.service;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.example.android.autofill.service.data.DataCallback;
+import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
+import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource;
+import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
+import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
+import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
+import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.util.AppExecutors;
+import com.google.gson.GsonBuilder;
+
+import java.util.List;
+
+import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
+
+public class ManualFieldPickerActivity extends AppCompatActivity {
+ private static final String EXTRA_DATASET_ID = "extra_dataset_id";
+ public static final String EXTRA_SELECTED_FIELD_DATASET_ID = "selected_field_dataset_id";
+ public static final String EXTRA_SELECTED_FIELD_TYPE_NAME = "selected_field_type_name";
+
+ private LocalAutofillDataSource mLocalAutofillDataSource;
+
+ private RecyclerView mRecyclerView;
+ private TextView mListTitle;
+ private DatasetWithFilledAutofillFields mDataset;
+
+ public static Intent getIntent(Context originContext, String datasetId) {
+ Intent intent = new Intent(originContext, ManualFieldPickerActivity.class);
+ intent.putExtra(EXTRA_DATASET_ID, datasetId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_field_picker);
+ SharedPreferences sharedPreferences = getSharedPreferences(
+ LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE);
+ DefaultFieldTypesSource defaultFieldTypesSource =
+ DefaultFieldTypesLocalJsonSource.getInstance(getResources(),
+ new GsonBuilder().create());
+ AutofillDao autofillDao = AutofillDatabase.getInstance(this,
+ defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ String datasetId = getIntent().getStringExtra(EXTRA_DATASET_ID);
+ mRecyclerView = findViewById(R.id.fieldsList);
+ mRecyclerView.addItemDecoration(new DividerItemDecoration(this, VERTICAL));
+ mListTitle = findViewById(R.id.listTitle);
+ mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(sharedPreferences,
+ autofillDao, new AppExecutors());
+ mLocalAutofillDataSource.getAutofillDatasetWithId(datasetId,
+ new DataCallback<DatasetWithFilledAutofillFields>() {
+ @Override
+ public void onLoaded(DatasetWithFilledAutofillFields dataset) {
+ mDataset = dataset;
+ if (mDataset != null) {
+ onLoadedDataset();
+ }
+ }
+
+ @Override
+ public void onDataNotAvailable(String msg, Object... params) {
+
+ }
+ });
+ }
+
+ public void onSelectedDataset(FilledAutofillField field) {
+ Intent data = new Intent()
+ .putExtra(EXTRA_SELECTED_FIELD_DATASET_ID, field.getDatasetId())
+ .putExtra(EXTRA_SELECTED_FIELD_TYPE_NAME, field.getFieldTypeName());
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ public void onLoadedDataset() {
+ FieldsAdapter fieldsAdapter = new FieldsAdapter(this, mDataset.filledAutofillFields);
+ mRecyclerView.setAdapter(fieldsAdapter);
+ mListTitle.setText(getString(R.string.manual_data_picker_title,
+ mDataset.autofillDataset.getDatasetName()));
+ }
+
+ private static class FieldsAdapter extends RecyclerView.Adapter<FieldViewHolder> {
+ private final Activity mActivity;
+ private final List<FilledAutofillField> mFields;
+
+ public FieldsAdapter(Activity activity, List<FilledAutofillField> fields) {
+ mActivity = activity;
+ mFields = fields;
+ }
+
+ @Override
+ public FieldViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new FieldViewHolder(LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.dataset_field, parent, false), mActivity);
+ }
+
+ @Override
+ public void onBindViewHolder(FieldViewHolder holder, int position) {
+ FilledAutofillField field = mFields.get(position);
+ holder.bind(field);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mFields.size();
+ }
+ }
+
+ private static class FieldViewHolder extends RecyclerView.ViewHolder {
+ private final View mRootView;
+ private final TextView mFieldTypeText;
+ private final Activity mActivity;
+
+ public FieldViewHolder(View itemView, Activity activity) {
+ super(itemView);
+ mRootView = itemView;
+ mFieldTypeText = itemView.findViewById(R.id.fieldType);
+ mActivity = activity;
+ }
+
+ public void bind(FilledAutofillField field) {
+ mFieldTypeText.setText(field.getFieldTypeName());
+ mRootView.setOnClickListener((view) -> {
+ ((ManualFieldPickerActivity) mActivity).onSelectedDataset(field);
+ });
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java
index b50c8482..ace2cefe 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/MyAutofillService.java
@@ -28,6 +28,7 @@ import android.service.autofill.FillResponse;
import android.service.autofill.SaveCallback;
import android.service.autofill.SaveRequest;
import android.support.annotation.NonNull;
+import android.view.autofill.AutofillManager;
import android.widget.RemoteViews;
import com.example.android.autofill.service.data.AutofillDataBuilder;
@@ -48,7 +49,7 @@ import com.example.android.autofill.service.data.source.local.db.AutofillDatabas
import com.example.android.autofill.service.model.DalCheck;
import com.example.android.autofill.service.model.DalInfo;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.settings.MyPreferences;
import com.example.android.autofill.service.util.AppExecutors;
import com.example.android.autofill.service.util.Util;
@@ -56,6 +57,7 @@ import com.google.gson.GsonBuilder;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import static com.example.android.autofill.service.util.Util.DalCheckRequirement;
import static com.example.android.autofill.service.util.Util.bundleToString;
@@ -104,14 +106,14 @@ public class MyAutofillService extends AutofillService {
AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure();
ClientParser parser = new ClientParser(structures);
-
// Check user's settings for authenticating Responses and Datasets.
boolean responseAuth = mPreferences.isResponseAuth();
boolean datasetAuth = mPreferences.isDatasetAuth();
+ boolean manual = (request.getFlags() & FillRequest.FLAG_MANUAL_REQUEST) != 0;
mLocalAutofillDataSource.getFieldTypeByAutofillHints(
- new DataCallback<HashMap<String, FieldTypeWithHints>>() {
+ new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() {
@Override
- public void onLoaded(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint) {
+ public void onLoaded(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
DatasetAdapter datasetAdapter = new DatasetAdapter(parser);
ClientViewMetadataBuilder clientViewMetadataBuilder =
new ClientViewMetadataBuilder(parser, fieldTypesByAutofillHint);
@@ -132,7 +134,7 @@ public class MyAutofillService extends AutofillService {
logw("Cancel autofill not implemented in this sample.")
);
fetchDataAndGenerateResponse(fieldTypesByAutofillHint, responseAuth,
- datasetAuth, callback);
+ datasetAuth, manual, callback);
}
@Override
@@ -143,9 +145,17 @@ public class MyAutofillService extends AutofillService {
}
private void fetchDataAndGenerateResponse(
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint, boolean responseAuth,
- boolean datasetAuth, FillCallback callback) {
- if (responseAuth) {
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint, boolean responseAuth,
+ boolean datasetAuth, boolean manual, FillCallback callback) {
+ if (manual) {
+ IntentSender sender = ManualActivity.getManualIntentSenderForResponse(this);
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(getPackageName(),
+ getString(R.string.autofill_manual_prompt));
+ FillResponse response = mResponseAdapter.buildManualResponse(sender, remoteViews);
+ if (response != null) {
+ callback.onSuccess(response);
+ }
+ } else if (responseAuth) {
// If the entire Autofill Response is authenticated, AuthActivity is used
// to generate Response.
IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this);
@@ -182,10 +192,12 @@ public class MyAutofillService extends AutofillService {
AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure();
ClientParser parser = new ClientParser(structures);
mLocalAutofillDataSource.getFieldTypeByAutofillHints(
- new DataCallback<HashMap<String, FieldTypeWithHints>>() {
+ new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() {
@Override
- public void onLoaded(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint) {
- mAutofillDataBuilder = new ClientAutofillDataBuilder(fieldTypesByAutofillHint, parser);
+ public void onLoaded(
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
+ mAutofillDataBuilder = new ClientAutofillDataBuilder(
+ fieldTypesByAutofillHint, getPackageName(), parser);
ClientViewMetadataBuilder clientViewMetadataBuilder =
new ClientViewMetadataBuilder(parser, fieldTypesByAutofillHint);
mClientViewMetadata = clientViewMetadataBuilder.buildClientViewMetadata();
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java
index 0ee922f3..10865657 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientAutofillDataBuilder.java
@@ -24,10 +24,9 @@ import android.view.autofill.AutofillValue;
import com.example.android.autofill.service.AutofillHints;
import com.example.android.autofill.service.ClientParser;
import com.example.android.autofill.service.model.AutofillDataset;
-import com.example.android.autofill.service.model.AutofillHint;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.model.FilledAutofillField;
import com.google.common.collect.ImmutableList;
@@ -41,12 +40,14 @@ import static com.example.android.autofill.service.util.Util.loge;
public class ClientAutofillDataBuilder implements AutofillDataBuilder {
private final ClientParser mClientParser;
- private final HashMap<String, FieldTypeWithHints> mFieldTypesByAutofillHint;
+ private final HashMap<String, FieldTypeWithHeuristics> mFieldTypesByAutofillHint;
+ private final String mPackageName;
- public ClientAutofillDataBuilder(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
- ClientParser clientParser) {
+ public ClientAutofillDataBuilder(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
+ String packageName, ClientParser clientParser) {
mClientParser = clientParser;
mFieldTypesByAutofillHint = fieldTypesByAutofillHint;
+ mPackageName = packageName;
}
@Override
@@ -119,9 +120,9 @@ public class ClientAutofillDataBuilder implements AutofillDataBuilder {
for (int i = 0; i < hints.length; i++) {
String hint = hints[i];
// Then check if the "actual" hint is supported.
- FieldTypeWithHints fieldTypeWithHints = mFieldTypesByAutofillHint.get(hint);
- if (fieldTypeWithHints != null) {
- FieldType fieldType = fieldTypeWithHints.fieldType;
+ FieldTypeWithHeuristics fieldTypeWithHeuristics = mFieldTypesByAutofillHint.get(hint);
+ if (fieldTypeWithHeuristics != null) {
+ FieldType fieldType = fieldTypeWithHeuristics.fieldType;
if (!AutofillHints.matchesPartition(fieldType.getPartition(), partition)) {
continue;
}
@@ -150,7 +151,7 @@ public class ClientAutofillDataBuilder implements AutofillDataBuilder {
}
String datasetId = datasetWithFilledAutofillFields.autofillDataset.getId();
datasetWithFilledAutofillFields.add(new FilledAutofillField(datasetId,
- fieldType.getTypeName(), textValue, dateValue, toggleValue));
+ mPackageName, fieldType.getTypeName(), textValue, dateValue, toggleValue));
} else {
loge("Invalid hint: %s", hint);
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java
index 21508aff..85b789ef 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadata.java
@@ -32,13 +32,15 @@ public class ClientViewMetadata {
private final int mSaveType;
private final AutofillId[] mAutofillIds;
private final String mWebDomain;
+ private final AutofillId[] mFocusedIds;
public ClientViewMetadata(List<String> allHints, int saveType, AutofillId[] autofillIds,
- String webDomain) {
+ AutofillId[] focusedIds, String webDomain) {
mAllHints = allHints;
mSaveType = saveType;
mAutofillIds = autofillIds;
mWebDomain = webDomain;
+ mFocusedIds = focusedIds;
}
public List<String> getAllHints() {
@@ -49,6 +51,10 @@ public class ClientViewMetadata {
return mAutofillIds;
}
+ public AutofillId[] getFocusedIds() {
+ return mFocusedIds;
+ }
+
public int getSaveType() {
return mSaveType;
}
@@ -57,13 +63,13 @@ public class ClientViewMetadata {
return mWebDomain;
}
- @Override
- public String toString() {
+ @Override public String toString() {
return "ClientViewMetadata{" +
"mAllHints=" + mAllHints +
", mSaveType=" + mSaveType +
", mAutofillIds=" + Arrays.toString(mAutofillIds) +
", mWebDomain='" + mWebDomain + '\'' +
+ ", mFocusedIds=" + Arrays.toString(mFocusedIds) +
'}';
}
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java
index 71f931bd..e2e97878 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/ClientViewMetadataBuilder.java
@@ -21,10 +21,9 @@ import android.app.assist.AssistStructure;
import android.util.MutableInt;
import android.view.autofill.AutofillId;
-import com.example.android.autofill.service.AutofillHints;
import com.example.android.autofill.service.ClientParser;
import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import java.util.ArrayList;
import java.util.HashMap;
@@ -34,10 +33,10 @@ import static com.example.android.autofill.service.util.Util.logd;
public class ClientViewMetadataBuilder {
private ClientParser mClientParser;
- private HashMap<String, FieldTypeWithHints> mFieldTypesByAutofillHint;
+ private HashMap<String, FieldTypeWithHeuristics> mFieldTypesByAutofillHint;
public ClientViewMetadataBuilder(ClientParser parser,
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint) {
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) {
mClientParser = parser;
mFieldTypesByAutofillHint = fieldTypesByAutofillHint;
}
@@ -47,11 +46,13 @@ public class ClientViewMetadataBuilder {
MutableInt saveType = new MutableInt(0);
List<AutofillId> autofillIds = new ArrayList<>();
StringBuilder webDomainBuilder = new StringBuilder();
- mClientParser.parse((node) -> parseNode(node, allHints, saveType, autofillIds));
+ List<AutofillId> focusedAutofillIds = new ArrayList<>();
+ mClientParser.parse((node) -> parseNode(node, allHints, saveType, autofillIds, focusedAutofillIds));
mClientParser.parse((node) -> parseWebDomain(node, webDomainBuilder));
String webDomain = webDomainBuilder.toString();
AutofillId[] autofillIdsArray = autofillIds.toArray(new AutofillId[autofillIds.size()]);
- return new ClientViewMetadata(allHints, saveType.value, autofillIdsArray, webDomain);
+ AutofillId[] focusedIds = focusedAutofillIds.toArray(new AutofillId[focusedAutofillIds.size()]);
+ return new ClientViewMetadata(allHints, saveType.value, autofillIdsArray, focusedIds, webDomain);
}
private void parseWebDomain(AssistStructure.ViewNode viewNode, StringBuilder validWebDomain) {
@@ -70,17 +71,21 @@ public class ClientViewMetadataBuilder {
}
private void parseNode(AssistStructure.ViewNode root, List<String> allHints,
- MutableInt autofillSaveType, List<AutofillId> autofillIds) {
+ MutableInt autofillSaveType, List<AutofillId> autofillIds,
+ List<AutofillId> focusedAutofillIds) {
String[] hints = root.getAutofillHints();
if (hints != null) {
for (String hint : hints) {
- FieldTypeWithHints fieldTypeWithHints = mFieldTypesByAutofillHint.get(hint);
+ FieldTypeWithHeuristics fieldTypeWithHints = mFieldTypesByAutofillHint.get(hint);
if (fieldTypeWithHints != null && fieldTypeWithHints.fieldType != null) {
allHints.add(hint);
autofillSaveType.value |= fieldTypeWithHints.fieldType.getSaveInfo();
- autofillIds.add(root.getAutofillId());
}
}
}
+ if (root.isFocused()) {
+ focusedAutofillIds.add(root.getAutofillId());
+ }
+ autofillIds.add(root.getAutofillId());
}
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java
index bbe1a74e..69ee0421 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/FakeAutofillDataBuilder.java
@@ -17,10 +17,9 @@
package com.example.android.autofill.service.data;
import com.example.android.autofill.service.AutofillHints;
-import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource;
import com.example.android.autofill.service.model.AutofillDataset;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.model.FilledAutofillField;
import com.google.common.collect.ImmutableList;
@@ -28,12 +27,15 @@ import java.util.List;
import java.util.UUID;
public class FakeAutofillDataBuilder implements AutofillDataBuilder {
- private final List<FieldTypeWithHints> mFieldTypesWithHints;
+ private final List<FieldTypeWithHeuristics> mFieldTypesWithHints;
+ private final String mPackageName;
private final int mSeed;
- public FakeAutofillDataBuilder(List<FieldTypeWithHints> fieldTypesWithHints, int seed) {
+ public FakeAutofillDataBuilder(List<FieldTypeWithHeuristics> fieldTypesWithHints,
+ String packageName, int seed) {
mFieldTypesWithHints = fieldTypesWithHints;
mSeed = seed;
+ mPackageName = packageName;
}
@Override
@@ -59,11 +61,12 @@ public class FakeAutofillDataBuilder implements AutofillDataBuilder {
DatasetWithFilledAutofillFields datasetWithFilledAutofillFields =
new DatasetWithFilledAutofillFields();
datasetWithFilledAutofillFields.autofillDataset = dataset;
- for (FieldTypeWithHints fieldTypeWithHints: mFieldTypesWithHints) {
- if (AutofillHints.matchesPartition(fieldTypeWithHints.getFieldType().getPartition(),
- partition)) {
+ for (FieldTypeWithHeuristics fieldTypeWithHeuristics : mFieldTypesWithHints) {
+ if (AutofillHints.matchesPartition(
+ fieldTypeWithHeuristics.getFieldType().getPartition(), partition)) {
FilledAutofillField fakeField =
- AutofillHints.generateFakeField(fieldTypeWithHints, mSeed, dataset.getId());
+ AutofillHints.generateFakeField(fieldTypeWithHeuristics, mPackageName,
+ mSeed, dataset.getId());
datasetWithFilledAutofillFields.add(fakeField);
}
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java
index 7ad5ab3e..6f7e6036 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/DatasetAdapter.java
@@ -29,12 +29,11 @@ import com.example.android.autofill.service.AutofillHints;
import com.example.android.autofill.service.ClientParser;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.model.FilledAutofillField;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -53,18 +52,29 @@ public class DatasetAdapter {
/**
* Wraps autofill data in a {@link Dataset} object which can then be sent back to the client.
*/
- public Dataset buildDataset(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ public Dataset buildDataset(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
DatasetWithFilledAutofillFields datasetWithFilledAutofillFields,
RemoteViews remoteViews) {
return buildDataset(fieldTypesByAutofillHint, datasetWithFilledAutofillFields, remoteViews,
null);
}
+ public Dataset buildDatasetForFocusedNode(FilledAutofillField filledAutofillField,
+ FieldType fieldType, RemoteViews remoteViews) {
+ Dataset.Builder datasetBuilder = new Dataset.Builder(remoteViews);
+ boolean setAtLeastOneValue = bindDatasetToFocusedNode(filledAutofillField,
+ fieldType, datasetBuilder);
+ if (!setAtLeastOneValue) {
+ return null;
+ }
+ return datasetBuilder.build();
+ }
+
/**
* Wraps autofill data in a {@link Dataset} object with an IntentSender, which can then be
* sent back to the client.
*/
- public Dataset buildDataset(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ public Dataset buildDataset(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
DatasetWithFilledAutofillFields datasetWithFilledAutofillFields,
RemoteViews remoteViews, IntentSender intentSender) {
Dataset.Builder datasetBuilder = new Dataset.Builder(remoteViews);
@@ -82,7 +92,7 @@ public class DatasetAdapter {
/**
* Build an autofill {@link Dataset} using saved data and the client's AssistStructure.
*/
- private boolean bindDataset(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ private boolean bindDataset(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
DatasetWithFilledAutofillFields datasetWithFilledAutofillFields,
Dataset.Builder datasetBuilder) {
MutableBoolean setValueAtLeastOnce = new MutableBoolean(false);
@@ -96,8 +106,19 @@ public class DatasetAdapter {
return setValueAtLeastOnce.value;
}
+ private boolean bindDatasetToFocusedNode(FilledAutofillField field,
+ FieldType fieldType, Dataset.Builder builder) {
+ MutableBoolean setValueAtLeastOnce = new MutableBoolean(false);
+ mClientParser.parse((node) -> {
+ if (node.isFocused() && node.getAutofillId() != null) {
+ bindValueToNode(node, field, builder, setValueAtLeastOnce);
+ }
+ });
+ return setValueAtLeastOnce.value;
+ }
+
private void parseAutofillFields(AssistStructure.ViewNode viewNode,
- HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
Map<String, FilledAutofillField> filledAutofillFieldsByTypeName,
Dataset.Builder builder, MutableBoolean setValueAtLeastOnce) {
String[] rawHints = viewNode.getAutofillHints();
@@ -114,6 +135,12 @@ public class DatasetAdapter {
if (field == null) {
return;
}
+ bindValueToNode(viewNode, field, builder, setValueAtLeastOnce);
+ }
+
+ void bindValueToNode(AssistStructure.ViewNode viewNode,
+ FilledAutofillField field, Dataset.Builder builder,
+ MutableBoolean setValueAtLeastOnce) {
AutofillId autofillId = viewNode.getAutofillId();
if (autofillId == null) {
logw("Autofill ID null for %s", viewNode.toString());
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java
index bffd7cb1..cd9d0ca3 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/adapter/ResponseAdapter.java
@@ -28,7 +28,9 @@ import com.example.android.autofill.service.AuthActivity;
import com.example.android.autofill.service.RemoteViewsHelper;
import com.example.android.autofill.service.data.ClientViewMetadata;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
import java.util.HashMap;
import java.util.List;
@@ -47,11 +49,25 @@ public class ResponseAdapter {
mPackageName = packageName;
}
+ public FillResponse buildResponseForFocusedNode(String datasetName, FilledAutofillField field,
+ FieldType fieldType) {
+ FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth(
+ mPackageName, datasetName);
+ Dataset dataset = mDatasetAdapter.buildDatasetForFocusedNode(field, fieldType, remoteViews);
+ if (dataset != null) {
+ responseBuilder.addDataset(dataset);
+ return responseBuilder.build();
+ } else {
+ return null;
+ }
+ }
+
/**
* Wraps autofill data in a Response object (essentially a series of Datasets) which can then
* be sent back to the client View.
*/
- public FillResponse buildResponse(HashMap<String, FieldTypeWithHints> fieldTypesByAutofillHint,
+ public FillResponse buildResponse(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint,
List<DatasetWithFilledAutofillFields> datasets, boolean datasetAuth) {
FillResponse.Builder responseBuilder = new FillResponse.Builder();
if (datasets != null) {
@@ -81,18 +97,40 @@ public class ResponseAdapter {
}
int saveType = 0;
AutofillId[] autofillIds = mClientViewMetadata.getAutofillIds();
- SaveInfo saveInfo = new SaveInfo.Builder(saveType, autofillIds).build();
- responseBuilder.setSaveInfo(saveInfo);
- return responseBuilder.build();
+ if (autofillIds != null && autofillIds.length > 0) {
+ SaveInfo saveInfo = new SaveInfo.Builder(saveType, autofillIds).build();
+ responseBuilder.setSaveInfo(saveInfo);
+ return responseBuilder.build();
+ } else {
+ return null;
+ }
}
public FillResponse buildResponse(IntentSender sender, RemoteViews remoteViews) {
FillResponse.Builder responseBuilder = new FillResponse.Builder();
int saveType = mClientViewMetadata.getSaveType();
AutofillId[] autofillIds = mClientViewMetadata.getAutofillIds();
- SaveInfo saveInfo = new SaveInfo.Builder(saveType, autofillIds).build();
- responseBuilder.setSaveInfo(saveInfo);
- responseBuilder.setAuthentication(autofillIds, sender, remoteViews);
- return responseBuilder.build();
+ if (autofillIds != null && autofillIds.length > 0) {
+ SaveInfo saveInfo = new SaveInfo.Builder(saveType, autofillIds).build();
+ responseBuilder.setSaveInfo(saveInfo);
+ responseBuilder.setAuthentication(autofillIds, sender, remoteViews);
+ return responseBuilder.build();
+ } else {
+ return null;
+ }
+ }
+
+ public FillResponse buildManualResponse(IntentSender sender, RemoteViews remoteViews) {
+ FillResponse.Builder responseBuilder = new FillResponse.Builder();
+ int saveType = mClientViewMetadata.getSaveType();
+ AutofillId[] focusedIds = mClientViewMetadata.getFocusedIds();
+ if (focusedIds != null && focusedIds.length > 0) {
+ SaveInfo saveInfo = new SaveInfo.Builder(saveType, focusedIds).build();
+ return responseBuilder.setSaveInfo(saveInfo)
+ .setAuthentication(focusedIds, sender, remoteViews)
+ .build();
+ } else {
+ return null;
+ }
}
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java
index eb2a5ada..e3579dd8 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/AutofillDataSource.java
@@ -17,7 +17,10 @@ package com.example.android.autofill.service.data.source;
import com.example.android.autofill.service.data.DataCallback;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldType;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
+import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
import java.util.HashMap;
import java.util.List;
@@ -31,6 +34,9 @@ public interface AutofillDataSource {
void getAutofillDatasets(List<String> allAutofillHints,
DataCallback<List<DatasetWithFilledAutofillFields>> datasetsCallback);
+ void getAllAutofillDatasets(
+ DataCallback<List<DatasetWithFilledAutofillFields>> datasetsCallback);
+
/**
* Asynchronously gets a saved {@link DatasetWithFilledAutofillFields} for a specific
* {@code datasetName} that contains some objects that can autofill fields with these
@@ -45,13 +51,22 @@ public interface AutofillDataSource {
void saveAutofillDatasets(List<DatasetWithFilledAutofillFields>
datasetsWithFilledAutofillFields);
+ void saveResourceIdHeuristic(ResourceIdHeuristic resourceIdHeuristic);
+
+ /**
+ * Gets all autofill field types.
+ */
+ void getFieldTypes(DataCallback<List<FieldTypeWithHeuristics>> fieldTypesCallback);
+
/**
* Gets all autofill field types.
*/
- void getFieldTypes(DataCallback<List<FieldTypeWithHints>> fieldTypesCallback);
+ void getFieldType(String typeName, DataCallback<FieldType> fieldTypeCallback);
void getFieldTypeByAutofillHints(
- DataCallback<HashMap<String, FieldTypeWithHints>> fieldTypeMapCallback);
+ DataCallback<HashMap<String, FieldTypeWithHeuristics>> fieldTypeMapCallback);
+
+ void getFilledAutofillField(String datasetId, String fieldTypeName, DataCallback<FilledAutofillField> fieldCallback);
/**
* Clears all data.
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java
index edda7d8e..d403f320 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/DefaultFieldTypesLocalJsonSource.java
@@ -21,8 +21,6 @@ import android.content.res.Resources;
import com.example.android.autofill.service.R;
import com.example.android.autofill.service.data.source.DefaultFieldTypesSource;
import com.example.android.autofill.service.model.DefaultFieldTypeWithHints;
-import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java
index c9b80958..e58e18b8 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/LocalAutofillDataSource.java
@@ -19,7 +19,6 @@ package com.example.android.autofill.service.data.source.local;
import android.content.SharedPreferences;
import android.service.autofill.Dataset;
-import com.example.android.autofill.service.AutofillHints;
import com.example.android.autofill.service.data.DataCallback;
import com.example.android.autofill.service.data.source.AutofillDataSource;
import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
@@ -27,8 +26,9 @@ import com.example.android.autofill.service.model.AutofillDataset;
import com.example.android.autofill.service.model.AutofillHint;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
import com.example.android.autofill.service.util.AppExecutors;
import java.util.HashMap;
@@ -79,7 +79,7 @@ public class LocalAutofillDataSource implements AutofillDataSource {
mAppExecutors.diskIO().execute(() -> {
final List<String> typeNames = getFieldTypesForAutofillHints(allAutofillHints)
.stream()
- .map(FieldTypeWithHints::getFieldType)
+ .map(FieldTypeWithHeuristics::getFieldType)
.map(FieldType::getTypeName)
.collect(Collectors.toList());
List<DatasetWithFilledAutofillFields> datasetsWithFilledAutofillFields =
@@ -91,6 +91,18 @@ public class LocalAutofillDataSource implements AutofillDataSource {
}
@Override
+ public void getAllAutofillDatasets(
+ DataCallback<List<DatasetWithFilledAutofillFields>> datasetsCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ List<DatasetWithFilledAutofillFields> datasetsWithFilledAutofillFields =
+ mAutofillDao.getAllDatasets();
+ mAppExecutors.mainThread().execute(() ->
+ datasetsCallback.onLoaded(datasetsWithFilledAutofillFields)
+ );
+ });
+ }
+
+ @Override
public void getAutofillDataset(List<String> allAutofillHints, String datasetName,
DataCallback<DatasetWithFilledAutofillFields> datasetsCallback) {
mAppExecutors.diskIO().execute(() -> {
@@ -107,7 +119,9 @@ public class LocalAutofillDataSource implements AutofillDataSource {
datasetsCallback.onLoaded(dataset)
);
} else {
- datasetsCallback.onDataNotAvailable("No data found.");
+ mAppExecutors.mainThread().execute(() ->
+ datasetsCallback.onDataNotAvailable("No data found.")
+ );
}
});
}
@@ -122,44 +136,86 @@ public class LocalAutofillDataSource implements AutofillDataSource {
List<FilledAutofillField> filledAutofillFields =
datasetWithFilledAutofillFields.filledAutofillFields;
AutofillDataset autofillDataset = datasetWithFilledAutofillFields.autofillDataset;
- mAutofillDao.saveAutofillDataset(autofillDataset);
- mAutofillDao.saveFilledAutofillFields(filledAutofillFields);
+ mAutofillDao.insertAutofillDataset(autofillDataset);
+ mAutofillDao.insertFilledAutofillFields(filledAutofillFields);
}
});
incrementDatasetNumber();
}
@Override
- public void getFieldTypes(DataCallback<List<FieldTypeWithHints>> fieldTypesCallback) {
+ public void saveResourceIdHeuristic(ResourceIdHeuristic resourceIdHeuristic) {
mAppExecutors.diskIO().execute(() -> {
- List<FieldTypeWithHints> fieldTypeWithHints = mAutofillDao.getFieldTypesWithHints();
- if (fieldTypeWithHints != null) {
- fieldTypesCallback.onLoaded(fieldTypeWithHints);
- } else {
- fieldTypesCallback.onDataNotAvailable("Field Types not found.");
- }
+ mAutofillDao.insertResourceIdHeuristic(resourceIdHeuristic);
+ });
+ }
+
+ @Override
+ public void getFieldTypes(DataCallback<List<FieldTypeWithHeuristics>> fieldTypesCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ List<FieldTypeWithHeuristics> fieldTypeWithHints = mAutofillDao.getFieldTypesWithHints();
+ mAppExecutors.mainThread().execute(() -> {
+ if (fieldTypeWithHints != null) {
+ fieldTypesCallback.onLoaded(fieldTypeWithHints);
+ } else {
+ fieldTypesCallback.onDataNotAvailable("Field Types not found.");
+ }
+ });
});
}
@Override
public void getFieldTypeByAutofillHints(
- DataCallback<HashMap<String, FieldTypeWithHints>> fieldTypeMapCallback) {
+ DataCallback<HashMap<String, FieldTypeWithHeuristics>> fieldTypeMapCallback) {
mAppExecutors.diskIO().execute(() -> {
- HashMap<String, FieldTypeWithHints> hintMap = getFieldTypeByAutofillHints();
- if (hintMap != null) {
- fieldTypeMapCallback.onLoaded(hintMap);
- } else {
- fieldTypeMapCallback.onDataNotAvailable("FieldTypes not found");
- }
+ HashMap<String, FieldTypeWithHeuristics> hintMap = getFieldTypeByAutofillHints();
+ mAppExecutors.mainThread().execute(() -> {
+ if (hintMap != null) {
+ fieldTypeMapCallback.onLoaded(hintMap);
+ } else {
+ fieldTypeMapCallback.onDataNotAvailable("FieldTypes not found");
+ }
+ });
+ });
+ }
+
+ @Override
+ public void getFilledAutofillField(String datasetId, String fieldTypeName, DataCallback<FilledAutofillField> fieldCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ FilledAutofillField filledAutofillField = mAutofillDao.getFilledAutofillField(datasetId, fieldTypeName);
+ mAppExecutors.mainThread().execute(() -> {
+ fieldCallback.onLoaded(filledAutofillField);
+ });
+ });
+ }
+
+ @Override
+ public void getFieldType(String fieldTypeName, DataCallback<FieldType> fieldTypeCallback) {
+ mAppExecutors.diskIO().execute(() -> {
+ FieldType fieldType = mAutofillDao.getFieldType(fieldTypeName);
+ mAppExecutors.mainThread().execute(() -> {
+ fieldTypeCallback.onLoaded(fieldType);
+ });
+ });
+ }
+
+ public void getAutofillDatasetWithId(String datasetId,
+ DataCallback<DatasetWithFilledAutofillFields> callback) {
+ mAppExecutors.diskIO().execute(() -> {
+ DatasetWithFilledAutofillFields dataset =
+ mAutofillDao.getAutofillDatasetWithId(datasetId);
+ mAppExecutors.mainThread().execute(() -> {
+ callback.onLoaded(dataset);
+ });
});
}
- private HashMap<String, FieldTypeWithHints> getFieldTypeByAutofillHints() {
- HashMap<String, FieldTypeWithHints> hintMap = new HashMap<>();
- List<FieldTypeWithHints> fieldTypeWithHints =
+ private HashMap<String, FieldTypeWithHeuristics> getFieldTypeByAutofillHints() {
+ HashMap<String, FieldTypeWithHeuristics> hintMap = new HashMap<>();
+ List<FieldTypeWithHeuristics> fieldTypeWithHints =
mAutofillDao.getFieldTypesWithHints();
if (fieldTypeWithHints != null) {
- for (FieldTypeWithHints fieldType : fieldTypeWithHints) {
+ for (FieldTypeWithHeuristics fieldType : fieldTypeWithHints) {
for (AutofillHint hint : fieldType.autofillHints) {
hintMap.put(hint.mAutofillHint, fieldType);
}
@@ -170,7 +226,7 @@ public class LocalAutofillDataSource implements AutofillDataSource {
}
}
- private List<FieldTypeWithHints> getFieldTypesForAutofillHints(List<String> autofillHints) {
+ private List<FieldTypeWithHeuristics> getFieldTypesForAutofillHints(List<String> autofillHints) {
return mAutofillDao.getFieldTypesForAutofillHints(autofillHints);
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java
index 9baffb46..50ed0cdf 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/dao/AutofillDao.java
@@ -25,8 +25,9 @@ import com.example.android.autofill.service.model.AutofillDataset;
import com.example.android.autofill.service.model.AutofillHint;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
import com.example.android.autofill.service.model.FieldType;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
import java.util.Collection;
import java.util.List;
@@ -44,46 +45,82 @@ public interface AutofillDao {
" AND FilledAutofillField.fieldTypeName IN (:allAutofillHints)")
List<DatasetWithFilledAutofillFields> getDatasets(List<String> allAutofillHints);
+ @Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
+ " WHERE AutofillDataset.id = FilledAutofillField.datasetId")
+ List<DatasetWithFilledAutofillFields> getAllDatasets();
+
/**
* Fetches a list of datasets associated to autofill fields. It should only return a dataset
* if that dataset has an autofill field associate with the view the user is focused on, and
* if that dataset's name matches the name passed in.
*
- * @param allAutofillHints Filtering parameter; represents all of the hints associated with
+ * @param fieldTypes Filtering parameter; represents all of the field types associated with
* all of the views on the page.
* @param datasetName Filtering parameter; only return datasets with this name.
*/
@Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
" WHERE AutofillDataset.id = FilledAutofillField.datasetId" +
" AND AutofillDataset.datasetName = (:datasetName)" +
- " AND FilledAutofillField.fieldTypeName IN (:allAutofillHints)")
+ " AND FilledAutofillField.fieldTypeName IN (:fieldTypes)")
List<DatasetWithFilledAutofillFields> getDatasetsWithName(
- List<String> allAutofillHints, String datasetName);
+ List<String> fieldTypes, String datasetName);
+
+ @Query("SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, AutofillHint" +
+ " WHERE FieldType.typeName = AutofillHint.fieldTypeName" +
+ " UNION " +
+ "SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, ResourceIdHeuristic" +
+ " WHERE FieldType.typeName = ResourceIdHeuristic.fieldTypeName")
+ List<FieldTypeWithHeuristics> getFieldTypesWithHints();
+
+ @Query("SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, AutofillHint, ResourceIdHeuristic" +
+ " WHERE FieldType.typeName = AutofillHint.fieldTypeName" +
+ " AND AutofillHint.autofillHint IN (:autofillHints)" +
+ " UNION " +
+ "SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, " +
+ "textTemplate, dateTemplate" +
+ " FROM FieldType, ResourceIdHeuristic" +
+ " WHERE FieldType.typeName = ResourceIdHeuristic.fieldTypeName")
+ List<FieldTypeWithHeuristics> getFieldTypesForAutofillHints(List<String> autofillHints);
- @Query("SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, textTemplate, dateTemplate FROM FieldType, AutofillHint WHERE " +
- "FieldType.typeName = AutofillHint.fieldTypeName")
- List<FieldTypeWithHints> getFieldTypesWithHints();
+ @Query("SELECT DISTINCT id, datasetName FROM FilledAutofillField, AutofillDataset" +
+ " WHERE AutofillDataset.id = FilledAutofillField.datasetId" +
+ " AND AutofillDataset.id = (:datasetId)")
+ DatasetWithFilledAutofillFields getAutofillDatasetWithId(String datasetId);
+
+ @Query("SELECT * FROM FilledAutofillField" +
+ " WHERE FilledAutofillField.datasetId = (:datasetId)" +
+ " AND FilledAutofillField.fieldTypeName = (:fieldTypeName)")
+ FilledAutofillField getFilledAutofillField(String datasetId, String fieldTypeName);
- @Query("SELECT DISTINCT typeName, autofillTypes, saveInfo, partition, strictExampleSet, textTemplate, dateTemplate FROM FieldType, AutofillHint WHERE " +
- "FieldType.typeName = AutofillHint.fieldTypeName " +
- "AND AutofillHint.autofillHint IN (:autofillHints)")
- List<FieldTypeWithHints> getFieldTypesForAutofillHints(List<String> autofillHints);
+ @Query("SELECT * FROM FieldType" +
+ " WHERE FieldType.typeName = (:fieldTypeName)")
+ FieldType getFieldType(String fieldTypeName);
/**
* @param autofillFields Collection of autofill fields to be saved to the db.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
- void saveFilledAutofillFields(Collection<FilledAutofillField> autofillFields);
+ void insertFilledAutofillFields(Collection<FilledAutofillField> autofillFields);
@Insert(onConflict = OnConflictStrategy.REPLACE)
- void saveAutofillDataset(AutofillDataset datasets);
+ void insertAutofillDataset(AutofillDataset datasets);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAutofillHints(List<AutofillHint> autofillHints);
@Insert(onConflict = OnConflictStrategy.REPLACE)
+ void insertResourceIdHeuristic(ResourceIdHeuristic resourceIdHeuristic);
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
void insertFieldTypes(List<FieldType> fieldTypes);
+
@Query("DELETE FROM AutofillDataset")
void clearAll();
} \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java
index 178e375d..fe007ced 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/data/source/local/db/AutofillDatabase.java
@@ -32,6 +32,7 @@ import com.example.android.autofill.service.model.DefaultFieldTypeWithHints;
import com.example.android.autofill.service.model.FakeData;
import com.example.android.autofill.service.model.FieldType;
import com.example.android.autofill.service.model.FilledAutofillField;
+import com.example.android.autofill.service.model.ResourceIdHeuristic;
import com.example.android.autofill.service.util.AppExecutors;
import java.util.ArrayList;
@@ -44,7 +45,8 @@ import static java.util.stream.Collectors.toList;
FilledAutofillField.class,
AutofillDataset.class,
FieldType.class,
- AutofillHint.class
+ AutofillHint.class,
+ ResourceIdHeuristic.class
}, version = 1)
@TypeConverters({Converters.class})
public abstract class AutofillDatabase extends RoomDatabase {
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHints.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHeuristics.java
index 308a50b5..97a646e5 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHints.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FieldTypeWithHeuristics.java
@@ -21,13 +21,16 @@ import android.arch.persistence.room.Relation;
import java.util.List;
-public class FieldTypeWithHints {
+public class FieldTypeWithHeuristics {
@Embedded
public FieldType fieldType;
@Relation(parentColumn = "typeName", entityColumn = "fieldTypeName", entity = AutofillHint.class)
public List<AutofillHint> autofillHints;
+ @Relation(parentColumn = "typeName", entityColumn = "fieldTypeName", entity = ResourceIdHeuristic.class)
+ public List<ResourceIdHeuristic> resourceIdHeuristics;
+
public FieldType getFieldType() {
return fieldType;
}
@@ -35,4 +38,8 @@ public class FieldTypeWithHints {
public List<AutofillHint> getAutofillHints() {
return autofillHints;
}
+
+ public List<ResourceIdHeuristic> getResourceIdHeuristics() {
+ return resourceIdHeuristics;
+ }
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java
index 5a71c15a..1eed720a 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/FilledAutofillField.java
@@ -51,9 +51,15 @@ public class FilledAutofillField {
@ColumnInfo(name = "fieldTypeName")
private final String mFieldTypeName;
- public FilledAutofillField(@NonNull String datasetId, @NonNull String fieldTypeName,
- @Nullable String textValue, @Nullable Long dateValue, @Nullable Boolean toggleValue) {
+ @NonNull
+ @ColumnInfo(name = "packageName")
+ private final String mPackageName;
+
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String packageName,
+ @NonNull String fieldTypeName, @Nullable String textValue, @Nullable Long dateValue,
+ @Nullable Boolean toggleValue) {
mDatasetId = datasetId;
+ mPackageName = packageName;
mFieldTypeName = fieldTypeName;
mTextValue = textValue;
mDateValue = dateValue;
@@ -61,32 +67,33 @@ public class FilledAutofillField {
}
@Ignore
- public FilledAutofillField(@NonNull String datasetId, @NonNull String hint,
- @Nullable String textValue, @Nullable Long dateValue) {
- this(datasetId, hint, textValue, dateValue, null);
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String packageName,
+ @NonNull String fieldTypeName, @Nullable String textValue, @Nullable Long dateValue) {
+ this(datasetId, packageName, fieldTypeName, textValue, dateValue, null);
}
@Ignore
- public FilledAutofillField(@NonNull String datasetId, @NonNull String hint,
- @Nullable String textValue) {
- this(datasetId, hint, textValue, null, null);
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String packageName,
+ @NonNull String fieldTypeName, @Nullable String textValue) {
+ this(datasetId, packageName, fieldTypeName, textValue, null, null);
}
@Ignore
- public FilledAutofillField(@NonNull String datasetId, @NonNull String hint,
- @Nullable Long dateValue) {
- this(datasetId, hint, null, dateValue, null);
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String packageName,
+ @NonNull String fieldTypeName, @Nullable Long dateValue) {
+ this(datasetId, packageName, fieldTypeName, null, dateValue, null);
}
@Ignore
- public FilledAutofillField(@NonNull String datasetId, @NonNull String hint,
- @Nullable Boolean toggleValue) {
- this(datasetId, hint, null, null, toggleValue);
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String packageName,
+ @NonNull String fieldTypeName, @Nullable Boolean toggleValue) {
+ this(datasetId, packageName, fieldTypeName, null, null, toggleValue);
}
@Ignore
- public FilledAutofillField(@NonNull String datasetId, @NonNull String hint) {
- this(datasetId, hint, null, null, null);
+ public FilledAutofillField(@NonNull String datasetId, @NonNull String packageName,
+ @NonNull String fieldTypeName) {
+ this(datasetId, packageName, fieldTypeName, null, null, null);
}
@NonNull
@@ -114,6 +121,11 @@ public class FilledAutofillField {
return mFieldTypeName;
}
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
public boolean isNull() {
return mTextValue == null && mDateValue == null && mToggleValue == null;
}
@@ -125,19 +137,25 @@ public class FilledAutofillField {
FilledAutofillField that = (FilledAutofillField) o;
+ if (!mDatasetId.equals(that.mDatasetId)) return false;
if (mTextValue != null ? !mTextValue.equals(that.mTextValue) : that.mTextValue != null)
return false;
if (mDateValue != null ? !mDateValue.equals(that.mDateValue) : that.mDateValue != null)
return false;
- return mToggleValue != null ? mToggleValue.equals(that.mToggleValue) :
- that.mToggleValue == null;
+ if (mToggleValue != null ? !mToggleValue.equals(that.mToggleValue) : that.mToggleValue != null)
+ return false;
+ if (!mFieldTypeName.equals(that.mFieldTypeName)) return false;
+ return mPackageName.equals(that.mPackageName);
}
@Override
public int hashCode() {
- int result = mTextValue != null ? mTextValue.hashCode() : 0;
+ int result = mDatasetId.hashCode();
+ result = 31 * result + (mTextValue != null ? mTextValue.hashCode() : 0);
result = 31 * result + (mDateValue != null ? mDateValue.hashCode() : 0);
result = 31 * result + (mToggleValue != null ? mToggleValue.hashCode() : 0);
+ result = 31 * result + mFieldTypeName.hashCode();
+ result = 31 * result + mPackageName.hashCode();
return result;
}
}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java
new file mode 100644
index 00000000..81c9abef
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/model/ResourceIdHeuristic.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.autofill.service.model;
+
+import android.arch.persistence.room.ColumnInfo;
+import android.arch.persistence.room.Entity;
+import android.arch.persistence.room.ForeignKey;
+import android.support.annotation.NonNull;
+
+@Entity(primaryKeys = {"resourceIdHeuristic", "packageName"}, foreignKeys = @ForeignKey(
+ entity = FieldType.class, parentColumns = "typeName", childColumns = "fieldTypeName",
+ onDelete = ForeignKey.CASCADE))
+public class ResourceIdHeuristic {
+
+ @NonNull
+ @ColumnInfo(name = "resourceIdHeuristic")
+ public String mResourceIdHeuristic;
+
+ @NonNull
+ @ColumnInfo(name = "packageName")
+ public String mPackageName;
+
+ @NonNull
+ @ColumnInfo(name = "fieldTypeName")
+ public String mFieldTypeName;
+
+ public ResourceIdHeuristic(@NonNull String resourceIdHeuristic, @NonNull String fieldTypeName,
+ @NonNull String packageName) {
+ mResourceIdHeuristic = resourceIdHeuristic;
+ mFieldTypeName = fieldTypeName;
+ mPackageName = packageName;
+ }
+}
diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java
index a45a99ff..1fd87df0 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java
+++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/settings/SettingsActivity.java
@@ -49,7 +49,7 @@ import com.example.android.autofill.service.data.source.local.SharedPrefsPackage
import com.example.android.autofill.service.data.source.local.dao.AutofillDao;
import com.example.android.autofill.service.data.source.local.db.AutofillDatabase;
import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields;
-import com.example.android.autofill.service.model.FieldTypeWithHints;
+import com.example.android.autofill.service.model.FieldTypeWithHeuristics;
import com.example.android.autofill.service.util.AppExecutors;
import com.example.android.autofill.service.util.Util;
import com.google.gson.GsonBuilder;
@@ -69,6 +69,7 @@ public class SettingsActivity extends AppCompatActivity {
private LocalAutofillDataSource mLocalAutofillDataSource;
private PackageVerificationDataSource mPackageVerificationDataSource;
private MyPreferences mPreferences;
+ private String mPackageName;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -81,6 +82,7 @@ public class SettingsActivity extends AppCompatActivity {
new GsonBuilder().create());
AutofillDao autofillDao = AutofillDatabase.getInstance(
this, defaultFieldTypesSource, new AppExecutors()).autofillDao();
+ mPackageName = getPackageName();
mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(localAfDataSourceSharedPrefs,
autofillDao, new AppExecutors());
mAutofillManager = getSystemService(AutofillManager.class);
@@ -204,9 +206,9 @@ public class SettingsActivity extends AppCompatActivity {
.setView(numberOfDatasetsPicker)
.setPositiveButton(R.string.settings_ok, (dialog, which) -> {
int numOfDatasets = numberOfDatasetsPicker.getValue();
- mLocalAutofillDataSource.getFieldTypes(new DataCallback<List<FieldTypeWithHints>>() {
+ mLocalAutofillDataSource.getFieldTypes(new DataCallback<List<FieldTypeWithHeuristics>>() {
@Override
- public void onLoaded(List<FieldTypeWithHints> fieldTypes) {
+ public void onLoaded(List<FieldTypeWithHeuristics> fieldTypes) {
boolean saved = buildAndSaveMockedAutofillFieldCollections(
fieldTypes, numOfDatasets);
dialog.dismiss();
@@ -228,7 +230,7 @@ public class SettingsActivity extends AppCompatActivity {
.create();
}
- public boolean buildAndSaveMockedAutofillFieldCollections(List<FieldTypeWithHints> fieldTypes,
+ public boolean buildAndSaveMockedAutofillFieldCollections(List<FieldTypeWithHeuristics> fieldTypes,
int numOfDatasets) {
if (numOfDatasets < 0 || numOfDatasets > 10) {
logw("Number of Datasets (%d) out of range.", numOfDatasets);
@@ -236,7 +238,7 @@ public class SettingsActivity extends AppCompatActivity {
for (int i = 0; i < numOfDatasets; i++) {
int datasetNumber = mLocalAutofillDataSource.getDatasetNumber();
AutofillDataBuilder autofillDataBuilder =
- new FakeAutofillDataBuilder(fieldTypes, datasetNumber);
+ new FakeAutofillDataBuilder(fieldTypes, mPackageName, datasetNumber);
List<DatasetWithFilledAutofillFields> datasetsWithFilledAutofillFields =
autofillDataBuilder.buildDatasetsByPartition(datasetNumber);
// Save datasets to database.
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml b/input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml
new file mode 100644
index 00000000..3d6d1624
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/drawable/list_divider.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (c) 2017 Google Inc.
+ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ in compliance with the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software distributed under the License
+ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing permissions and limitations under
+ the License.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <size android:height="1dp" android:width="1dp" />
+ <solid android:color="@color/light_grey" />
+</shape> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml
new file mode 100644
index 00000000..949dbfb9
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/activity_field_picker.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/activity_vertical_margin">
+
+ <TextView
+ android:id="@+id/listTitle"
+ style="@style/Manual.Header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:text="@string/manual_data_picker_title"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/fieldsList"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginTop="@dimen/spacing_large"
+ android:background="@null"
+ android:elevation="@dimen/card_elevation"
+ android:listDivider="@drawable/list_divider"
+ android:orientation="vertical"
+ android:scrollbarStyle="insideOverlay"
+ android:scrollbars="vertical"
+ app:layoutManager="LinearLayoutManager"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ tools:listitem="@layout/dataset_field" />
+</android.support.constraint.ConstraintLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml
new file mode 100644
index 00000000..b441dba8
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_field.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/spacing_large"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <TextView
+ style="@style/Manual.Content"
+ android:id="@+id/fieldType"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/manual_dataset_name_label"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:text="Username" />
+
+</android.support.constraint.ConstraintLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml
new file mode 100644
index 00000000..030de0a1
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/dataset_suggestion.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="@+id/fieldTypes"
+ android:src="@drawable/ic_person_black_24dp"/>
+
+ <TextView
+ style="@style/Manual.Header"
+ android:id="@+id/datasetNameLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/manual_dataset_name_label"
+ app:layout_constraintStart_toEndOf="@+id/icon"
+ app:layout_constraintTop_toTopOf="parent" />
+ <TextView
+ style="@style/Manual.Content"
+ android:id="@+id/datasetName"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="@+id/datasetNameLabel"
+ app:layout_constraintTop_toBottomOf="@+id/datasetNameLabel"
+ tools:text="dataset-1"/>
+ <TextView
+ style="@style/Manual.Header"
+ android:id="@+id/fieldTypesLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/manual_field_types_label"
+ app:layout_constraintStart_toStartOf="@+id/datasetName"
+ app:layout_constraintTop_toBottomOf="@+id/datasetName"/>
+ <TextView
+ style="@style/Manual.Content"
+ android:id="@+id/fieldTypes"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toStartOf="@+id/fieldTypesLabel"
+ app:layout_constraintTop_toBottomOf="@+id/fieldTypesLabel"
+ tools:text="Username, password, and more."/>
+
+</android.support.constraint.ConstraintLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_manual_activity.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_manual_activity.xml
new file mode 100644
index 00000000..56117aa7
--- /dev/null
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/multidataset_service_manual_activity.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+<android.support.constraint.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/activity_vertical_margin"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/suggestionsList"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ android:background="@null"
+ android:elevation="@dimen/card_elevation"
+ android:listDivider="@drawable/list_divider"
+ android:orientation="vertical"
+ android:scrollbarStyle="insideOverlay"
+ android:scrollbars="vertical"
+ app:layoutManager="LinearLayoutManager"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ tools:listitem="@layout/dataset_suggestion" />
+</android.support.constraint.ConstraintLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/colors.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/colors.xml
index 3ab3e9cb..4d80b9fc 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/res/values/colors.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/colors.xml
@@ -1,6 +1,21 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
+ <color name="light_grey">#ffeeeeee</color>
</resources>
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml
index d451580d..8a21b708 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/dimens.xml
@@ -1,4 +1,18 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
<resources>
<dimen name="padding_normal">16dp</dimen>
<dimen name="activity_horizontal_margin">16dp</dimen>
@@ -7,4 +21,5 @@
<dimen name="spacing_large">32dp</dimen>
<dimen name="a11y_min_touch_target_dimen">48dp</dimen>
<dimen name="text_field_width">250sp</dimen>
+ <dimen name="card_elevation">2dp</dimen>
</resources> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml
index da0e0a8c..e7b1f880 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/strings.xml
@@ -1,9 +1,27 @@
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
<resources>
<string name="invalid_package_signature">Invalid package signature</string>
<string name="security_exception">Web domain security exception.</string>
<string name="dal_exception">DAL verification failure.</string>
<string name="autofill_sign_in_prompt">Tap to sign in.</string>
+ <string name="autofill_manual_prompt">Tap to manually select data.</string>
<string name="authentication_name">Autofill Authentication</string>
+ <string name="manual_name">Manual (Auto)fill</string>
+ <string name="manual_field_picker_name">Select Field</string>
<string name="settings_name">Autofill Settings</string>
<string name="settings_cancel">Cancel</string>
<string name="settings_save">Save</string>
@@ -48,7 +66,10 @@
<string name="auth_password_label">Password</string>
<string name="auth_cancel">Cancel</string>
<string name="auth_login_label">Login</string>
- <string name="settings_ok">Ok</string>
+ <string name="settings_ok">OK</string>
+ <string name="manual_dataset_name_label">Dataset Name</string>
+ <string name="manual_field_types_label">Field Types</string>
+ <string name="manual_data_picker_title">Fields for %1$s</string>
<string-array name="month_array">
<item>1</item>
<item>2</item>
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml b/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml
index bed03729..a7e7bf2f 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/values/styles.xml
@@ -1,3 +1,18 @@
+<!--
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
<resources>
<!-- Base application theme. -->
@@ -34,6 +49,19 @@
<item name="android:paddingStart">?android:listPreferredItemPaddingStart</item>
<item name="android:paddingBottom">@dimen/spacing_normal</item>
<item name="android:paddingTop">@dimen/spacing_large</item>
+ </style>
+ <style name="Manual.Header" parent="@style/TextAppearance.AppCompat.Large">
+ <item name="android:paddingStart">?android:listPreferredItemPaddingStart</item>
+ <item name="android:paddingEnd">?android:listPreferredItemPaddingEnd</item>
+ <item name="android:paddingBottom">@dimen/spacing_normal</item>
+ <item name="android:paddingTop">@dimen/spacing_normal</item>
+ </style>
+
+ <style name="Manual.Content" parent="@style/TextAppearance.AppCompat.Medium">
+ <item name="android:paddingStart">?android:listPreferredItemPaddingStart</item>
+ <item name="android:paddingEnd">?android:listPreferredItemPaddingEnd</item>
+ <item name="android:paddingBottom">@dimen/spacing_normal</item>
+ <item name="android:paddingTop">@dimen/spacing_normal</item>
</style>
</resources>