diff options
author | Douglas Sigelbaum <sigelbaum@google.com> | 2017-12-13 23:57:13 -0800 |
---|---|---|
committer | Douglas Sigelbaum <sigelbaum@google.com> | 2018-01-22 15:11:53 -0800 |
commit | ec1e19bd26615d25a38e7a8f289ca12aa68cabe6 (patch) | |
tree | 86c01da76e1c8ac10a533a73bbbd12decbf42251 /input | |
parent | 591f74c8fbea899f7590351bc48141641266349b (diff) | |
download | android-ec1e19bd26615d25a38e7a8f289ca12aa68cabe6.tar.gz |
Autofill sample: proof of concept for manual longpress.
Bug: 71907097
Test: Manual
Change-Id: Ic05356100c6fd23bcf5a1915507f2f28343c1dab
Diffstat (limited to 'input')
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> |