Autofill sample: Adds client activity for RecyclerView.

Uses a RecyclerView containing autofillable fields to exercise P DP2
API change (mutable AutofillIds).

Bug: 74553154
Test: manual
Change-Id: Ided48cbd3d22e99a598c95ce9dff73ea7986f23e
diff --git a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
index 4079e2e..7c18468 100644
--- a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
@@ -51,7 +51,7 @@
         <activity android:name="com.example.android.autofill.app.commoncases.WebViewSignInActivity" />
         <activity android:name="com.example.android.autofill.app.edgecases.MultipleStepsSignInActivity" />
         <activity android:name="com.example.android.autofill.app.edgecases.MultipleStepsCreditCardActivity" />
-
+        <activity android:name="com.example.android.autofill.app.commoncases.RecyclerViewActivity" />
     </application>
 
 </manifest>
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/RecyclerViewActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/RecyclerViewActivity.java
new file mode 100644
index 0000000..c2ce43c
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/commoncases/RecyclerViewActivity.java
@@ -0,0 +1,358 @@
+/*
+ * 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.app.commoncases;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.annotation.DrawableRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.StringRes;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.recyclerview.extensions.ListAdapter;
+import android.support.v7.util.DiffUtil;
+import android.support.v7.widget.DividerItemDecoration;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.example.android.autofill.app.R;
+import com.example.android.autofill.app.WelcomeActivity;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This is mostly a normal Activity containing a RecyclerView. The only difference is, the rows in
+ * the RecyclerView have autofillable fields. Therefore, when we bind data to a recycled view, we
+ * need to also set the {@link AutofillId} on the view.
+ */
+@RequiresApi(28)
+public class RecyclerViewActivity extends AppCompatActivity {
+    private AutofillManager mAfm;
+    private List<FieldMetadata> mFields;
+    private FieldAdapter mFieldAdapter;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.recycler_view_activity);
+        RecyclerView recyclerView = findViewById(R.id.recyclerView);
+        mAfm = getSystemService(AutofillManager.class);
+
+        // Init RecyclerView
+        LinearLayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
+        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
+                recyclerView.getContext(), layoutManager.getOrientation());
+        recyclerView.addItemDecoration(dividerItemDecoration);
+        mFields = initList();
+        mFieldAdapter = new FieldAdapter(mFields);
+        recyclerView.setAdapter(mFieldAdapter);
+        recyclerView.setLayoutManager(layoutManager);
+
+        // Init submit and clear buttons.
+        findViewById(R.id.submitButton).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                submit();
+            }
+        });
+        findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                AutofillManager afm = getSystemService(AutofillManager.class);
+                if (afm != null) {
+                    afm.cancel();
+                }
+                resetFields();
+            }
+        });
+    }
+
+    private void resetFields() {
+        for (FieldMetadata fieldMetadata : mFields) {
+            fieldMetadata.setEnteredText("");
+        }
+        mFieldAdapter.notifyDataSetChanged();
+    }
+
+    private void submit() {
+        Intent intent = WelcomeActivity.getStartActivityIntent(RecyclerViewActivity.this);
+        startActivity(intent);
+        finish();
+    }
+
+    public List<FieldMetadata> initList() {
+        return Arrays.asList(
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_NAME,
+                        R.string.recycler_view_label_name,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_TEXT
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        "bday-month",
+                        R.string.recycler_view_label_birthday_month,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_EMAIL_ADDRESS,
+                        R.string.recycler_view_label_email,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_PHONE,
+                        R.string.recycler_view_label_phone,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_PHONE
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        "tel_extension",
+                        R.string.recycler_view_label_tel_extension,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_PHONE
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_CREDIT_CARD_NUMBER,
+                        R.string.recycler_view_label_cc_number,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,
+                        R.string.recycler_view_label_cc_sc,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
+                        R.string.recycler_view_label_cc_exp_month,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
+                        R.string.recycler_view_label_cc_exp_year,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        "address-line1",
+                        R.string.recycler_view_label_address_line_1,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        "address-line2",
+                        R.string.recycler_view_label_address_line_2,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        "address-line3",
+                        R.string.recycler_view_label_address_line_3,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        View.AUTOFILL_HINT_POSTAL_CODE,
+                        R.string.recycler_view_label_postal_code,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                ),
+                new FieldMetadata(
+                        mAfm.getNextAutofillId(),
+                        "bday-year",
+                        R.string.recycler_view_label_birthday_year,
+                        R.drawable.ic_person_black_24dp,
+                        InputType.TYPE_CLASS_NUMBER
+                )
+        );
+    }
+
+    static class FieldAdapter extends ListAdapter<FieldMetadata, FieldViewHolder> {
+        private List<FieldMetadata> mFields;
+        public FieldAdapter(List<FieldMetadata> fields) {
+            super(new FieldDiff());
+            mFields = fields;
+            submitList(mFields);
+        }
+
+        @NonNull
+        @Override
+        public FieldViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+            return new FieldViewHolder(LayoutInflater.from(parent.getContext())
+                    .inflate(R.layout.user_data_field, parent, false),
+                    new FieldWatcher(mFields));
+        }
+
+        @Override
+        public void onBindViewHolder(@NonNull FieldViewHolder holder, int position) {
+            holder.bind(getItem(position));
+        }
+    }
+
+    static class FieldViewHolder extends RecyclerView.ViewHolder {
+        ImageView mIcon;
+        TextView mLabel;
+        EditText mField;
+        FieldWatcher mWatcher;
+
+        public FieldViewHolder(@NonNull View itemView, FieldWatcher textWatcher) {
+            super(itemView);
+            mIcon = itemView.findViewById(R.id.icon);
+            mLabel = itemView.findViewById(R.id.label);
+            mField = itemView.findViewById(R.id.field);
+            mWatcher = textWatcher;
+            mField.addTextChangedListener(mWatcher);
+        }
+
+        void bind(FieldMetadata fieldMetadata) {
+            mWatcher.updatePosition(getAdapterPosition());
+            Drawable drawable = mIcon.getResources().getDrawable(fieldMetadata.getIconRes());
+            mIcon.setImageDrawable(drawable);
+            mLabel.setText(fieldMetadata.getLabelRes());
+            mField.setAutofillHints(fieldMetadata.getAutofillHint());
+            mField.setInputType(fieldMetadata.getInputType());
+            mField.setText(fieldMetadata.getEnteredText());
+
+            // IMPORTANT: setAutofillId of recycled View.
+            mField.setAutofillId(fieldMetadata.getAutofillId());
+        }
+    }
+
+    /**
+     * TextWatcher implementation to ensure EditTexts get recycled properly.
+     */
+    static class FieldWatcher implements TextWatcher {
+        private int mPosition;
+        private List<FieldMetadata> mFields;
+
+        public FieldWatcher(List<FieldMetadata> fields) {
+            mFields = fields;
+        }
+
+        public void updatePosition(int position) {
+            this.mPosition = position;
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+            // NO-OP
+        }
+
+        @Override
+        public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+            mFields.get(mPosition).setEnteredText(charSequence);
+        }
+
+        @Override
+        public void afterTextChanged(Editable editable) {
+            // NO-OP
+        }
+    }
+
+    /**
+     * Model class that holds all of the data needed for a row in the {@link RecyclerView}.
+     */
+    static class FieldMetadata {
+        AutofillId mAutofillId;
+        String mAutofillHint;
+        @StringRes int mLabelRes;
+        @DrawableRes int mIconRes;
+        int mInputType;
+        CharSequence mEnteredText = "";
+
+        FieldMetadata(AutofillId autofillId, String autofillHint, @StringRes int labelRes,
+                @DrawableRes int iconRes, int inputType) {
+            mAutofillId = autofillId;
+            mAutofillHint = autofillHint;
+            mLabelRes = labelRes;
+            mIconRes = iconRes;
+            mInputType = inputType;
+        }
+
+        public AutofillId getAutofillId() {
+            return mAutofillId;
+        }
+
+        public String getAutofillHint() {
+            return mAutofillHint;
+        }
+
+        public int getLabelRes() {
+            return mLabelRes;
+        }
+
+        public int getIconRes() {
+            return mIconRes;
+        }
+
+        public int getInputType() {
+            return mInputType;
+        }
+
+        public void setEnteredText(CharSequence enteredText) {
+            mEnteredText = enteredText;
+        }
+
+        public CharSequence getEnteredText() {
+            return mEnteredText;
+        }
+    }
+
+    static class FieldDiff extends DiffUtil.ItemCallback<FieldMetadata> {
+        @Override
+        public boolean areItemsTheSame(@NonNull FieldMetadata oldItem,
+                @NonNull FieldMetadata newItem) {
+            return oldItem == newItem;
+        }
+
+        @Override
+        public boolean areContentsTheSame(@NonNull FieldMetadata oldItem,
+                @NonNull FieldMetadata newItem) {
+            return oldItem.equals(newItem);
+        }
+    }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java
index 9fea6bf..1647277 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofill/app/view/widget/NavigationItem.java
@@ -20,10 +20,12 @@
 import android.content.res.TypedArray;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.support.annotation.ColorRes;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.content.ContextCompat;
