OSDN Git Service

Add custom presentation (RemoteViews) on Dataset values.
authorFelipe Leme <felipeal@google.com>
Mon, 20 Mar 2017 20:24:10 +0000 (13:24 -0700)
committerFelipe Leme <felipeal@google.com>
Tue, 21 Mar 2017 01:20:28 +0000 (18:20 -0700)
New tests on LoginActivityTest:

- testAutofillOneDatasetCustomPresentation()
- testAutofillMultipleDatasetsCustomPresentations()
- testAutofillMultipleDatasetsCustomPresentationSameFields()
- testAutofillMultipleDatasetsCustomPresentationFirstDatasetMissingSecondField()
- testAutofillMultipleDatasetsCustomPresentationSecondDatasetMissingFirstField()

Fixes: 36067706
Test: CtsAutoFillServiceTestCases pass

Change-Id: Iacb660bf5a5cf311dea4bfcbfe1b3722aab34715

api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/service/autofill/Dataset.java
services/autofill/java/com/android/server/autofill/ui/FillUi.java

index 7f87608..9215225 100644 (file)
@@ -36838,9 +36838,11 @@ package android.service.autofill {
 
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(android.widget.RemoteViews);
+    ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
   }
 
   public final class FillCallback {
index cd945e1..4b09f58 100644 (file)
@@ -39872,9 +39872,11 @@ package android.service.autofill {
 
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(android.widget.RemoteViews);
+    ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
   }
 
   public final class FillCallback {
index a80e7d9..d28e9f3 100644 (file)
@@ -36984,9 +36984,11 @@ package android.service.autofill {
 
   public static final class Dataset.Builder {
     ctor public Dataset.Builder(android.widget.RemoteViews);
+    ctor public Dataset.Builder();
     method public android.service.autofill.Dataset build();
     method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
+    method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
   }
 
   public final class FillCallback {
index ebe02c2..47e7803 100644 (file)
@@ -49,12 +49,14 @@ public final class Dataset implements Parcelable {
 
     private final ArrayList<AutofillId> mFieldIds;
     private final ArrayList<AutofillValue> mFieldValues;
+    private final ArrayList<RemoteViews> mFieldPresentations;
     private final RemoteViews mPresentation;
     private final IntentSender mAuthentication;
 
     private Dataset(Builder builder) {
         mFieldIds = builder.mFieldIds;
         mFieldValues = builder.mFieldValues;
+        mFieldPresentations = builder.mFieldPresentations;
         mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
     }
@@ -70,6 +72,12 @@ public final class Dataset implements Parcelable {
     }
 
     /** @hide */
+    public RemoteViews getFieldPresentation(int index) {
+        final RemoteViews customPresentation = mFieldPresentations.get(index);
+        return customPresentation != null ? customPresentation : mPresentation;
+    }
+
+    /** @hide */
     public @Nullable RemoteViews getPresentation() {
         return mPresentation;
     }
@@ -91,6 +99,8 @@ public final class Dataset implements Parcelable {
         return new StringBuilder("Dataset [")
                 .append(", fieldIds=").append(mFieldIds)
                 .append(", fieldValues=").append(mFieldValues)
+                .append(", fieldPresentations=")
+                .append(mFieldPresentations == null ? 0 : mFieldPresentations.size())
                 .append(", hasPresentation=").append(mPresentation != null)
                 .append(", hasAuthentication=").append(mAuthentication != null)
                 .append(']').toString();
@@ -103,6 +113,7 @@ public final class Dataset implements Parcelable {
     public static final class Builder {
         private ArrayList<AutofillId> mFieldIds;
         private ArrayList<AutofillValue> mFieldValues;
+        private ArrayList<RemoteViews> mFieldPresentations;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
@@ -118,6 +129,15 @@ public final class Dataset implements Parcelable {
         }
 
         /**
+         * Creates a new builder for a dataset where each field will be visualized independently.
+         *
+         * <p>When using this constructor, fields must be set through
+         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)}.
+         */
+        public Builder() {
+        }
+
+        /**
          * Requires a dataset authentication before autofilling the activity with this dataset.
          *
          * <p>This method is called when you need to provide an authentication
@@ -175,24 +195,54 @@ public final class Dataset implements Parcelable {
          *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
          * @param value value to be auto filled.
          * @return This builder.
+         * @throws IllegalStateException if the builder was constructed without a presentation
+         * ({@link RemoteViews}).
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @NonNull AutofillValue value) {
             throwIfDestroyed();
+            if (mPresentation == null) {
+                throw new IllegalStateException("Dataset presentation not set on constructor");
+            }
+            setValueAndPresentation(id, value, null);
+            return this;
+        }
+
+        /**
+         * Sets the value of a field, usin a custom presentation to visualize it.
+         *
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param value value to be auto filled.
+         * @param presentation The presentation used to visualize this field.
+         * @return This builder.
+         */
+        public @NonNull Builder setValue(@NonNull AutofillId id, @NonNull AutofillValue value,
+                @NonNull RemoteViews presentation) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(presentation, "presentation cannot be null");
+            setValueAndPresentation(id, value, presentation);
+            return this;
+        }
+
+        private void setValueAndPresentation(AutofillId id, AutofillValue value,
+                RemoteViews presentation) {
             Preconditions.checkNotNull(id, "id cannot be null");
             Preconditions.checkNotNull(value, "value cannot be null");
             if (mFieldIds != null) {
                 final int existingIdx = mFieldIds.indexOf(id);
                 if (existingIdx >= 0) {
                     mFieldValues.set(existingIdx, value);
-                    return this;
+                    mFieldPresentations.set(existingIdx, presentation);
+                    return;
                 }
             } else {
                 mFieldIds = new ArrayList<>();
                 mFieldValues = new ArrayList<>();
+                mFieldPresentations = new ArrayList<>();
             }
             mFieldIds.add(id);
             mFieldValues.add(value);
-            return this;
+            mFieldPresentations.add(presentation);
         }
 
         /**
@@ -234,6 +284,7 @@ public final class Dataset implements Parcelable {
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeTypedArrayList(mFieldIds, flags);
         parcel.writeTypedArrayList(mFieldValues, flags);
+        parcel.writeParcelableList(mFieldPresentations, flags);
         parcel.writeParcelable(mAuthentication, flags);
     }
 
@@ -243,15 +294,22 @@ public final class Dataset implements Parcelable {
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = new Builder(parcel.readParcelable(null));
+            final RemoteViews presentation = parcel.readParcelable(null);
+            final Builder builder = (presentation == null)
+                    ? new Builder()
+                    : new Builder(presentation);
             final ArrayList<AutofillId> ids = parcel.readTypedArrayList(null);
             final ArrayList<AutofillValue> values = parcel.readTypedArrayList(null);
+            final ArrayList<RemoteViews> presentations = new ArrayList<>();
+            parcel.readParcelableList(presentations, null);
             final int idCount = (ids != null) ? ids.size() : 0;
             final int valueCount = (values != null) ? values.size() : 0;
             for (int i = 0; i < idCount; i++) {
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = (valueCount > i) ? values.get(i) : null;
-                builder.setValue(id, value);
+                final RemoteViews fieldPresentation = presentations.isEmpty() ? null
+                        : presentations.get(i);
+                builder.setValueAndPresentation(id, value, fieldPresentation);
             }
             builder.setAuthentication(parcel.readParcelable(null));
             return builder.build();
index a8c8752..85eecdf 100644 (file)
@@ -36,6 +36,8 @@ import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
 import android.widget.ArrayAdapter;
 import android.widget.ListView;
+import android.widget.RemoteViews;
+
 import com.android.internal.R;
 import libcore.util.Objects;
 
@@ -110,15 +112,15 @@ final class FillUi {
                 final Dataset dataset = response.getDatasets().get(i);
                 final int index = dataset.getFieldIds().indexOf(focusedViewId);
                 if (index >= 0) {
-                    final AutofillValue value = dataset.getFieldValues().get(index);
+                    final RemoteViews presentation = dataset.getFieldPresentation(index);
                     final View view;
                     try {
-                        view = dataset.getPresentation().apply(context, null);
+                        view = presentation.apply(context, null);
                     } catch (RuntimeException e) {
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
                     }
-
+                    final AutofillValue value = dataset.getFieldValues().get(index);
                     String valueText = null;
                     if (value.isText()) {
                         valueText = value.getTextValue().toString().toLowerCase();