diff options
author | Felipe Leme <felipeal@google.com> | 2018-09-25 15:28:58 -0700 |
---|---|---|
committer | Felipe Leme <felipeal@google.com> | 2018-09-25 16:15:30 -0700 |
commit | f74de0e61e9b9f0bf13a5b0e2860e7fbdb5307be (patch) | |
tree | 3c1f494ea351aa11ee7ae1324245402107ea7a7c /input/autofill | |
parent | 0c6c93dc1003eeecb7321862a71913523ef8be52 (diff) | |
download | android-f74de0e61e9b9f0bf13a5b0e2860e7fbdb5307be.tar.gz |
Changes on Autofill Service project:
- Renamed BasicHeuristicsService to HeuristicsService
- Split HeuristicsService from BasicService
- Added compat-mode support to Chromium
- Supported authentication on HeuristicsService
- Changed value to "N-hint" to make it easier to filter out datasets
- Added settings to HeuristicService
- Made BasicService final, as it's kind of the "smallest" service possible
Bug: 114236837
Test: ./gradlew :afservice:installDebug
Change-Id: Ie36a8009d5ef39b9f82117f47a86ba11a9a4a9c1
Diffstat (limited to 'input/autofill')
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml | 11 | ||||
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java | 109 | ||||
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java | 42 | ||||
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/HeuristicsService.java | 309 | ||||
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/SimpleAuthActivity.java | 117 | ||||
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/res/layout/simple_service_auth_activity.xml | 47 | ||||
-rw-r--r-- | input/autofill/AutofillFramework/afservice/src/main/res/xml/heuristics_service.xml (renamed from input/autofill/AutofillFramework/afservice/src/main/res/xml/basic_heuristics_service.xml) | 10 |
7 files changed, 503 insertions, 142 deletions
diff --git a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml index 6ece581e..8c0b7750 100644 --- a/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml +++ b/input/autofill/AutofillFramework/afservice/src/main/AndroidManifest.xml @@ -36,12 +36,12 @@ </service> <service - android:name=".simple.BasicHeuristicsService" - android:label="Basic Heuristics Autofill Service" + android:name=".simple.HeuristicsService" + android:label="Heuristics Autofill Service" android:permission="android.permission.BIND_AUTOFILL_SERVICE"> <meta-data android:name="android.autofill" - android:resource="@xml/basic_heuristics_service"/> + android:resource="@xml/heuristics_service"/> <intent-filter> <action android:name="android.service.autofill.AutofillService" /> </intent-filter> @@ -53,6 +53,11 @@ android:label="@string/authentication_name" /> <activity + android:name=".simple.SimpleAuthActivity" + android:taskAffinity=".simple.SimpleAuthActivity" + android:label="@string/authentication_name" /> + + <activity android:name=".ManualActivity" android:taskAffinity=".ManualActivity" android:label="@string/manual_name" /> diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java deleted file mode 100644 index 81aac2b6..00000000 --- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicHeuristicsService.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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.simple; - -import android.app.assist.AssistStructure.ViewNode; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; - -import com.example.android.autofill.service.MyAutofillService; - -/** - * A basic service that uses some rudimentary heuristics to identify fields that are not explicitly - * marked with autofill hints. - * - * <p>The goal of this class is to provide a simple autofill service implementation that is easy - * to understand and extend, but it should <strong>not</strong> be used as-is on real apps because - * it lacks fundamental security requirements such as data partitioning and package verification - * &mdashthese requirements are fullfilled by {@link MyAutofillService}. * - */ -public class BasicHeuristicsService extends BasicService { - - private static final String TAG = "BasicHeuristicsService"; - - @Override - @Nullable - protected String getHint(@NonNull ViewNode node) { - - // First try the explicit autofill hints... - - String hint = super.getHint(node); - if (hint != null) return hint; - - // Then try some rudimentary heuristics based on other node properties - - String viewHint = node.getHint(); - hint = inferHint(viewHint); - if (hint != null) { - Log.d(TAG, "Found hint using view hint(" + viewHint + "): " + hint); - return hint; - } else if (!TextUtils.isEmpty(viewHint)) { - Log.v(TAG, "No hint using view hint: " + viewHint); - } - - String resourceId = node.getIdEntry(); - hint = inferHint(resourceId); - if (hint != null) { - Log.d(TAG, "Found hint using resourceId(" + resourceId + "): " + hint); - return hint; - } else if (!TextUtils.isEmpty(resourceId)) { - Log.v(TAG, "No hint using resourceId: " + resourceId); - } - - CharSequence text = node.getText(); - CharSequence className = node.getClassName(); - if (text != null && className != null && className.toString().contains("EditText")) { - hint = inferHint(text.toString()); - if (hint != null) { - // NODE: text should not be logged, as it could contain PII - Log.d(TAG, "Found hint using text(" + text + "): " + hint); - return hint; - } - } else if (!TextUtils.isEmpty(text)) { - // NODE: text should not be logged, as it could contain PII - Log.v(TAG, "No hint using text: " + text + " and class " + className); - } - return null; - } - - /** - * Uses heuristics to infer an autofill hint from a {@code string}. - * - * @return standard autofill hint, or {@code null} when it could not be inferred. - */ - @Nullable - protected String inferHint(@Nullable String string) { - if (string == null) return null; - - string = string.toLowerCase(); - if (string.contains("label")) { - Log.v(TAG, "Ignroing 'label' hint: " + string); - return null; - } - if (string.contains("password")) return View.AUTOFILL_HINT_PASSWORD; - if (string.contains("username") - || (string.contains("login") && string.contains("id"))) - return View.AUTOFILL_HINT_USERNAME; - if (string.contains("email")) return View.AUTOFILL_HINT_EMAIL_ADDRESS; - if (string.contains("name")) return View.AUTOFILL_HINT_NAME; - if (string.contains("phone")) return View.AUTOFILL_HINT_PHONE; - - return null; - } -} diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java index 73210beb..16937e74 100644 --- a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java +++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/BasicService.java @@ -53,7 +53,7 @@ import java.util.Map.Entry; * it lacks fundamental security requirements such as data partitioning and package verification * &mdashthese requirements are fullfilled by {@link MyAutofillService}. */ -public class BasicService extends AutofillService { +public final class BasicService extends AutofillService { private static final String TAG = "BasicService"; @@ -88,9 +88,9 @@ public class BasicService extends AutofillService { for (Entry<String, AutofillId> field : fields.entrySet()) { String hint = field.getKey(); AutofillId id = field.getValue(); - String value = hint + i; - // We're simple - our dataset values are hardcoded as "hintN" (for example, - // "username1", "username2") and they're displayed as such, except if they're a + String value = i + "-" + hint; + // We're simple - our dataset values are hardcoded as "N-hint" (for example, + // "1-username", "2-username") and they're displayed as such, except if they're a // password String displayValue = hint.contains("password") ? "password for #" + i : value; RemoteViews presentation = newDatasetPresentation(packageName, displayValue); @@ -141,15 +141,19 @@ public class BasicService extends AutofillService { */ private void addAutofillableFields(@NonNull Map<String, AutofillId> fields, @NonNull ViewNode node) { - int type = node.getAutofillType(); - String hint = getHint(node); + String[] hints = node.getAutofillHints(); + if (hints == null) return; + + // We're simple, we only care about the first hint + String hint = hints[0].toLowerCase(); + if (hint != null) { AutofillId id = node.getAutofillId(); if (!fields.containsKey(hint)) { - Log.v(TAG, "Setting hint " + hint + " on " + id); + Log.v(TAG, "Setting hint '" + hint + "' on " + id); fields.put(hint, id); } else { - Log.v(TAG, "Ignoring hint " + hint + " on " + id + Log.v(TAG, "Ignoring hint '" + hint + "' on " + id + " because it was already set"); } } @@ -160,29 +164,11 @@ public class BasicService extends AutofillService { } /** - * Gets the autofill hint associated with the given node. - * - * <p>By default it just return the first entry on the node's - * {@link ViewNode#getAutofillHints() autofillHints} (when available), but subclasses could - * extend it to use heuristics when the app developer didn't explicitly provide these hints. - * - */ - @Nullable - protected String getHint(@NonNull ViewNode node) { - String[] hints = node.getAutofillHints(); - if (hints == null) return null; - - // We're simple, we only care about the first hint - String hint = hints[0].toLowerCase(); - return hint; - } - - /** * Helper method to get the {@link AssistStructure} associated with the latest request * in an autofill context. */ @NonNull - private static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) { + static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) { List<FillContext> fillContexts = request.getFillContexts(); return fillContexts.get(fillContexts.size() - 1).getStructure(); } @@ -191,7 +177,7 @@ public class BasicService extends AutofillService { * Helper method to create a dataset presentation with the given text. */ @NonNull - private static RemoteViews newDatasetPresentation(@NonNull String packageName, + static RemoteViews newDatasetPresentation(@NonNull String packageName, @NonNull CharSequence text) { RemoteViews presentation = new RemoteViews(packageName, R.layout.multidataset_service_list_item); diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/HeuristicsService.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/HeuristicsService.java new file mode 100644 index 00000000..e19288e3 --- /dev/null +++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/HeuristicsService.java @@ -0,0 +1,309 @@ +/* + * 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.simple; + +import static com.example.android.autofill.service.simple.BasicService.getLatestAssistStructure; +import static com.example.android.autofill.service.simple.BasicService.newDatasetPresentation; + +import android.app.assist.AssistStructure; +import android.app.assist.AssistStructure.ViewNode; +import android.content.Context; +import android.content.IntentSender; +import android.os.CancellationSignal; +import android.service.autofill.AutofillService; +import android.service.autofill.Dataset; +import android.service.autofill.FillCallback; +import android.service.autofill.FillRequest; +import android.service.autofill.FillResponse; +import android.service.autofill.SaveCallback; +import android.service.autofill.SaveInfo; +import android.service.autofill.SaveRequest; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; +import android.view.View; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.widget.RemoteViews; +import android.widget.Toast; + +import com.example.android.autofill.service.MyAutofillService; +import com.example.android.autofill.service.settings.MyPreferences; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +/** + * A basic service that uses some rudimentary heuristics to identify fields that are not explicitly + * marked with autofill hints. + * + * <p>The goal of this class is to provide a simple autofill service implementation that is easy + * to understand and extend, but it should <strong>not</strong> be used as-is on real apps because + * it lacks fundamental security requirements such as data partitioning and package verification + * &mdashthese requirements are fullfilled by {@link MyAutofillService}. + */ +public class HeuristicsService extends AutofillService { + + private static final String TAG = "HeuristicsService"; + + private boolean mAuthenticateResponses; + private boolean mAuthenticateDatasets; + private int mNumberDatasets = 4; + + @Override + public void onConnected() { + super.onConnected(); + + // TODO(b/114236837): use its own preferences? + MyPreferences pref = MyPreferences.getInstance(getApplicationContext()); + mAuthenticateResponses = pref.isResponseAuth(); + mAuthenticateDatasets = pref.isDatasetAuth(); + // TODO(b/114236837): get number dataset from preferences + + Log.d(TAG, "onConnected(): numberDatasets=" + mNumberDatasets + + ", authResponses=" + mAuthenticateResponses + + ", authDatasets=" + mAuthenticateDatasets); + } + + @Override + public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, + FillCallback callback) { + Log.d(TAG, "onFillRequest()"); + + // Find autofillable fields + AssistStructure structure = getLatestAssistStructure(request); + ArrayMap<String, AutofillId> fields = getAutofillableFields(structure); + Log.d(TAG, "autofillable fields:" + fields); + + if (fields.isEmpty()) { + toast("No autofill hints found"); + callback.onSuccess(null); + return; + } + + // Create response... + FillResponse response; + if (mAuthenticateResponses) { + int size = fields.size(); + String[] hints = new String[size]; + AutofillId[] ids = new AutofillId[size]; + for (int i = 0; i < size; i++) { + hints[i] = fields.keyAt(i); + ids[i] = fields.valueAt(i); + } + + IntentSender authentication = SimpleAuthActivity.newIntentSenderForResponse(this, hints, + ids, mAuthenticateDatasets); + RemoteViews presentation = newDatasetPresentation(getPackageName(), + "Tap to auth response"); + + response = new FillResponse.Builder() + .setAuthentication(ids, authentication, presentation).build(); + } else { + response = createResponse(this, fields, mNumberDatasets,mAuthenticateDatasets); + } + + // ... and return it + callback.onSuccess(response); + } + + @Override + public void onSaveRequest(SaveRequest request, SaveCallback callback) { + Log.d(TAG, "onSaveRequest()"); + toast("Save not supported"); + callback.onSuccess(); + } + + /** + * Parses the {@link AssistStructure} representing the activity being autofilled, and returns a + * map of autofillable fields (represented by their autofill ids) mapped by the hint associate + * with them. + * + * <p>An autofillable field is a {@link ViewNode} whose {@link #getHint(ViewNode)} metho + */ + @NonNull + private ArrayMap<String, AutofillId> getAutofillableFields(@NonNull AssistStructure structure) { + ArrayMap<String, AutofillId> fields = new ArrayMap<>(); + int nodes = structure.getWindowNodeCount(); + for (int i = 0; i < nodes; i++) { + ViewNode node = structure.getWindowNodeAt(i).getRootViewNode(); + addAutofillableFields(fields, node); + } + return fields; + } + + /** + * Adds any autofillable view from the {@link ViewNode} and its descendants to the map. + */ + private void addAutofillableFields(@NonNull Map<String, AutofillId> fields, + @NonNull ViewNode node) { + String hint = getHint(node); + if (hint != null) { + AutofillId id = node.getAutofillId(); + if (!fields.containsKey(hint)) { + Log.v(TAG, "Setting hint '" + hint + "' on " + id); + fields.put(hint, id); + } else { + Log.v(TAG, "Ignoring hint '" + hint + "' on " + id + + " because it was already set"); + } + } + int childrenSize = node.getChildCount(); + for (int i = 0; i < childrenSize; i++) { + addAutofillableFields(fields, node.getChildAt(i)); + } + } + + @Nullable + protected String getHint(@NonNull ViewNode node) { + + // First try the explicit autofill hints... + + String[] hints = node.getAutofillHints(); + if (hints != null) { + // We're simple, we only care about the first hint + return hints[0].toLowerCase(); + } + + // Then try some rudimentary heuristics based on other node properties + + String viewHint = node.getHint(); + String hint = inferHint(viewHint); + if (hint != null) { + Log.d(TAG, "Found hint using view hint(" + viewHint + "): " + hint); + return hint; + } else if (!TextUtils.isEmpty(viewHint)) { + Log.v(TAG, "No hint using view hint: " + viewHint); + } + + String resourceId = node.getIdEntry(); + hint = inferHint(resourceId); + if (hint != null) { + Log.d(TAG, "Found hint using resourceId(" + resourceId + "): " + hint); + return hint; + } else if (!TextUtils.isEmpty(resourceId)) { + Log.v(TAG, "No hint using resourceId: " + resourceId); + } + + CharSequence text = node.getText(); + CharSequence className = node.getClassName(); + if (text != null && className != null && className.toString().contains("EditText")) { + hint = inferHint(text.toString()); + if (hint != null) { + // NODE: text should not be logged, as it could contain PII + Log.d(TAG, "Found hint using text(" + text + "): " + hint); + return hint; + } + } else if (!TextUtils.isEmpty(text)) { + // NODE: text should not be logged, as it could contain PII + Log.v(TAG, "No hint using text: " + text + " and class " + className); + } + return null; + } + + /** + * Uses heuristics to infer an autofill hint from a {@code string}. + * + * @return standard autofill hint, or {@code null} when it could not be inferred. + */ + @Nullable + protected String inferHint(@Nullable String string) { + if (string == null) return null; + + string = string.toLowerCase(); + if (string.contains("label")) { + Log.v(TAG, "Ignoring 'label' hint: " + string); + return null; + } + if (string.contains("password")) return View.AUTOFILL_HINT_PASSWORD; + if (string.contains("username") + || (string.contains("login") && string.contains("id"))) + return View.AUTOFILL_HINT_USERNAME; + if (string.contains("email")) return View.AUTOFILL_HINT_EMAIL_ADDRESS; + if (string.contains("name")) return View.AUTOFILL_HINT_NAME; + if (string.contains("phone")) return View.AUTOFILL_HINT_PHONE; + + return null; + } + + static FillResponse createResponse(@NonNull Context context, + @NonNull ArrayMap<String, AutofillId> fields, int numDatasets, + boolean authenticateDatasets) { + String packageName = context.getPackageName(); + FillResponse.Builder response = new FillResponse.Builder(); + // 1.Add the dynamic datasets + for (int i = 1; i <= numDatasets; i++) { + Dataset unlockedDataset = newUnlockedDataset(fields, packageName, i); + if (authenticateDatasets) { + Dataset.Builder lockedDataset = new Dataset.Builder(); + for (Entry<String, AutofillId> field : fields.entrySet()) { + String hint = field.getKey(); + AutofillId id = field.getValue(); + String value = i + "-" + hint; + IntentSender authentication = + SimpleAuthActivity.newIntentSenderForDataset(context, unlockedDataset); + RemoteViews presentation = newDatasetPresentation(packageName, + "Tap to auth " + value); + lockedDataset.setValue(id, null, presentation) + .setAuthentication(authentication); + } + response.addDataset(lockedDataset.build()); + } else { + response.addDataset(unlockedDataset); + } + } + + // 2.Add save info + Collection<AutofillId> ids = fields.values(); + AutofillId[] requiredIds = new AutofillId[ids.size()]; + ids.toArray(requiredIds); + response.setSaveInfo( + // We're simple, so we're generic + new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, requiredIds).build()); + + // 3.Profit! + return response.build(); + } + + static Dataset newUnlockedDataset(@NonNull Map<String, AutofillId> fields, + @NonNull String packageName, int i) { + Dataset.Builder dataset = new Dataset.Builder(); + for (Entry<String, AutofillId> field : fields.entrySet()) { + String hint = field.getKey(); + AutofillId id = field.getValue(); + String value = i + "-" + hint; + + // We're simple - our dataset values are hardcoded as "N-hint" (for example, + // "1-username", "2-username") and they're displayed as such, except if they're a + // password + String displayValue = hint.contains("password") ? "password for #" + i : value; + RemoteViews presentation = newDatasetPresentation(packageName, displayValue); + dataset.setValue(id, AutofillValue.forText(value), presentation); + } + + return dataset.build(); + } + + /** + * Displays a toast with the given message. + */ + private void toast(@NonNull CharSequence message) { + Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); + } +} diff --git a/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/SimpleAuthActivity.java b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/SimpleAuthActivity.java new file mode 100644 index 00000000..4ff97a77 --- /dev/null +++ b/input/autofill/AutofillFramework/afservice/src/main/java/com/example/android/autofill/service/simple/SimpleAuthActivity.java @@ -0,0 +1,117 @@ +/* + * 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.simple; + +import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE; +import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.os.Bundle; +import android.os.Parcelable; +import android.service.autofill.Dataset; +import android.service.autofill.FillResponse; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.ArrayMap; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.widget.RemoteViews; + +import com.example.android.autofill.service.R; + +import java.util.Map.Entry; + +/** + * Activity used for autofill authentication, it simply sets the dataste upon tapping OK. + */ +// TODO(b/114236837): should display a small dialog, not take the full screen +public class SimpleAuthActivity extends Activity { + + private static final String EXTRA_DATASET = "dataset"; + private static final String EXTRA_HINTS = "hints"; + private static final String EXTRA_IDS = "ids"; + private static final String EXTRA_AUTH_DATASETS = "auth_datasets"; + + private static int sPendingIntentId = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.simple_service_auth_activity); + findViewById(R.id.yes).setOnClickListener((view) -> onYes()); + findViewById(R.id.no).setOnClickListener((view) -> onNo()); + } + + private void onYes() { + Intent myIntent = getIntent(); + Intent replyIntent = new Intent(); + Dataset dataset = myIntent.getParcelableExtra(EXTRA_DATASET); + if (dataset != null) { + replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset); + } else { + String[] hints = myIntent.getStringArrayExtra(EXTRA_HINTS); + Parcelable[] ids = myIntent.getParcelableArrayExtra(EXTRA_IDS); + boolean authenticateDatasets = myIntent.getBooleanExtra(EXTRA_AUTH_DATASETS, false); + int size = hints.length; + ArrayMap<String, AutofillId> fields = new ArrayMap<>(size); + for (int i = 0; i < size; i++) { + fields.put(hints[i], (AutofillId) ids[i]); + } + FillResponse response = + HeuristicsService.createResponse(this, fields, 1, authenticateDatasets); + replyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, response); + + } + setResult(RESULT_OK, replyIntent); + finish(); + } + + private void onNo() { + setResult(RESULT_CANCELED); + finish(); + } + + public static IntentSender newIntentSenderForDataset(@NonNull Context context, + @NonNull Dataset dataset) { + return newIntentSender(context, dataset, null, null, false); + } + + public static IntentSender newIntentSenderForResponse(@NonNull Context context, + @NonNull String[] hints, @NonNull AutofillId[] ids, boolean authenticateDatasets) { + return newIntentSender(context, null, hints, ids, authenticateDatasets); + } + + private static IntentSender newIntentSender(@NonNull Context context, + @Nullable Dataset dataset, @Nullable String[] hints, @Nullable AutofillId[] ids, + boolean authenticateDatasets) { + Intent intent = new Intent(context, SimpleAuthActivity.class); + if (dataset != null) { + intent.putExtra(EXTRA_DATASET, dataset); + } else { + intent.putExtra(EXTRA_HINTS, hints); + intent.putExtra(EXTRA_IDS, ids); + intent.putExtra(EXTRA_AUTH_DATASETS, authenticateDatasets); + } + + return PendingIntent.getActivity(context, ++sPendingIntentId, intent, + PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); + } +} diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/layout/simple_service_auth_activity.xml b/input/autofill/AutofillFramework/afservice/src/main/res/layout/simple_service_auth_activity.xml new file mode 100644 index 00000000..54fb6759 --- /dev/null +++ b/input/autofill/AutofillFramework/afservice/src/main/res/layout/simple_service_auth_activity.xml @@ -0,0 +1,47 @@ +<?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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:importantForAutofill="noExcludeDescendants" + android:focusable="true" + android:focusableInTouchMode="true" + android:orientation="vertical" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Authenticate?" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" > + <Button + android:id="@+id/no" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="No" /> + <Button + android:id="@+id/yes" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Yes" /> + </LinearLayout> + +</LinearLayout> diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/xml/basic_heuristics_service.xml b/input/autofill/AutofillFramework/afservice/src/main/res/xml/heuristics_service.xml index cfaabb03..fed1c479 100644 --- a/input/autofill/AutofillFramework/afservice/src/main/res/xml/basic_heuristics_service.xml +++ b/input/autofill/AutofillFramework/afservice/src/main/res/xml/heuristics_service.xml @@ -14,13 +14,16 @@ * limitations under the License. --> -<autofill-service xmlns:android="http://schemas.android.com/apk/res/android"> +<!-- TODO(b/114236837): use its own Settings Activity --> +<autofill-service xmlns:android="http://schemas.android.com/apk/res/android" + android:settingsActivity="com.example.android.autofill.service.settings.SettingsActivity"> + <!-- sample app --> <compatibility-package android:name="com.example.android.autofill.app" android:maxLongVersionCode="10000000000"/> - <!-- well-known browswers --> + <!-- well-known browswers, alphabetical order --> <compatibility-package android:name="com.android.chrome" android:maxLongVersionCode="10000000000"/> @@ -55,6 +58,9 @@ android:name="com.sec.android.app.sbrowser.beta" android:maxLongVersionCode="10000000000"/> <compatibility-package + android:name="org.chromium.chrome" + android:maxLongVersionCode="10000000000"/> + <compatibility-package android:name="org.mozilla.fennec_aurora" android:maxLongVersionCode="10000000000"/> <compatibility-package |