+import android.support.v4.os.BuildCompat;
 import android.support.v7.widget.CardView;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -54,6 +56,15 @@
         super(context, attrs, defStyleAttr, defStyleRes);
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NavigationItem,
                 defStyleAttr, defStyleRes);
+        int activityMinSdk = typedArray.getInteger(R.styleable.NavigationItem_minSdk, 26);
+
+        // TODO: Remove BuildCompat.isAtLeastP() check when API 28 is finalized.
+        int deviceMinSdk = BuildCompat.isAtLeastP() ? 28 : Build.VERSION.SDK_INT;
+        if (deviceMinSdk < activityMinSdk) {
+            // If device's SDK is lower than the minSdk specified by the NavigationItem, hide it.
+            setVisibility(View.GONE);
+            return;
+        }
         String labelText = typedArray.getString(R.styleable.NavigationItem_labelText);
         String infoText = typedArray.getString(R.styleable.NavigationItem_infoText);
         Drawable logoDrawable = typedArray.getDrawable(R.styleable.NavigationItem_itemLogo);
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_format_list_bulleted_black_24dp.xml b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_format_list_bulleted_black_24dp.xml
new file mode 100644
index 0000000..b53c0be
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/drawable/ic_format_list_bulleted_black_24dp.xml
@@ -0,0 +1,24 @@
+<!--
+ * 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4,10.5c-0.83,0 -1.5,0.67 -1.5,1.5s0.67,1.5 1.5,1.5 1.5,-0.67 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM4,4.5c-0.83,0 -1.5,0.67 -1.5,1.5S3.17,7.5 4,7.5 5.5,6.83 5.5,6 4.83,4.5 4,4.5zM4,16.5c-0.83,0 -1.5,0.68 -1.5,1.5s0.68,1.5 1.5,1.5 1.5,-0.68 1.5,-1.5 -0.67,-1.5 -1.5,-1.5zM7,19h14v-2L7,17v2zM7,13h14v-2L7,11v2zM7,5v2h14L21,5L7,5z"/>
+</vector>
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_common_cases.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_common_cases.xml
index 2d8078e..5b2223c 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_common_cases.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/fragment_common_cases.xml
@@ -96,5 +96,16 @@
             app:itemLogo="@drawable/ic_view_module_black_24dp"
             app:labelText="@string/navigation_button_date_picker_credit_card_label"
             app:destinationActivityName="com.example.android.autofill.app.commoncases.CreditCardDatePickerActivity" />
