fillContexts = request.getFillContexts();
+ return fillContexts.get(fillContexts.size() - 1).getStructure();
+ }
+
+ /**
+ * 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.
+ *
+ * An autofillable field is a {@link AssistStructure.ViewNode} whose getHint(ViewNode)
+ * method.
+ */
+ @NonNull
+ private static ArrayMap getAutofillableFields(
+ @NonNull AssistStructure structure) {
+ ArrayMap fields = new ArrayMap<>();
+ int nodes = structure.getWindowNodeCount();
+ for (int i = 0; i < nodes; i++) {
+ AssistStructure.ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
+ addAutofillableFields(fields, node);
+ }
+ ArrayMap result = new ArrayMap<>();
+ int filedCount = fields.size();
+ for (int i = 0; i < filedCount; i++) {
+ String key = fields.keyAt(i);
+ AutofillId value = fields.valueAt(i);
+ // For fields with no hint we just use Field
+ if (key.equals(value.toString())) {
+ result.put("Field:" + i + "-", fields.valueAt(i));
+ } else {
+ result.put(key, fields.valueAt(i));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Adds any autofillable view from the {@link AssistStructure.ViewNode} and its descendants to
+ * the map.
+ */
+ private static void addAutofillableFields(@NonNull Map fields,
+ @NonNull AssistStructure.ViewNode node) {
+ if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) {
+ if (!fields.containsValue(node.getAutofillId())) {
+ final String key;
+ if (node.getHint() != null) {
+ key = node.getHint().toLowerCase();
+ } else if (node.getAutofillHints() != null) {
+ key = node.getAutofillHints()[0].toLowerCase();
+ } else {
+ key = node.getAutofillId().toString();
+ }
+ fields.put(key, node.getAutofillId());
+ }
+ }
+ int childrenSize = node.getChildCount();
+ for (int i = 0; i < childrenSize; i++) {
+ addAutofillableFields(fields, node.getChildAt(i));
+ }
+ }
+}
diff --git a/samples/InlineFillService/src/foo/bar/inline/InlineFillService.java b/samples/InlineFillService/src/foo/bar/inline/InlineFillService.java
index 59c9f73a3..d0c88c552 100644
--- a/samples/InlineFillService/src/foo/bar/inline/InlineFillService.java
+++ b/samples/InlineFillService/src/foo/bar/inline/InlineFillService.java
@@ -15,20 +15,11 @@
*/
package foo.bar.inline;
-import android.app.PendingIntent;
-import android.app.assist.AssistStructure;
-import android.app.assist.AssistStructure.ViewNode;
-import android.app.slice.Slice;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.IntentSender;
-import android.graphics.drawable.Icon;
import android.os.CancellationSignal;
import android.service.autofill.AutofillService;
-import android.service.autofill.Dataset;
import android.service.autofill.FillCallback;
-import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.InlinePresentation;
@@ -37,23 +28,14 @@ import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Size;
-import android.view.View;
import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
-import android.widget.inline.InlinePresentationSpec;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.widget.RemoteViews;
-import android.widget.Toast;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.autofill.InlinePresentationBuilder;
import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Optional;
/**
* A basic {@link AutofillService} implementation that only shows dynamic-generated datasets
@@ -61,7 +43,7 @@ import java.util.Map.Entry;
*/
public class InlineFillService extends AutofillService {
- private static final String TAG = "InlineFillService";
+ static final String TAG = "InlineFillService";
/**
* Number of datasets sent on each request - we're simple, that value is hardcoded in our DNA!
@@ -76,22 +58,21 @@ public class InlineFillService extends AutofillService {
FillCallback callback) {
Log.d(TAG, "onFillRequest()");
+ final Context context = getApplicationContext();
+
// Find autofillable fields
- AssistStructure structure = getLatestAssistStructure(request);
-
- ArrayMap fields = getAutofillableFields(structure);
+ ArrayMap fields = Helper.getAutofillableFields(request);
Log.d(TAG, "autofillable fields:" + fields);
-
if (fields.isEmpty()) {
- showMessage("Service could not figure out how to autofill this screen");
+ Helper.showMessage(context,
+ "InlineFillService could not figure out how to autofill this screen");
callback.onSuccess(null);
return;
}
-
- final InlineSuggestionsRequest inlineRequest = request.getInlineSuggestionsRequest();
- final int maxSuggestionsCount = inlineRequest == null
- ? NUMBER_DATASETS
- : Math.min(inlineRequest.getMaxSuggestionCount(), NUMBER_DATASETS);
+ final Optional inlineRequest =
+ InlineRequestHelper.getInlineSuggestionsRequest(request);
+ final int maxSuggestionsCount = InlineRequestHelper.getMaxSuggestionCount(inlineRequest,
+ NUMBER_DATASETS);
// Create the base response
final FillResponse response;
@@ -103,31 +84,20 @@ public class InlineFillService extends AutofillService {
hints[i] = fields.keyAt(i);
ids[i] = fields.valueAt(i);
}
-
IntentSender authentication = AuthActivity.newIntentSenderForResponse(this, hints,
- ids, mAuthenticateDatasets, inlineRequest);
- RemoteViews presentation = newDatasetPresentation(getPackageName(),
+ ids, mAuthenticateDatasets, inlineRequest.orElse(null));
+ RemoteViews presentation = ResponseHelper.newDatasetPresentation(getPackageName(),
"Tap to auth response");
- final InlinePresentation inlinePresentation;
- if (inlineRequest != null) {
- final Slice authSlice = new InlinePresentationBuilder("Tap to auth respones")
- .build();
- final List specs = inlineRequest.getInlinePresentationSpecs();
- final int specsSize = specs.size();
- final InlinePresentationSpec currentSpec = specsSize > 0 ? specs.get(0) : null;
- inlinePresentation = new InlinePresentation(authSlice, currentSpec,
- /* pined= */ false);
- } else {
- inlinePresentation = null;
- }
-
+ InlinePresentation inlinePresentation =
+ InlineRequestHelper.maybeCreateInlineAuthenticationResponse(context,
+ inlineRequest);
response = new FillResponse.Builder()
.setAuthentication(ids, authentication, presentation, inlinePresentation)
.build();
} else {
response = createResponse(this, fields, maxSuggestionsCount, mAuthenticateDatasets,
- request.getInlineSuggestionsRequest());
+ inlineRequest);
}
callback.onSuccess(response);
@@ -135,63 +105,30 @@ public class InlineFillService extends AutofillService {
static FillResponse createResponse(@NonNull Context context,
@NonNull ArrayMap fields, int numDatasets,
- boolean authenticateDatasets, @Nullable InlineSuggestionsRequest inlineRequest) {
+ boolean authenticateDatasets,
+ @NonNull Optional inlineRequest) {
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(context, fields, packageName, i,
- inlineRequest);
+ for (int i = 0; i < numDatasets; i++) {
if (authenticateDatasets) {
- Dataset.Builder lockedDataset = new Dataset.Builder();
- for (Entry field : fields.entrySet()) {
- String hint = field.getKey();
- AutofillId id = field.getValue();
- String value = i + "-" + hint;
- IntentSender authentication =
- AuthActivity.newIntentSenderForDataset(context, unlockedDataset);
- RemoteViews presentation = newDatasetPresentation(packageName,
- "Tap to auth " + value);
-
- final InlinePresentation inlinePresentation;
- if (inlineRequest != null) {
- final Slice authSlice = new InlinePresentationBuilder(
- "Tap to auth " + value).build();
- final List specs
- = inlineRequest.getInlinePresentationSpecs();
- final int specsSize = specs.size();
- final InlinePresentationSpec currentSpec =
- specsSize > 0 ? specs.get(0) : null;
- inlinePresentation = new InlinePresentation(authSlice, currentSpec,
- /* pined= */ false);
- lockedDataset.setValue(id, null, presentation, inlinePresentation)
- .setAuthentication(authentication);
- } else {
- lockedDataset.setValue(id, null, presentation)
- .setAuthentication(authentication);
- }
- }
- response.addDataset(lockedDataset.build());
+ response.addDataset(ResponseHelper.newLockedDataset(context, fields, packageName, i,
+ inlineRequest));
} else {
- response.addDataset(unlockedDataset);
+ response.addDataset(ResponseHelper.newUnlockedDataset(context, fields,
+ packageName, i, inlineRequest));
}
}
- if (inlineRequest != null) {
- // Reuse the first spec's height for the inline action size, as there isn't dedicated
- // value from the request for this.
- final int height = inlineRequest.getInlinePresentationSpecs().get(0)
- .getMinSize().getHeight();
- final Size actionIconSize = new Size(height, height);
- response.addDataset(
- newInlineActionDataset(context, actionIconSize, R.drawable.ic_settings,
- fields));
- response.addDataset(
- newInlineActionDataset(context, actionIconSize, R.drawable.ic_settings,
- fields));
+ // 2. Add some inline actions
+ if (inlineRequest.isPresent()) {
+ response.addDataset(InlineRequestHelper.createInlineActionDataset(context, fields,
+ inlineRequest.get(), R.drawable.ic_settings));
+ response.addDataset(InlineRequestHelper.createInlineActionDataset(context, fields,
+ inlineRequest.get(), R.drawable.ic_settings));
}
- // 2.Add save info
+ // 3.Add save info
Collection ids = fields.values();
AutofillId[] requiredIds = new AutofillId[ids.size()];
ids.toArray(requiredIds);
@@ -199,162 +136,14 @@ public class InlineFillService extends AutofillService {
// We're simple, so we're generic
new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_GENERIC, requiredIds).build());
- // 3.Profit!
+ // 4.Profit!
return response.build();
}
- static Dataset newInlineActionDataset(@NonNull Context context,
- @NonNull Size size, int drawable, ArrayMap fields) {
- Intent intent = new Intent().setComponent(
- new ComponentName(context.getPackageName(), SettingsActivity.class.getName()));
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- final Slice suggestionSlice = new InlinePresentationBuilder()
- .setStartIcon(Icon.createWithResource(context, drawable))
- .setAttribution(pendingIntent)
- .build();
- final InlinePresentationSpec currentSpec = new InlinePresentationSpec.Builder(size,
- size).build();
- Dataset.Builder builder = new Dataset.Builder()
- .setInlinePresentation(
- new InlinePresentation(suggestionSlice, currentSpec, /** pined= */true))
- .setAuthentication(pendingIntent.getIntentSender());
- for (AutofillId fieldId : fields.values()) {
- builder.setValue(fieldId, null);
- }
- return builder.build();
- }
-
- static Dataset newUnlockedDataset(@NonNull Context context,
- @NonNull Map fields, @NonNull String packageName, int i,
- @Nullable InlineSuggestionsRequest inlineRequest) {
-
- Dataset.Builder dataset = new Dataset.Builder();
- for (Entry field : fields.entrySet()) {
- final String hint = field.getKey();
- final AutofillId id = field.getValue();
- final 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
- // password
- final String displayValue = hint.contains("password") ? "password for #" + i : value;
- final RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
-
- // Add Inline Suggestion required info.
- if (inlineRequest != null) {
- Log.d(TAG, "Found InlineSuggestionsRequest in FillRequest: " + inlineRequest);
-
- Intent intent = new Intent().setComponent(
- new ComponentName(context.getPackageName(), SettingsActivity.class.getName()));
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- final Slice suggestionSlice = new InlinePresentationBuilder(value)
- .setAttribution(pendingIntent).build();
-
- final List specs = inlineRequest.getInlinePresentationSpecs();
- final int specsSize = specs.size();
- final InlinePresentationSpec currentSpec = i - 1 < specsSize
- ? specs.get(i - 1)
- : specs.get(specsSize - 1);
- final InlinePresentation inlinePresentation =
- new InlinePresentation(suggestionSlice, currentSpec, /** pined= */false);
- dataset.setValue(id, AutofillValue.forText(value), presentation,
- inlinePresentation);
- } else {
- dataset.setValue(id, AutofillValue.forText(value), presentation);
- }
- }
-
- return dataset.build();
- }
-
@Override
public void onSaveRequest(SaveRequest request, SaveCallback callback) {
Log.d(TAG, "onSaveRequest()");
- showMessage("Save not supported");
+ Helper.showMessage(getApplicationContext(), "InlineFillService doesn't support Save");
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.
- *
- * An autofillable field is a {@link ViewNode} whose getHint(ViewNode) method.
- */
- @NonNull
- private ArrayMap getAutofillableFields(@NonNull AssistStructure structure) {
- ArrayMap fields = new ArrayMap<>();
- int nodes = structure.getWindowNodeCount();
- for (int i = 0; i < nodes; i++) {
- ViewNode node = structure.getWindowNodeAt(i).getRootViewNode();
- addAutofillableFields(fields, node);
- }
- ArrayMap result = new ArrayMap<>();
- int filedCount = fields.size();
- for (int i = 0; i < filedCount; i++) {
- String key = fields.keyAt(i);
- AutofillId value = fields.valueAt(i);
- // For fields with no hint we just use Field
- if (key.equals(value.toString())) {
- result.put("Field:" + i + "-", fields.valueAt(i));
- } else {
- result.put(key, fields.valueAt(i));
- }
- }
- return result;
- }
-
- /**
- * Adds any autofillable view from the {@link ViewNode} and its descendants to the map.
- */
- private void addAutofillableFields(@NonNull Map fields,
- @NonNull ViewNode node) {
- if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) {
- if (!fields.containsValue(node.getAutofillId())) {
- final String key;
- if (node.getHint() != null) {
- key = node.getHint().toLowerCase();
- } else {
- key = node.getAutofillId().toString();
- }
- fields.put(key, node.getAutofillId());
- }
- }
- int childrenSize = node.getChildCount();
- for (int i = 0; i < childrenSize; i++) {
- addAutofillableFields(fields, node.getChildAt(i));
- }
- }
-
- /**
- * Helper method to get the {@link AssistStructure} associated with the latest request
- * in an autofill context.
- */
- @NonNull
- private static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) {
- List fillContexts = request.getFillContexts();
- return fillContexts.get(fillContexts.size() - 1).getStructure();
- }
-
- /**
- * Helper method to create a dataset presentation with the given text.
- */
- @NonNull
- private static RemoteViews newDatasetPresentation(@NonNull String packageName,
- @NonNull CharSequence text) {
- RemoteViews presentation =
- new RemoteViews(packageName, R.layout.list_item);
- presentation.setTextViewText(R.id.text, text);
- return presentation;
- }
-
- /**
- * Displays a toast with the given message.
- */
- private void showMessage(@NonNull CharSequence message) {
- Log.i(TAG, message.toString());
- Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
- }
}
diff --git a/samples/InlineFillService/src/foo/bar/inline/InlineRequestHelper.java b/samples/InlineFillService/src/foo/bar/inline/InlineRequestHelper.java
new file mode 100644
index 000000000..92fb2a278
--- /dev/null
+++ b/samples/InlineFillService/src/foo/bar/inline/InlineRequestHelper.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2020 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 foo.bar.inline;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.service.autofill.Dataset;
+import android.service.autofill.FillRequest;
+import android.service.autofill.InlinePresentation;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.widget.inline.InlinePresentationSpec;
+
+import androidx.autofill.inline.v1.InlineSuggestionUi;
+import androidx.autofill.inline.v1.InlineSuggestionUi.Content;
+
+import java.util.Optional;
+
+public class InlineRequestHelper {
+ static Optional getInlineSuggestionsRequest(FillRequest request) {
+ final InlineSuggestionsRequest inlineRequest = request.getInlineSuggestionsRequest();
+ if (inlineRequest != null && inlineRequest.getMaxSuggestionCount() > 0
+ && !inlineRequest.getInlinePresentationSpecs().isEmpty()) {
+ return Optional.of(inlineRequest);
+ }
+ return Optional.empty();
+ }
+
+ static int getMaxSuggestionCount(Optional inlineRequest, int max) {
+ if (inlineRequest.isPresent()) {
+ return Math.min(max, inlineRequest.get().getMaxSuggestionCount());
+ }
+ return max;
+ }
+
+ static InlinePresentation maybeCreateInlineAuthenticationResponse(
+ Context context, Optional inlineRequest) {
+ if (!inlineRequest.isPresent()) {
+ return null;
+ }
+ final PendingIntent attribution = createAttribution(context,
+ "Please tap on the chip to authenticate the Autofill response.");
+ final Slice slice = createSlice("Tap to auth response", null, null, null, attribution);
+ final InlinePresentationSpec spec = inlineRequest.get().getInlinePresentationSpecs().get(0);
+ return new InlinePresentation(slice, spec, false);
+ }
+
+ static InlinePresentation createInlineDataset(Context context,
+ InlineSuggestionsRequest inlineRequest, String value, int index) {
+ final PendingIntent attribution = createAttribution(context,
+ "Please tap on the chip to autofill the value:" + value);
+ final Slice slice = createSlice(value, null, null, null, attribution);
+ index = Math.min(inlineRequest.getInlinePresentationSpecs().size() - 1, index);
+ final InlinePresentationSpec spec = inlineRequest.getInlinePresentationSpecs().get(index);
+ return new InlinePresentation(slice, spec, false);
+ }
+
+ static Dataset createInlineActionDataset(Context context,
+ ArrayMap fields,
+ InlineSuggestionsRequest inlineRequest, int drawable) {
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(context, 0, new Intent(context, SettingsActivity.class),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ Dataset.Builder builder =
+ new Dataset.Builder()
+ .setInlinePresentation(createInlineAction(context, inlineRequest, drawable))
+ .setAuthentication(pendingIntent.getIntentSender());
+ for (AutofillId fieldId : fields.values()) {
+ builder.setValue(fieldId, null);
+ }
+ return builder.build();
+ }
+
+ private static InlinePresentation createInlineAction(Context context,
+ InlineSuggestionsRequest inlineRequest, int drawable) {
+ final PendingIntent attribution = createAttribution(context,
+ "Please tap on the chip to launch the action.");
+ final Icon icon = Icon.createWithResource(context, drawable);
+ final Slice slice = createSlice(null, null, icon, null, attribution);
+ // Reuse the first spec's height for the inline action size, as there isn't dedicated
+ // value from the request for this.
+ final InlinePresentationSpec spec = inlineRequest.getInlinePresentationSpecs().get(0);
+ return new InlinePresentation(slice, spec, true);
+ }
+
+ private static Slice createSlice(
+ String title, String subtitle, Icon startIcon, Icon endIcon,
+ PendingIntent attribution) {
+ Content.Builder builder = InlineSuggestionUi.newContentBuilder();
+ if (attribution != null) {
+ builder.setAttribution(attribution);
+ }
+ if (!TextUtils.isEmpty(title)) {
+ builder.setTitle(title);
+ }
+ if (!TextUtils.isEmpty(subtitle)) {
+ builder.setSubtitle(subtitle);
+ }
+ if (startIcon != null) {
+ builder.setStartIcon(startIcon);
+ }
+ if (endIcon != null) {
+ builder.setEndIcon(endIcon);
+ }
+ return builder.build().getSlice();
+ }
+
+ private static PendingIntent createAttribution(Context context, String msg) {
+ Intent intent = new Intent(context, AttributionDialogActivity.class);
+ intent.putExtra(AttributionDialogActivity.KEY_MSG, msg);
+ // Should use different request code to avoid the new intent overriding the old one.
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(
+ context, msg.hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return pendingIntent;
+ }
+
+}
diff --git a/samples/InlineFillService/src/foo/bar/inline/ResponseHelper.java b/samples/InlineFillService/src/foo/bar/inline/ResponseHelper.java
new file mode 100644
index 000000000..ad1349756
--- /dev/null
+++ b/samples/InlineFillService/src/foo/bar/inline/ResponseHelper.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 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 foo.bar.inline;
+
+import static foo.bar.inline.InlineFillService.TAG;
+
+import android.content.Context;
+import android.content.IntentSender;
+import android.service.autofill.Dataset;
+import android.service.autofill.InlinePresentation;
+import android.util.Log;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+
+import java.util.Map;
+import java.util.Optional;
+
+class ResponseHelper {
+
+ static Dataset newUnlockedDataset(@NonNull Context context,
+ @NonNull Map fields, @NonNull String packageName, int index,
+ @NonNull Optional inlineRequest) {
+
+ Dataset.Builder dataset = new Dataset.Builder();
+ for (Map.Entry field : fields.entrySet()) {
+ final String hint = field.getKey();
+ final AutofillId id = field.getValue();
+ final String value = hint + (index + 1);
+
+ // 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
+ // password
+ Log.d(TAG, "hint: " + hint);
+ final String displayValue = hint.contains("password") ? "password for #" + (index + 1)
+ : value;
+ final RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
+
+ // Add Inline Suggestion required info.
+ if (inlineRequest.isPresent()) {
+ Log.d(TAG, "Found InlineSuggestionsRequest in FillRequest: " + inlineRequest);
+ final InlinePresentation inlinePresentation =
+ InlineRequestHelper.createInlineDataset(context, inlineRequest.get(),
+ displayValue, index);
+ dataset.setValue(id, AutofillValue.forText(value), presentation,
+ inlinePresentation);
+ } else {
+ dataset.setValue(id, AutofillValue.forText(value), presentation);
+ }
+ }
+
+ return dataset.build();
+ }
+
+ static Dataset newLockedDataset(@NonNull Context context,
+ @NonNull Map fields, @NonNull String packageName, int index,
+ @NonNull Optional inlineRequest) {
+ Dataset unlockedDataset = ResponseHelper.newUnlockedDataset(context, fields,
+ packageName, index, inlineRequest);
+
+ Dataset.Builder lockedDataset = new Dataset.Builder();
+ for (Map.Entry field : fields.entrySet()) {
+ String hint = field.getKey();
+ AutofillId id = field.getValue();
+ String value = (index + 1) + "-" + hint;
+ String displayValue = "Tap to auth " + value;
+ IntentSender authentication =
+ AuthActivity.newIntentSenderForDataset(context, unlockedDataset);
+ RemoteViews presentation = newDatasetPresentation(packageName, displayValue);
+ if (inlineRequest.isPresent()) {
+ final InlinePresentation inlinePresentation =
+ InlineRequestHelper.createInlineDataset(context, inlineRequest.get(),
+ displayValue, index);
+ lockedDataset.setValue(id, null, presentation, inlinePresentation)
+ .setAuthentication(authentication);
+ } else {
+ lockedDataset.setValue(id, null, presentation)
+ .setAuthentication(authentication);
+ }
+ }
+ return lockedDataset.build();
+ }
+
+ /**
+ * Helper method to create a dataset presentation with the givean text.
+ */
+ @NonNull
+ static RemoteViews newDatasetPresentation(@NonNull String packageName,
+ @NonNull CharSequence text) {
+ RemoteViews presentation =
+ new RemoteViews(packageName, R.layout.list_item);
+ presentation.setTextViewText(R.id.text, text);
+ return presentation;
+ }
+}