+
+        <com.example.android.autofill.app.view.widget.NavigationItem
+            android:id="@+id/recyclerViewButton"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:imageColor="@android:color/holo_orange_light"
+            app:infoText="@string/recycler_view_info"
+            app:itemLogo="@drawable/ic_format_list_bulleted_black_24dp"
+            app:labelText="@string/navigation_button_recycler_view_label"
+            app:destinationActivityName="com.example.android.autofill.app.commoncases.RecyclerViewActivity"
+            app:minSdk="28"/>
     </LinearLayout>
 </ScrollView>
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/recycler_view_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/recycler_view_activity.xml
new file mode 100644
index 0000000..50090ef
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/recycler_view_activity.xml
@@ -0,0 +1,66 @@
+<?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.
+-->
+<LinearLayout 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:orientation="vertical"
+    android:weightSum="1">
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/recyclerView"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="0.8"
+        tools:listItem="@layout/user_data_field" />
+
+    <android.support.constraint.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0.2">
+
+        <TextView
+            android:id="@+id/clearButton"
+            style="@style/Widget.AppCompat.Button.Borderless"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/spacing_normal"
+            android:layout_marginStart="@dimen/spacing_normal"
+            android:layout_marginTop="@dimen/spacing_normal"
+            android:text="@string/clear_label"
+            android:textColor="@android:color/holo_blue_dark"
+            app:layout_constraintEnd_toStartOf="@+id/submitButton"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintHorizontal_chainStyle="packed"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="parent" />
+
+        <TextView
+            android:id="@+id/submitButton"
+            style="@style/Widget.AppCompat.Button.Borderless"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="@dimen/spacing_normal"
+            android:layout_marginStart="@dimen/spacing_normal"
+            android:text="@string/submit_label"
+            android:textColor="@android:color/holo_blue_dark"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0.5"
+            app:layout_constraintStart_toEndOf="@+id/clearButton"
+            app:layout_constraintTop_toTopOf="@+id/clearButton" />
+    </android.support.constraint.ConstraintLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/user_data_field.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/user_data_field.xml
new file mode 100644
index 0000000..26f0b2e
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/user_data_field.xml
@@ -0,0 +1,58 @@
+<?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"
+    android:layout_marginTop="@dimen/activity_vertical_margin"
+    android:layout_marginBottom="@dimen/activity_vertical_margin"
+    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"
+        android:layout_marginTop="@dimen/spacing_normal"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@+id/field"
+        app:layout_constraintBottom_toBottomOf="@+id/field"
+        tools:background="@drawable/ic_person_black_24dp" />
+
+    <TextView
+        android:id="@+id/label"
+        style="@style/TextAppearance.AppCompat.Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        app:layout_constraintBottom_toBottomOf="@+id/field"
+        app:layout_constraintTop_toTopOf="@+id/field"
+        app:layout_constraintStart_toEndOf="@+id/icon"
+        tools:text="@string/recycler_view_label_name" />
+
+    <EditText
+        android:id="@+id/field"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        android:ems="10"
+        app:layout_constraintStart_toEndOf="@+id/label"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:text="Doug" />
+</android.support.constraint.ConstraintLayout>
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml b/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml
index bfadd1a..01328b8 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/attrs.xml
@@ -20,6 +20,7 @@
         <attr name="destinationActivityName" format="string" />
         <attr name="itemLogo" format="integer" />
         <attr name="imageColor" format="reference" />
+        <attr name="minSdk" format="integer" />
     </declare-styleable>
     <declare-styleable name="InfoButton">
         <attr name="dialogText" format="string" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
index a8441ba..7f2c646 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
@@ -27,6 +27,7 @@
     <string name="navigation_button_email_compose_label">Sample Email Compose Using EditTexts</string>
     <string name="navigation_button_compound_view_credit_card_label">Sample Credit Card Check Out Using Compound Views</string>
     <string name="navigation_button_date_picker_credit_card_label">Sample Credit Card Check Out Using Date Picker</string>
+    <string name="navigation_button_recycler_view_label">Sample Credit Card Form Using RecyclerView</string>
     <string name="navigation_button_multiple_partitions_label">Sample Page with Multiple Data Partitions</string>
     <string name="navigation_button_web_view_login_label">Sample Login Using a WebView</string>
     <string name="navigation_button_anti_pattern_credit_card_label">Sample Credit Card Anti Pattern</string>
@@ -109,6 +110,11 @@
         While the EditText is optimized out of the box for autofill, this example shows how to
         use it to autofill a date field.
     </string>
+    <string name="recycler_view_info">
+        This is a sample form that has a list of fields in a RecyclerView. It demos the new APIs in
+        API 28 that allow you to dynamically change the AutofillId associated with a View. This is
+        needed when Views are being recycled, as they are in a RecyclerView.
+    </string>
     <string name="multiple_partitions">
         This is a sample page that contains multiple partitions (login credentials, address,
         credit card info) and can be used to make sure that only one partition can be autofilled
@@ -207,4 +213,18 @@
         <item>27</item>
     </string-array>
 
+    <string name="recycler_view_label_name">Name</string>
+    <string name="recycler_view_label_birthday_month">Birthday Month</string>
+    <string name="recycler_view_label_birthday_year">Birthday Year</string>
+    <string name="recycler_view_label_email">Email</string>
+    <string name="recycler_view_label_phone">Telephone</string>
+    <string name="recycler_view_label_tel_extension">Tel. Extension</string>
+    <string name="recycler_view_label_cc_number">CC Number</string>
+    <string name="recycler_view_label_cc_sc">CSC</string>
+    <string name="recycler_view_label_cc_exp_month">CC Exp Month</string>
+    <string name="recycler_view_label_cc_exp_year">CC Exp Year</string>
+    <string name="recycler_view_label_address_line_1">Address Line 1</string>
+    <string name="recycler_view_label_address_line_2">Address Line 2</string>
+    <string name="recycler_view_label_address_line_3">Address Line 3</string>
+    <string name="recycler_view_label_postal_code">Postal Code</string>
 </resources>
diff --git a/input/autofill/AutofillFramework/afservice/src/main/res/raw/default_field_types b/input/autofill/AutofillFramework/afservice/src/main/res/raw/default_field_types
index 637edea..a0e6b26 100644
--- a/input/autofill/AutofillFramework/afservice/src/main/res/raw/default_field_types
+++ b/input/autofill/AutofillFramework/afservice/src/main/res/raw/default_field_types
@@ -388,8 +388,20 @@
         3
       ],
       "fakeData": {
-        "dateTemplate": "curr_time",
-        "strictExampleSet": []
+        "strictExampleSet": [
+          "2000",
+          "1995",
+          "1990",
+          "1985",
+          "1980",
+          "1975",
+          "1970",
+          "1965",
+          "1960",
+          "1955",
+          "1950",
+          "1945"
+        ]
       },
       "partition": 0,
       "saveInfo": 0,