Bunch of improvements on Autofill samples:

- Created CreditCardAntiPatternActivity that uses text field for credit card
  expiration date.
- Renamed CreditCardAcitivity to CreditCardSpinnersActivity.
- Created a new CreditCardAntiActivity that uses text field for individual
  credit card expiration date fields (day, month, year).
- Added a helper method to dump an AssistStructure.
- Changed CustomVirtualView to accept fields of type AUTOFILL_TYPE_DATE.
- Changed MultiplePartitionsActivity to use either date or text for credit card
  expiration dates.
- Removed invisiblity property from CustomVirtualView lines.

Bug: 65384186
Bug: 65673410

Test: manual verification

Change-Id: I02e80a87ce14792dbd7c50b1c9e9665be38babb1
diff --git a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
index 2bef2c8..a5abfe8 100644
--- a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
@@ -47,6 +47,9 @@
             android:name=".app.CreditCardActivity"
             android:taskAffinity=".CreditCardActivity" />
         <activity
+            android:name=".app.CreditCardSpinnersActivity"
+            android:taskAffinity=".CreditCardSpinnersActivity" />
+        <activity
             android:name=".app.EmailComposeActivity"
             android:taskAffinity=".EmailComposeActivity" />
         <activity
@@ -56,6 +59,9 @@
             android:name=".app.CreditCardDatePickerActivity"
             android:taskAffinity=".CreditCardDatePickerActivity" />
         <activity
+            android:name=".app.CreditCardAntiPatternActivity"
+            android:taskAffinity=".CreditCardAntiPatternActivity" />
+        <activity
             android:name=".app.MultiplePartitionsActivity"
             android:taskAffinity=".MultiplePartitionsActivity"
             android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java
index 9e95eaf..55d0761 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java
@@ -15,7 +15,13 @@
  */
 package com.example.android.autofillframework;
 
+import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.WindowNode;
 import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewStructure.HtmlInfo;
 
 import java.util.Arrays;
 import java.util.Set;
@@ -51,4 +57,78 @@
         bundleToString(builder, data);
         return builder.toString();
     }
+
+    public static String getTypeAsString(int type) {
+        switch (type) {
+            case View.AUTOFILL_TYPE_TEXT:
+                return "TYPE_TEXT";
+            case View.AUTOFILL_TYPE_LIST:
+                return "TYPE_LIST";
+            case View.AUTOFILL_TYPE_NONE:
+                return "TYPE_NONE";
+            case View.AUTOFILL_TYPE_TOGGLE:
+                return "TYPE_TOGGLE";
+            case View.AUTOFILL_TYPE_DATE:
+                return "TYPE_DATE";
+        }
+        return "UNKNOWN_TYPE";
+    }
+
+    public static void dumpStructure(AssistStructure structure) {
+        int nodeCount = structure.getWindowNodeCount();
+        Log.v(TAG, "dumpStructure(): component=" + structure.getActivityComponent()
+                + " numberNodes=" + nodeCount);
+        for (int i = 0; i < nodeCount; i++) {
+            Log.v(TAG, "node #" + i);
+            WindowNode node = structure.getWindowNodeAt(i);
+            dumpNode("  ", node.getRootViewNode());
+        }
+    }
+
+    private static void dumpNode(String prefix, ViewNode node) {
+        StringBuilder builder = new StringBuilder();
+        String[] hints = node.getAutofillHints();
+        builder.append(prefix)
+                .append("autoFillId: ").append(node.getAutofillId())
+                .append("\tidEntry: ").append(node.getIdEntry())
+                .append("\tid: ").append(node.getId())
+                .append("\thints: ").append(hints == null ? "N/A" : Arrays.toString(hints))
+                .append("\tclassName: ").append(node.getClassName())
+                .append('\n');
+
+        builder.append(prefix)
+                .append("focused: ").append(node.isFocused())
+                .append("\tvisibility").append(node.getVisibility())
+                .append("\tchecked: ").append(node.isChecked())
+                .append("\tURL: ").append(node.getWebDomain())
+                .append('\n');
+
+        HtmlInfo htmlInfo = node.getHtmlInfo();
+
+        if (htmlInfo != null) {
+            builder.append(prefix)
+                    .append("HTML TAG: ").append(htmlInfo.getTag())
+                    .append(" attrs: ").append(htmlInfo.getAttributes())
+                    .append('\n');
+        }
+
+        CharSequence[] options = node.getAutofillOptions();
+        builder.append(prefix).append("afType: ").append(getTypeAsString(node.getAutofillType()))
+                .append("\tafValue:").append(node.getAutofillValue())
+                .append("\tafOptions:").append(options == null ? "N/A" : Arrays.toString(options))
+                .append("\tinputType:").append(node.getInputType())
+                .append('\n');
+
+        int numberChildren = node.getChildCount();
+        builder.append(prefix).append("# children: ").append(numberChildren)
+                .append("\ttext: ").append(node.getText())
+                .append('\n');
+
+        Log.v(TAG, builder.toString());
+        final String prefix2 = prefix + "  ";
+        for (int i = 0; i < numberChildren; i++) {
+            Log.v(TAG, prefix + "child #" + i);
+            dumpNode(prefix2, node.getChildAt(i));
+        }
+    }
 }
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java
index e540a36..83e8614 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java
@@ -18,27 +18,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.annotation.Nullable;
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.view.autofill.AutofillManager;
-import android.widget.ArrayAdapter;
 import android.widget.EditText;
-import android.widget.Spinner;
 
 import com.example.android.autofillframework.R;
 
-import java.util.Calendar;
-
 public class CreditCardActivity extends AppCompatActivity {
 
-    private static final int CC_EXP_YEARS_COUNT = 5;
-
-    private final String[] years = new String[CC_EXP_YEARS_COUNT];
-
-    private Spinner mCcExpirationDaySpinner;
-    private Spinner mCcExpirationMonthSpinner;
-    private Spinner mCcExpirationYearSpinner;
-    private EditText mCcCardNumber;
+    private EditText mCcExpDayView;
+    private EditText mCcExpMonthView;
+    private EditText mCcExpYearView;
+    private EditText mCcNumber;
     private EditText mCcSecurityCode;
 
     public static Intent getStartActivityIntent(Context context) {
@@ -47,45 +40,21 @@
     }
 
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.credit_card_activity);
-        mCcExpirationDaySpinner = findViewById(R.id.expirationDay);
-        mCcExpirationMonthSpinner = findViewById(R.id.expirationMonth);
-        mCcExpirationYearSpinner = findViewById(R.id.expirationYear);
-        mCcCardNumber = findViewById(R.id.creditCardNumberField);
+        mCcExpDayView = findViewById(R.id.expirationDay);
+        mCcExpMonthView = findViewById(R.id.expirationMonth);
+        mCcExpYearView = findViewById(R.id.expirationYear);
+        mCcNumber = findViewById(R.id.creditCardNumberField);
         mCcSecurityCode = findViewById(R.id.creditCardSecurityCode);
-
-        // Create an ArrayAdapter using the string array and a default spinner layout
-        ArrayAdapter<CharSequence> dayAdapter = ArrayAdapter.createFromResource
-                (this, R.array.day_array, android.R.layout.simple_spinner_item);
-        // Specify the layout to use when the list of choices appears
-        dayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        // Apply the adapter to the spinner
-        mCcExpirationDaySpinner.setAdapter(dayAdapter);
-
-        ArrayAdapter<CharSequence> monthAdapter = ArrayAdapter.createFromResource
-                (this, R.array.month_array, android.R.layout.simple_spinner_item);
-        monthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-        mCcExpirationMonthSpinner.setAdapter(monthAdapter);
-        int year = Calendar.getInstance().get(Calendar.YEAR);
-        for (int i = 0; i < years.length; i++) {
-            years[i] = Integer.toString(year + i);
-        }
-        mCcExpirationYearSpinner.setAdapter(
-                new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, years) {
-                    @Override
-                    public CharSequence[] getAutofillOptions() {
-                        return years;
-                    }
-                });
-        findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() {
+        findViewById(R.id.submitButton).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 submit();
             }
         });
-        findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
+        findViewById(R.id.clearButton).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 getSystemService(AutofillManager.class).cancel();
@@ -95,10 +64,10 @@
     }
 
     private void resetFields() {
-        mCcExpirationDaySpinner.setSelection(0);
-        mCcExpirationMonthSpinner.setSelection(0);
-        mCcExpirationYearSpinner.setSelection(0);
-        mCcCardNumber.setText("");
+        mCcExpDayView.setText("");
+        mCcExpMonthView.setText("");
+        mCcExpYearView.setText("");
+        mCcNumber.setText("");
         mCcSecurityCode.setText("");
     }
 
@@ -107,7 +76,7 @@
      * any new data.
      */
     private void submit() {
-        Intent intent = WelcomeActivity.getStartActivityIntent(CreditCardActivity.this);
+        Intent intent = WelcomeActivity.getStartActivityIntent(this);
         startActivity(intent);
         finish();
     }
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardAntiPatternActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardAntiPatternActivity.java
new file mode 100644
index 0000000..a7c1429
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardAntiPatternActivity.java
@@ -0,0 +1,77 @@
+/*
+ * 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.autofillframework.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.autofill.AutofillManager;
+import android.widget.EditText;
+
+import com.example.android.autofillframework.R;
+
+public class CreditCardAntiPatternActivity extends AppCompatActivity {
+
+    private EditText mCcExpDateView;
+    private EditText mCcExpNumber;
+    private EditText mCcSecurityCode;
+
+    public static Intent getStartActivityIntent(Context context) {
+        Intent intent = new Intent(context, CreditCardAntiPatternActivity.class);
+        return intent;
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.credit_card_anti_pattern_activity);
+        mCcExpDateView = findViewById(R.id.creditCardExpirationView);
+        mCcExpNumber = findViewById(R.id.creditCardNumberField);
+        mCcSecurityCode = findViewById(R.id.creditCardSecurityCode);
+        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) {
+                getSystemService(AutofillManager.class).cancel();
+                resetFields();
+            }
+        });
+    }
+
+    private void resetFields() {
+        mCcExpDateView.setText("");
+        mCcExpNumber.setText("");
+        mCcSecurityCode.setText("");
+    }
+
+    /**
+     * Launches new Activity and finishes, triggering an autofill save request if the user entered
+     * any new data.
+     */
+    private void submit() {
+        Intent intent = WelcomeActivity.getStartActivityIntent(this);
+        startActivity(intent);
+        finish();
+    }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java
index 0fac7d9..afa105e 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardExpirationDateCompoundView.java
@@ -19,6 +19,7 @@
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.autofill.AutofillManager;
@@ -32,6 +33,8 @@
 
 import java.util.Calendar;
 
+import static com.example.android.autofillframework.CommonUtil.TAG;
+
 /**
  * A custom view that represents a {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} using
  * 2 {@link Spinner spinners} to represent the credit card expiration month and year.
@@ -109,6 +112,7 @@
     @Override
     public void autofill(AutofillValue value) {
         if (!value.isDate()) {
+            Log.w(TAG, "Ignoring autofill() because service sent a non-date value:" + value);
             return;
         }
         Calendar calendar = Calendar.getInstance();
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardSpinnersActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardSpinnersActivity.java
new file mode 100644
index 0000000..6b9a823
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardSpinnersActivity.java
@@ -0,0 +1,114 @@
+/*
+ * 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.autofillframework.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.autofill.AutofillManager;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import com.example.android.autofillframework.R;
+
+import java.util.Calendar;
+
+public class CreditCardSpinnersActivity extends AppCompatActivity {
+
+    private static final int CC_EXP_YEARS_COUNT = 5;
+
+    private final String[] years = new String[CC_EXP_YEARS_COUNT];
+
+    private Spinner mCcExpirationDaySpinner;
+    private Spinner mCcExpirationMonthSpinner;
+    private Spinner mCcExpirationYearSpinner;
+    private EditText mCcCardNumber;
+    private EditText mCcSecurityCode;
+
+    public static Intent getStartActivityIntent(Context context) {
+        Intent intent = new Intent(context, CreditCardSpinnersActivity.class);
+        return intent;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.credit_card_spinners_activity);
+        mCcExpirationDaySpinner = findViewById(R.id.expirationDay);
+        mCcExpirationMonthSpinner = findViewById(R.id.expirationMonth);
+        mCcExpirationYearSpinner = findViewById(R.id.expirationYear);
+        mCcCardNumber = findViewById(R.id.creditCardNumberField);
+        mCcSecurityCode = findViewById(R.id.creditCardSecurityCode);
+
+        // Create an ArrayAdapter using the string array and a default spinner layout
+        ArrayAdapter<CharSequence> dayAdapter = ArrayAdapter.createFromResource
+                (this, R.array.day_array, android.R.layout.simple_spinner_item);
+        // Specify the layout to use when the list of choices appears
+        dayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        // Apply the adapter to the spinner
+        mCcExpirationDaySpinner.setAdapter(dayAdapter);
+
+        ArrayAdapter<CharSequence> monthAdapter = ArrayAdapter.createFromResource
+                (this, R.array.month_array, android.R.layout.simple_spinner_item);
+        monthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        mCcExpirationMonthSpinner.setAdapter(monthAdapter);
+        int year = Calendar.getInstance().get(Calendar.YEAR);
+        for (int i = 0; i < years.length; i++) {
+            years[i] = Integer.toString(year + i);
+        }
+        mCcExpirationYearSpinner.setAdapter(
+                new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, years) {
+                    @Override
+                    public CharSequence[] getAutofillOptions() {
+                        return years;
+                    }
+                });
+        findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                submit();
+            }
+        });
+        findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                getSystemService(AutofillManager.class).cancel();
+                resetFields();
+            }
+        });
+    }
+
+    private void resetFields() {
+        mCcExpirationDaySpinner.setSelection(0);
+        mCcExpirationMonthSpinner.setSelection(0);
+        mCcExpirationYearSpinner.setSelection(0);
+        mCcCardNumber.setText("");
+        mCcSecurityCode.setText("");
+    }
+
+    /**
+     * Launches new Activity and finishes, triggering an autofill save request if the user entered
+     * any new data.
+     */
+    private void submit() {
+        Intent intent = WelcomeActivity.getStartActivityIntent(CreditCardSpinnersActivity.this);
+        startActivity(intent);
+        finish();
+    }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java
index 6c3b365..f1fd433 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java
@@ -36,10 +36,13 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.example.android.autofillframework.CommonUtil;
 import com.example.android.autofillframework.R;
 
+import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 
 import static com.example.android.autofillframework.CommonUtil.bundleToString;
 
@@ -93,6 +96,8 @@
 
     @Override
     public void autofill(SparseArray<AutofillValue> values) {
+        Context context = getContext();
+
         // User has just selected a Dataset from the list of autofill suggestions.
         // The Dataset is comprised of a list of AutofillValues, with each AutofillValue meant
         // to fill a specific autofillable view. Now we have to update the UI based on the
@@ -106,7 +111,7 @@
             int id = values.keyAt(i);
             Partition partition = mPartitionsByAutofillId.get(id);
             if (partition == null) {
-                showError(getContext().getString(R.string.message_autofill_no_partitions, id,
+                showError(context.getString(R.string.message_autofill_no_partitions, id,
                         mPartitionsByAutofillId));
                 return;
             }
@@ -115,26 +120,49 @@
 
         // Then make sure they follow the Highlander rule (There can be only one)
         if (partitions.size() != 1) {
-            showError(getContext().getString(R.string.message_autofill_blocked, partitions));
+            showError(context.getString(R.string.message_autofill_blocked, partitions));
             return;
         }
 
         // Finally, autofill it.
+        DateFormat df = android.text.format.DateFormat.getDateFormat(context);
         for (int i = 0; i < values.size(); i++) {
             int id = values.keyAt(i);
             AutofillValue value = values.valueAt(i);
             Item item = mVirtualViews.get(id);
-            if (item != null && item.editable) {
-                // Set the item's text to the text wrapped in the AutofillValue.
-                item.text = value.getTextValue();
-            } else if (item == null) {
+
+            if (item == null) {
                 Log.w(TAG, "No item for id " + id);
+                continue;
+            }
+
+            if (!item.editable) {
+                showError(context.getString(R.string.message_autofill_readonly, item.text));
+                continue;
+            }
+
+            // Check if the type was properly set by the autofill service
+            if (DEBUG) {
+                Log.d(TAG, "Validating " + i
+                        + ": expectedType=" + CommonUtil.getTypeAsString(item.type)
+                        + "(" + item.type + "), value=" + value);
+            }
+            boolean valid = false;
+            if (value.isText() && item.type == AUTOFILL_TYPE_TEXT) {
+                item.text = value.getTextValue();
+                valid = true;
+            } else if (value.isDate() && item.type == AUTOFILL_TYPE_DATE) {
+                item.text = df.format(new Date(value.getDateValue()));
+                valid = true;
             } else {
-                Log.w(TAG, "Item for id " + id + " is not editable: " + item);
+                Log.w(TAG, "Unsupported type: " + value);
+            }
+            if (!valid) {
+                item.text = context.getString(R.string.message_autofill_invalid);
             }
         }
         postInvalidate();
-        showMessage(getContext().getString(R.string.message_autofill_ok, partitions.valueAt(0)));
+        showMessage(context.getString(R.string.message_autofill_ok, partitions.valueAt(0)));
     }
 
     @Override
@@ -158,13 +186,13 @@
             child.setAutofillHints(item.hints);
             child.setAutofillType(item.type);
             child.setDataIsSensitive(!item.sanitized);
-            if (TextUtils.getTrimmedLength(item.text) > 0) {
-                child.setAutofillValue(AutofillValue.forText(item.text));
-            }
+            child.setAutofillValue(item.getAutofillValue());
             child.setFocused(item.focused);
+            child.setVisibility(View.VISIBLE);
+            child.setDimens(item.line.mBounds.left, item.line.mBounds.top, 0, 0,
+                    item.line.mBounds.width(), item.line.mBounds.height());
             child.setId(item.id, getContext().getPackageName(), null, item.line.mIdEntry);
             child.setClassName(item.getClassName());
-            child.setVisibility(item.line.mVisible ? View.VISIBLE : View.INVISIBLE);
             child.setDimens(item.line.mBounds.left, item.line.mBounds.top, 0, 0,
                     item.line.mBounds.width(), item.line.mBounds.height());
             index++;
@@ -182,10 +210,6 @@
         float y = mTopMargin + mLineLength;
         for (int i = 0; i < mVirtualViewGroups.size(); i++) {
             Line line = mVirtualViewGroups.get(i);
-            if (!line.mVisible) {
-                if (VERBOSE) Log.v(TAG, "onDraw(): skipping invisible line " + line);
-                continue;
-            }
             x = mLeftMargin;
             if (VERBOSE) Log.v(TAG, "Drawing '" + line + "' at " + x + "x" + y);
             mTextPaint.setColor(line.mFieldTextItem.focused ? FOCUSED_COLOR : UNFOCUSED_COLOR);
@@ -224,10 +248,6 @@
         int upperY = -1;
         for (int i = 0; i < mVirtualViewGroups.size(); i++) {
             Line line = mVirtualViewGroups.get(i);
-            if (!line.mVisible) {
-                if (VERBOSE) Log.v(TAG, "onMotion(): skipping invisible line " + line);
-                continue;
-            }
             upperY = lowerY + mLineLength;
             if (DEBUG) Log.d(TAG, "Line " + i + " ranges from " + lowerY + " to " + upperY);
             if (lowerY <= y && y <= upperY) {
@@ -290,6 +310,7 @@
         private final int type;
         private CharSequence text;
         private boolean focused = false;
+        private long date;
 
         Item(Line line, int id, String[] hints, int type, CharSequence text, boolean editable,
                 boolean sanitized) {
@@ -304,13 +325,30 @@
 
         @Override
         public String toString() {
-            return id + ": " + text + (editable ? " (editable)" : " (read-only)"
-                    + (sanitized ? " (sanitized)" : " (sensitive"));
+            return id + ": "
+                    + (type == AUTOFILL_TYPE_DATE ? date : text) // TODO: use DateFormater for date
+                    + " (" + CommonUtil.getTypeAsString(type) + ")"
+                    + (editable ? " (editable)" : " (read-only)"
+                    + (sanitized ? " (sanitized)" : " (sensitive"))
+                    + (hints == null ? " (no hints)" : " ( " + Arrays.toString(hints) + ")");
         }
 
         public String getClassName() {
             return editable ? EditText.class.getName() : TextView.class.getName();
         }
+
+        public AutofillValue getAutofillValue() {
+            switch (type) {
+                case AUTOFILL_TYPE_TEXT:
+                    if (TextUtils.getTrimmedLength(text) > 0) {
+                        return AutofillValue.forText(text);
+                    }
+                case AUTOFILL_TYPE_DATE:
+                    return AutofillValue.forDate(date);
+                default:
+                    return null;
+            }
+        }
     }
 
     /**
@@ -327,16 +365,22 @@
         /**
          * Adds a new line (containining a label and an input field) to the view.
          *
-         * @param idEntry   id used to identify the line.
-         * @param label     text used in the label.
-         * @param text      initial text used in the input field.
-         * @param sensitive whether the input is considered sensitive.
-         * @param hints     list of autofill hints.
+         * @param idEntry       id used to identify the line.
+         * @param autofillType  {@link View#getAutofillType() autofill type} of the field.
+         * @param label         text used in the label.
+         * @param text          initial text used in the input field.
+         * @param sensitive     whether the input is considered sensitive.
+         * @param autofillHints list of autofill hints.
          * @return the new line.
          */
-        public Line addLine(String idEntry, String label, String text, boolean sensitive,
-                String... hints) {
-            Line line = new Line(idEntry, label, hints, text, !sensitive);
+        public Line addLine(String idEntry, int autofillType, String label, String text,
+                boolean sensitive, String... autofillHints) {
+            // TODO: use PreConditions
+            if (autofillType != AUTOFILL_TYPE_TEXT && autofillType != AUTOFILL_TYPE_DATE) {
+                throw new IllegalArgumentException("unsupported type: " + autofillType);
+            }
+
+            Line line = new Line(idEntry, autofillType, label, autofillHints, text, !sensitive);
             mVirtualViewGroups.add(line);
             int id = line.mFieldTextItem.id;
             mLines.put(id, line);
@@ -356,16 +400,6 @@
             }
         }
 
-        /**
-         * Sets whether the lines in this partition are visible or not.
-         */
-        public void setVisibility(boolean visible) {
-            for (int i = 0; i < mLines.size(); i++) {
-                mLines.valueAt(i).mVisible = visible;
-            }
-            invalidate();
-        }
-
         @Override
         public String toString() {
             return mName;
@@ -382,13 +416,15 @@
         private final Rect mBounds = new Rect();
         private final Item mLabelItem;
         private final String mIdEntry;
-        private boolean mVisible = true;
+        private final int mAutofillType;
 
-        private Line(String idEntry, String label, String[] hints, String text, boolean sanitized) {
+        private Line(String idEntry, int autofillType, String label, String[] hints, String text,
+                boolean sanitized) {
             this.mIdEntry = idEntry;
+            this.mAutofillType = autofillType;
             this.mLabelItem = new Item(this, ++sNextId, null, AUTOFILL_TYPE_NONE, label,
                     false, true);
-            this.mFieldTextItem = new Item(this, ++sNextId, hints, AUTOFILL_TYPE_TEXT, text,
+            this.mFieldTextItem = new Item(this, ++sNextId, hints, autofillType, text,
                     true, sanitized);
         }
 
@@ -438,7 +474,7 @@
         @Override
         public String toString() {
             return "Label: " + mLabelItem + " Text: " + mFieldTextItem + " Focused: " +
-                    mFieldTextItem.focused + " Visible: " + mVisible;
+                    mFieldTextItem.focused + " Type: " + mAutofillType;
         }
     }
 }
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java
index 48ec4b0..0e139f9 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java
@@ -42,12 +42,14 @@
         setContentView(R.layout.activity_main);
         NavigationItem loginEditTexts = findViewById(R.id.standardViewSignInButton);
         NavigationItem loginCustomVirtual = findViewById(R.id.virtualViewSignInButton);
-        NavigationItem creditCardSpinners = findViewById(R.id.creditCardCheckoutButton);
+        NavigationItem creditCard = findViewById(R.id.creditCardButton);
+        NavigationItem creditCardSpinners = findViewById(R.id.creditCardSpinnersButton);
         NavigationItem loginAutoComplete = findViewById(R.id.standardLoginWithAutoCompleteButton);
         NavigationItem emailCompose = findViewById(R.id.emailComposeButton);
         NavigationItem creditCardCompoundView = findViewById(R.id.creditCardCompoundViewButton);
         NavigationItem creditCardDatePicker = findViewById(R.id.creditCardDatePickerButton);
-        NavigationItem mulitplePartitions = findViewById(R.id.multiplePartitionsButton);
+        NavigationItem creditCardAntiPatternPicker = findViewById(R.id.creditCardAntiPatternButton);
+        NavigationItem multiplePartitions = findViewById(R.id.multiplePartitionsButton);
         NavigationItem loginWebView = findViewById(R.id.webviewSignInButton);
         loginEditTexts.setNavigationButtonClickListener(new View.OnClickListener() {
             @Override
@@ -61,12 +63,18 @@
                 startActivity(VirtualSignInActivity.getStartActivityIntent(MainActivity.this));
             }
         });
-        creditCardSpinners.setNavigationButtonClickListener(new View.OnClickListener() {
+        creditCard.setNavigationButtonClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 startActivity(CreditCardActivity.getStartActivityIntent(MainActivity.this));
             }
         });
+        creditCardSpinners.setNavigationButtonClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                startActivity(CreditCardSpinnersActivity.getStartActivityIntent(MainActivity.this));
+            }
+        });
         loginAutoComplete.setNavigationButtonClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
@@ -91,7 +99,13 @@
                 startActivity(CreditCardDatePickerActivity.getStartActivityIntent(MainActivity.this));
             }
         });
-        mulitplePartitions.setNavigationButtonClickListener(new View.OnClickListener() {
+        creditCardAntiPatternPicker.setNavigationButtonClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                startActivity(CreditCardAntiPatternActivity.getStartActivityIntent(MainActivity.this));
+            }
+        });
+        multiplePartitions.setNavigationButtonClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 startActivity(MultiplePartitionsActivity.getStartActivityIntent(MainActivity.this));
@@ -114,6 +128,8 @@
                 try {
                     Intent newIntent = new Intent(this,
                             Class.forName("com.example.android.autofillframework." + target));
+                    newIntent.putExtras(intent);
+                    newIntent.removeExtra("target");
                     getApplicationContext().startActivity(newIntent);
                     finish();
                     return true;
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java
index ed380ae..319ed19 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MultiplePartitionsActivity.java
@@ -21,7 +21,9 @@
 import android.support.v7.app.AppCompatActivity;
 import android.view.View;
 import android.view.autofill.AutofillManager;
+import android.widget.Toast;
 
+import com.example.android.autofillframework.CommonUtil;
 import com.example.android.autofillframework.R;
 
 /**
@@ -32,10 +34,9 @@
 /*
  * TODO list
  *
- * - Use a combo box to select which partition is visible (will require changes on CustomView to
- *   hide a partition).
+ * - Fix top margin.
+ * - Use a combo box to select if credit card expiration date is expressed as date or text.
  * - Use a dedicated TextView (instead of Toast) for error messages.
- * - Resize line height based on number of lines on screen.
  * - Use wrap_context to CustomView container.
  * - Use different background color (or borders) for each partition.
  * - Add more partitions (like address) - should match same partitions from service.
@@ -62,30 +63,49 @@
 
         mCustomVirtualView = findViewById(R.id.custom_view);
 
+
         mCredentialsPartition =
                 mCustomVirtualView.addPartition(getString(R.string.partition_credentials));
-        mCredentialsPartition.addLine("username", getString(R.string.username_label),
+        mCredentialsPartition.addLine("username", View.AUTOFILL_TYPE_TEXT,
+                getString(R.string.username_label),
                 "         ", false, View.AUTOFILL_HINT_USERNAME);
-        mCredentialsPartition.addLine("password", getString(R.string.password_label),
+        mCredentialsPartition.addLine("password", View.AUTOFILL_TYPE_TEXT,
+                getString(R.string.password_label),
                 "         ", true, View.AUTOFILL_HINT_PASSWORD);
 
-        mCcPartition = mCustomVirtualView.addPartition(getString(R.string.partition_credit_card));
-        mCcPartition.addLine("ccNumber", getString(R.string.credit_card_number_label),
-                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
-        mCcPartition.addLine("ccDay", getString(R.string.credit_card_expiration_day_label),
-                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY);
-        mCcPartition.addLine("ccMonth", getString(R.string.credit_card_expiration_month_label),
-                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
-        mCcPartition.addLine("ccYear", getString(R.string.credit_card_expiration_year_label),
-                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
-        // TODO: figure out why expiration date is not being autofilled
-        mCcPartition.addLine("ccDate", getString(R.string.credit_card_expiration_date_label),
-                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
-        mCcPartition.addLine("ccSecurityCode", getString(R.string.credit_card_security_code_label),
-                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
+        int ccExpirationType = View.AUTOFILL_TYPE_DATE;
+        // TODO: add a checkbox to switch between text / date instead
+        Intent intent = getIntent();
+        if (intent != null) {
+            int newType = intent.getIntExtra("dateType", -1);
+            if (newType != -1) {
+                ccExpirationType = newType;
+                String typeMessage = getString(R.string.message_credit_card_expiration_type,
+                        CommonUtil.getTypeAsString(ccExpirationType));
+                // TODO: display type in a header or proper status widget
+                Toast.makeText(getApplicationContext(), typeMessage, Toast.LENGTH_LONG).show();
+            }
+        }
 
-        // TODO: add ComboBox that changes visibility of partitions
-        // mCcPartition.setVisibility(false);
+        mCcPartition = mCustomVirtualView.addPartition(getString(R.string.partition_credit_card));
+        mCcPartition.addLine("ccNumber", View.AUTOFILL_TYPE_TEXT,
+                getString(R.string.credit_card_number_label),
+                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
+        mCcPartition.addLine("ccDay", View.AUTOFILL_TYPE_TEXT,
+                getString(R.string.credit_card_expiration_day_label),
+                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY);
+        mCcPartition.addLine("ccMonth", ccExpirationType,
+                getString(R.string.credit_card_expiration_month_label),
+                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH);
+        mCcPartition.addLine("ccYear", View.AUTOFILL_TYPE_TEXT,
+                getString(R.string.credit_card_expiration_year_label),
+                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR);
+        mCcPartition.addLine("ccDate", ccExpirationType,
+                getString(R.string.credit_card_expiration_date_label),
+                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE);
+        mCcPartition.addLine("ccSecurityCode", View.AUTOFILL_TYPE_TEXT,
+                getString(R.string.credit_card_security_code_label),
+                "         ", true, View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE);
 
         findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
             @Override
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java
index 2267a68..de1259d 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.java
@@ -50,10 +50,10 @@
 
         CustomVirtualView.Partition credentialsPartition =
                 mCustomVirtualView.addPartition(getString(R.string.partition_credentials));
-        mUsernameLine = credentialsPartition.addLine("usernameField",
+        mUsernameLine = credentialsPartition.addLine("usernameField", View.AUTOFILL_TYPE_TEXT,
                 getString(R.string.username_label),
                 "         ", false, View.AUTOFILL_HINT_USERNAME);
-        mPasswordLine = credentialsPartition.addLine("passwordField",
+        mPasswordLine = credentialsPartition.addLine("passwordField", View.AUTOFILL_TYPE_TEXT,
                 getString(R.string.password_label),
                 "         ", true, View.AUTOFILL_HINT_PASSWORD);
 
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
index 047ee67..8fe9c4c 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
@@ -41,8 +41,10 @@
 import java.util.HashMap;
 import java.util.List;
 
+import static com.example.android.autofillframework.CommonUtil.DEBUG;
 import static com.example.android.autofillframework.CommonUtil.TAG;
 import static com.example.android.autofillframework.CommonUtil.bundleToString;
+import static com.example.android.autofillframework.CommonUtil.dumpStructure;
 
 public class MyAutofillService extends AutofillService {
 
@@ -59,7 +61,10 @@
             return;
         }
         final Bundle data = request.getClientState();
-        Log.d(TAG, "onFillRequest(): data=" + bundleToString(data));
+        if (DEBUG) {
+            Log.d(TAG, "onFillRequest(): data=" + bundleToString(data));
+            dumpStructure(structure);
+        }
 
         cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
             @Override
@@ -108,7 +113,10 @@
             return;
         }
         final Bundle data = request.getClientState();
-        Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data));
+        if (DEBUG) {
+            Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data));
+            dumpStructure(structure);
+        }
         StructureParser parser = new StructureParser(structure);
         parser.parseForSave();
         FilledAutofillFieldCollection filledAutofillFieldCollection = parser.getClientFormData();
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java
index 00f17e3..aaf5b16 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/StructureParser.java
@@ -32,13 +32,13 @@
  * parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
  */
 final class StructureParser {
-    private final AutofillFieldMetadataCollection mAutofillFields = new AutofillFieldMetadataCollection();
+    private final AutofillFieldMetadataCollection mAutofillFields =
+            new AutofillFieldMetadataCollection();
     private final AssistStructure mStructure;
     private FilledAutofillFieldCollection mFilledAutofillFieldCollection;
 
     StructureParser(AssistStructure structure) {
         mStructure = structure;
-
     }
 
     public void parseForFill() {
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
index 03df327..6ca5960 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
@@ -36,7 +36,6 @@
 import com.example.android.autofillframework.multidatasetservice.AutofillHints;
 import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
 import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
-import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillField;
 import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
 
 public class SettingsActivity extends AppCompatActivity {
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml
index 557259f..f2ff0dd 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/activity_main.xml
@@ -64,7 +64,7 @@
             app:labelText="@string/navigation_button_web_view_login_label" />
 
         <com.example.android.autofillframework.app.NavigationItem
-            android:id="@+id/creditCardCheckoutButton"
+            android:id="@+id/creditCardSpinnersButton"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             app:imageColor="@android:color/holo_orange_dark"
@@ -107,5 +107,23 @@
             app:labelText="@string/navigation_button_multiple_partitions_label"
             app:itemLogo="@drawable/ic_custom_virtual_logo_24dp"
             app:imageColor="@android:color/holo_green_dark"/>
+
+        <com.example.android.autofillframework.app.NavigationItem
+            android:id="@+id/creditCardButton"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:imageColor="@android:color/holo_orange_dark"
+            app:infoText="@string/credit_card_info"
+            app:itemLogo="@drawable/ic_spinners_logo_24dp"
+            app:labelText="@string/navigation_button_credit_card_label" />
+
+        <com.example.android.autofillframework.app.NavigationItem
+            android:id="@+id/creditCardAntiPatternButton"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            app:imageColor="@android:color/holo_orange_dark"
+            app:infoText="@string/anti_pattern_credit_card_info"
+            app:itemLogo="@drawable/ic_spinners_logo_24dp"
+            app:labelText="@string/navigation_button_anti_pattern_credit_card_label" />
     </LinearLayout>
 </ScrollView>
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_activity.xml
index ca7e560..630651a 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_activity.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -14,23 +13,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
 -->
+<!--  TODO: fix this layout -->
 <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/authLayout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:orientation="vertical"
     android:paddingBottom="@dimen/activity_vertical_margin"
     android:paddingLeft="@dimen/activity_horizontal_margin"
     android:paddingRight="@dimen/activity_horizontal_margin"
     android:paddingTop="@dimen/activity_vertical_margin">
 
     <TextView
-        android:id="@+id/spinners_credit_card_header"
+        android:id="@+id/standard_login_header"
         style="@style/TextAppearance.AppCompat.Large"
         android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:layout_marginTop="8dp"
         android:gravity="center"
-        android:text="@string/navigation_button_spinners_credit_card_label"
+        android:text="@string/navigation_button_credit_card_label"
+        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintEnd_toStartOf="@+id/imageButton"
         app:layout_constraintHorizontal_bias="0.5"
         app:layout_constraintHorizontal_chainStyle="spread"
@@ -41,93 +44,131 @@
         android:id="@+id/imageButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
         android:background="@drawable/ic_info_black_24dp"
-        app:layout_constraintBottom_toBottomOf="@+id/spinners_credit_card_header"
+        app:dialogText="@string/anti_pattern_credit_card_info"
+        app:layout_constraintBottom_toBottomOf="@+id/standard_login_header"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintStart_toEndOf="@+id/spinners_credit_card_header"
-        app:layout_constraintTop_toTopOf="@+id/spinners_credit_card_header"
-        app:dialogText="@string/spinners_credit_card_info" />
+        app:layout_constraintStart_toEndOf="@+id/standard_login_header"
+        app:layout_constraintTop_toTopOf="@+id/standard_login_header" />
 
     <TextView
         android:id="@+id/creditCardNumberLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        android:layout_marginStart="@dimen/spacing_normal"
         android:layout_marginTop="@dimen/spacing_large"
-        android:importantForAutofill="no"
         android:labelFor="@+id/creditCardNumberField"
         android:text="@string/credit_card_number_label"
-        app:layout_constraintStart_toStartOf="@+id/creditCardExpirationLabel"
-        app:layout_constraintTop_toBottomOf="@+id/spinners_credit_card_header" />
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/standard_login_header" />
 
     <EditText
         android:id="@+id/creditCardNumberField"
-        android:layout_width="0dp"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_alignBaseline="@+id/creditCardNumberLabel"
-        android:layout_marginEnd="8dp"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginTop="@dimen/spacing_normal"
         android:autofillHints="creditCardNumber"
-        android:focusedByDefault="true"
+        android:ems="12"
         android:inputType="number"
+        android:paddingHorizontal="@dimen/spacing_micro"
         app:layout_constraintBottom_toBottomOf="@+id/creditCardNumberLabel"
-        app:layout_constraintEnd_toEndOf="@+id/expirationYear"
-        app:layout_constraintStart_toStartOf="@+id/expirationDay"
+        app:layout_constraintStart_toEndOf="@+id/creditCardNumberLabel"
         app:layout_constraintTop_toTopOf="@+id/creditCardNumberLabel" />
 
     <TextView
-        android:id="@+id/creditCardExpirationLabel"
+        android:id="@+id/expirationDayLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/spacing_large"
         android:importantForAutofill="no"
-        android:text="@string/credit_card_expiration_label"
-        app:layout_constraintEnd_toStartOf="@+id/expirationDay"
+        android:labelFor="@+id/expirationDay"
+        android:text="@string/credit_card_expiration_day_label"
+        app:layout_constraintEnd_toStartOf="@+id/creditCardNumberLabel"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/creditCardNumberLabel" />
 
-    <Spinner
+    <EditText
         android:id="@+id/expirationDay"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginStart="8dp"
+        android:layout_alignBaseline="@+id/expirationDayLabel"
+        android:layout_marginEnd="8dp"
+        android:inputType="number"
         android:autofillHints="creditCardExpirationDay"
-        app:layout_constraintBottom_toBottomOf="@+id/creditCardExpirationLabel"
-        app:layout_constraintEnd_toStartOf="@+id/expirationMonth"
-        app:layout_constraintStart_toEndOf="@+id/creditCardNumberLabel"
-        app:layout_constraintTop_toTopOf="@+id/creditCardExpirationLabel" />
+        app:layout_constraintBottom_toBottomOf="@+id/expirationDayLabel"
+        app:layout_constraintEnd_toEndOf="@+id/creditCardNumberField"
+        app:layout_constraintStart_toStartOf="@+id/creditCardNumberField"
+        app:layout_constraintTop_toTopOf="@+id/expirationDayLabel" />
 
-    <Spinner
+    <TextView
+        android:id="@+id/expirationMonthLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:importantForAutofill="no"
+        android:labelFor="@+id/expirationMonth"
+        android:text="@string/credit_card_expiration_month_label"
+        app:layout_constraintEnd_toStartOf="@+id/expirationDay"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/expirationDay" />
+
+    <EditText
         android:id="@+id/expirationMonth"
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
+        android:layout_alignBaseline="@+id/expirationMonthLabel"
+        android:layout_marginEnd="8dp"
+        android:inputType="number"
         android:autofillHints="creditCardExpirationMonth"
-        app:layout_constraintBottom_toBottomOf="@+id/expirationDay"
-        app:layout_constraintEnd_toStartOf="@+id/expirationYear"
-        app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintStart_toEndOf="@+id/expirationDay"
-        app:layout_constraintTop_toTopOf="@+id/expirationDay" />
+        app:layout_constraintBottom_toBottomOf="@+id/expirationMonthLabel"
+        app:layout_constraintEnd_toEndOf="@+id/expirationDay"
+        app:layout_constraintStart_toStartOf="@+id/expirationDay"
+        app:layout_constraintTop_toTopOf="@+id/expirationMonthLabel" />
 
-    <Spinner
-        android:id="@+id/expirationYear"
+    <TextView
+        android:id="@+id/expirationYearLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:importantForAutofill="no"
+        android:labelFor="@+id/expirationYear"
+        android:text="@string/credit_card_expiration_year_label"
+        app:layout_constraintEnd_toStartOf="@+id/expirationMonth"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/expirationMonth" />
+
+    <EditText
+        android:id="@+id/expirationYear"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_alignBaseline="@+id/expirationYearLabel"
+        android:layout_marginEnd="8dp"
+        android:inputType="number"
         android:autofillHints="creditCardExpirationYear"
-        app:layout_constraintBottom_toBottomOf="@+id/expirationMonth"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintStart_toEndOf="@+id/expirationMonth"
-        app:layout_constraintTop_toTopOf="@+id/expirationMonth" />
+        app:layout_constraintBottom_toBottomOf="@+id/expirationYearLabel"
+        app:layout_constraintEnd_toEndOf="@+id/expirationMonth"
+        app:layout_constraintStart_toStartOf="@+id/expirationMonth"
+        app:layout_constraintTop_toTopOf="@+id/expirationYearLabel" />
 
     <TextView
         android:id="@+id/creditCardSecurityCodeLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/spacing_large"
         android:importantForAutofill="no"
         android:text="@string/credit_card_security_code_label"
-        app:layout_constraintEnd_toStartOf="@+id/expirationDate"
+        app:layout_constraintEnd_toStartOf="@+id/expirationYear"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationLabel" />
+        app:layout_constraintTop_toBottomOf="@+id/expirationYear" />
 
     <EditText
         android:id="@+id/creditCardSecurityCode"
@@ -138,27 +179,27 @@
         android:inputType="number"
         android:autofillHints="creditCardSecurityCode"
         app:layout_constraintBottom_toBottomOf="@+id/creditCardSecurityCodeLabel"
-        app:layout_constraintEnd_toEndOf="@+id/expirationMonth"
-        app:layout_constraintStart_toStartOf="@+id/expirationDay"
+        app:layout_constraintEnd_toEndOf="@+id/expirationYear"
+        app:layout_constraintStart_toStartOf="@+id/expirationYear"
         app:layout_constraintTop_toTopOf="@+id/creditCardSecurityCodeLabel" />
 
     <TextView
-        android:id="@+id/clear"
+        android:id="@+id/clearButton"
         style="@style/Widget.AppCompat.Button.Borderless"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/spacing_normal"
-        android:layout_marginTop="@dimen/spacing_large"
+        android:layout_marginTop="@dimen/spacing_normal"
         android:text="@string/clear_label"
         android:textColor="@android:color/holo_blue_dark"
-        app:layout_constraintEnd_toStartOf="@+id/submit"
+        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="@+id/creditCardSecurityCodeLabel" />
+        app:layout_constraintTop_toBottomOf="@+id/creditCardSecurityCode" />
 
     <TextView
-        android:id="@+id/submit"
+        android:id="@+id/submitButton"
         style="@style/Widget.AppCompat.Button.Borderless"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -168,6 +209,7 @@
         android:textColor="@android:color/holo_blue_dark"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0.5"
-        app:layout_constraintStart_toEndOf="@+id/clear"
-        app:layout_constraintTop_toTopOf="@+id/clear" />
+        app:layout_constraintStart_toEndOf="@+id/clearButton"
+        app:layout_constraintTop_toTopOf="@+id/clearButton" />
+
 </android.support.constraint.ConstraintLayout>
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_anti_pattern_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_anti_pattern_activity.xml
new file mode 100644
index 0000000..c547253
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_anti_pattern_activity.xml
@@ -0,0 +1,161 @@
+<?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"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/authLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin">
+
+    <TextView
+        android:id="@+id/standard_login_header"
+        style="@style/TextAppearance.AppCompat.Large"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:gravity="center"
+        android:text="@string/navigation_button_anti_pattern_credit_card_label"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintEnd_toStartOf="@+id/imageButton"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintHorizontal_chainStyle="spread"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <com.example.android.autofillframework.app.InfoButton
+        android:id="@+id/imageButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:background="@drawable/ic_info_black_24dp"
+        app:dialogText="@string/anti_pattern_credit_card_info"
+        app:layout_constraintBottom_toBottomOf="@+id/standard_login_header"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/standard_login_header"
+        app:layout_constraintTop_toTopOf="@+id/standard_login_header" />
+
+    <TextView
+        android:id="@+id/creditCardNumberLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/spacing_normal"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:labelFor="@+id/creditCardNumberField"
+        android:text="@string/credit_card_number_label"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/standard_login_header" />
+
+    <EditText
+        android:id="@+id/creditCardNumberField"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginTop="@dimen/spacing_normal"
+        android:autofillHints="creditCardNumber"
+        android:ems="12"
+        android:inputType="number"
+        android:paddingHorizontal="@dimen/spacing_micro"
+        app:layout_constraintBottom_toBottomOf="@+id/creditCardNumberLabel"
+        app:layout_constraintStart_toEndOf="@+id/creditCardNumberLabel"
+        app:layout_constraintTop_toTopOf="@+id/creditCardNumberLabel" />
+
+    <TextView
+        android:id="@+id/creditCardExpirationLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:labelFor="@+id/creditCardExpirationView"
+        android:text="@string/credit_card_expiration_label"
+        app:layout_constraintStart_toStartOf="@id/creditCardNumberLabel"
+        app:layout_constraintTop_toBottomOf="@+id/creditCardNumberLabel" />
+
+    <EditText
+        android:id="@+id/creditCardExpirationView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginTop="@dimen/spacing_normal"
+        android:autofillHints="creditCardExpirationDate"
+        android:ems="12"
+        android:paddingHorizontal="@dimen/spacing_micro"
+        app:layout_constraintBottom_toBottomOf="@+id/creditCardExpirationLabel"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/creditCardExpirationLabel"
+        app:layout_constraintTop_toTopOf="@+id/creditCardExpirationLabel" />
+
+    <TextView
+        android:id="@+id/creditCardSecurityCodeLabel"
+        style="@style/TextAppearance.AppCompat.Body1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:importantForAutofill="no"
+        android:text="@string/credit_card_security_code_label"
+        app:layout_constraintEnd_toStartOf="@+id/creditCardExpirationView"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationView" />
+
+    <EditText
+        android:id="@+id/creditCardSecurityCode"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_alignBaseline="@+id/creditCardSecurityCodeLabel"
+        android:layout_marginEnd="8dp"
+        android:inputType="number"
+        android:autofillHints="creditCardSecurityCode"
+        app:layout_constraintBottom_toBottomOf="@+id/creditCardSecurityCodeLabel"
+        app:layout_constraintEnd_toEndOf="@+id/creditCardExpirationView"
+        app:layout_constraintStart_toStartOf="@+id/creditCardExpirationView"
+        app:layout_constraintTop_toTopOf="@+id/creditCardSecurityCodeLabel" />
+
+    <TextView
+        android:id="@+id/clearButton"
+        style="@style/Widget.AppCompat.Button.Borderless"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        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="@+id/creditCardSecurityCode" />
+
+    <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>
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_compound_view_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_compound_view_activity.xml
index 14db038..838cc82 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_compound_view_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_compound_view_activity.xml
@@ -111,7 +111,7 @@
         android:layout_marginTop="@dimen/spacing_large"
         android:importantForAutofill="no"
         android:text="@string/credit_card_security_code_label"
-        app:layout_constraintEnd_toStartOf="@+id/expirationDate"
+        app:layout_constraintEnd_toStartOf="@+id/creditCardExpirationView"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationView" />
 
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_date_picker_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_date_picker_activity.xml
index de5f7f5..ace33cb 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_date_picker_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_date_picker_activity.xml
@@ -31,7 +31,7 @@
         android:layout_height="wrap_content"
         android:layout_marginTop="8dp"
         android:gravity="center"
-        android:text="@string/navigation_button_compound_view_credit_card_label"
+        android:text="@string/navigation_button_date_picker_credit_card_label"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintEnd_toStartOf="@+id/imageButton"
         app:layout_constraintHorizontal_bias="0.5"
@@ -45,7 +45,7 @@
         android:layout_height="wrap_content"
         android:layout_marginStart="@dimen/spacing_normal"
         android:background="@drawable/ic_info_black_24dp"
-        app:dialogText="@string/compound_view_credit_card_info"
+        app:dialogText="@string/date_picker_credit_card_info"
         app:layout_constraintBottom_toBottomOf="@+id/standard_login_header"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintHorizontal_bias="0.5"
@@ -112,7 +112,7 @@
         android:layout_marginTop="@dimen/spacing_large"
         android:importantForAutofill="no"
         android:text="@string/credit_card_security_code_label"
-        app:layout_constraintEnd_toStartOf="@+id/expirationDate"
+        app:layout_constraintEnd_toStartOf="@+id/creditCardExpirationView"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationView" />
 
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml
new file mode 100644
index 0000000..8504fc1
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/credit_card_spinners_activity.xml
@@ -0,0 +1,173 @@
+<?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"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin">
+
+    <TextView
+        android:id="@+id/spinners_credit_card_header"
+        style="@style/TextAppearance.AppCompat.Large"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:gravity="center"
+        android:text="@string/navigation_button_spinners_credit_card_label"
+        app:layout_constraintEnd_toStartOf="@+id/imageButton"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintHorizontal_chainStyle="spread"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <com.example.android.autofillframework.app.InfoButton
+        android:id="@+id/imageButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/ic_info_black_24dp"
+        app:layout_constraintBottom_toBottomOf="@+id/spinners_credit_card_header"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/spinners_credit_card_header"
+        app:layout_constraintTop_toTopOf="@+id/spinners_credit_card_header"
+        app:dialogText="@string/spinners_credit_card_info" />
+
+    <TextView
+        android:id="@+id/creditCardNumberLabel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:importantForAutofill="no"
+        android:labelFor="@+id/creditCardNumberField"
+        android:text="@string/credit_card_number_label"
+        app:layout_constraintStart_toStartOf="@+id/creditCardExpirationLabel"
+        app:layout_constraintTop_toBottomOf="@+id/spinners_credit_card_header" />
+
+    <EditText
+        android:id="@+id/creditCardNumberField"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_alignBaseline="@+id/creditCardNumberLabel"
+        android:layout_marginEnd="8dp"
+        android:autofillHints="creditCardNumber"
+        android:focusedByDefault="true"
+        android:inputType="number"
+        app:layout_constraintBottom_toBottomOf="@+id/creditCardNumberLabel"
+        app:layout_constraintEnd_toEndOf="@+id/expirationYear"
+        app:layout_constraintStart_toStartOf="@+id/expirationDay"
+        app:layout_constraintTop_toTopOf="@+id/creditCardNumberLabel" />
+
+    <TextView
+        android:id="@+id/creditCardExpirationLabel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:importantForAutofill="no"
+        android:text="@string/credit_card_expiration_label"
+        app:layout_constraintEnd_toStartOf="@+id/expirationDay"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/creditCardNumberLabel" />
+
+    <Spinner
+        android:id="@+id/expirationDay"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="8dp"
+        android:autofillHints="creditCardExpirationDay"
+        app:layout_constraintBottom_toBottomOf="@+id/creditCardExpirationLabel"
+        app:layout_constraintEnd_toStartOf="@+id/expirationMonth"
+        app:layout_constraintStart_toEndOf="@+id/creditCardNumberLabel"
+        app:layout_constraintTop_toTopOf="@+id/creditCardExpirationLabel" />
+
+    <Spinner
+        android:id="@+id/expirationMonth"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:autofillHints="creditCardExpirationMonth"
+        app:layout_constraintBottom_toBottomOf="@+id/expirationDay"
+        app:layout_constraintEnd_toStartOf="@+id/expirationYear"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/expirationDay"
+        app:layout_constraintTop_toTopOf="@+id/expirationDay" />
+
+    <Spinner
+        android:id="@+id/expirationYear"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:autofillHints="creditCardExpirationYear"
+        app:layout_constraintBottom_toBottomOf="@+id/expirationMonth"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintStart_toEndOf="@+id/expirationMonth"
+        app:layout_constraintTop_toTopOf="@+id/expirationMonth" />
+
+    <TextView
+        android:id="@+id/creditCardSecurityCodeLabel"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:importantForAutofill="no"
+        android:text="@string/credit_card_security_code_label"
+        app:layout_constraintEnd_toStartOf="@+id/creditCardExpirationView"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/creditCardExpirationLabel" />
+
+    <EditText
+        android:id="@+id/creditCardSecurityCode"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_alignBaseline="@+id/creditCardSecurityCodeLabel"
+        android:layout_marginEnd="8dp"
+        android:inputType="number"
+        android:autofillHints="creditCardSecurityCode"
+        app:layout_constraintBottom_toBottomOf="@+id/creditCardSecurityCodeLabel"
+        app:layout_constraintEnd_toEndOf="@+id/expirationMonth"
+        app:layout_constraintStart_toStartOf="@+id/expirationDay"
+        app:layout_constraintTop_toTopOf="@+id/creditCardSecurityCodeLabel" />
+
+    <TextView
+        android:id="@+id/clear"
+        style="@style/Widget.AppCompat.Button.Borderless"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/spacing_normal"
+        android:layout_marginTop="@dimen/spacing_large"
+        android:text="@string/clear_label"
+        android:textColor="@android:color/holo_blue_dark"
+        app:layout_constraintEnd_toStartOf="@+id/submit"
+        app:layout_constraintHorizontal_bias="0.5"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@+id/creditCardSecurityCodeLabel" />
+
+    <TextView
+        android:id="@+id/submit"
+        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/clear"
+        app:layout_constraintTop_toTopOf="@+id/clear" />
+</android.support.constraint.ConstraintLayout>
\ No newline at end of file
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 11b0942..4f7ebb0 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
@@ -19,6 +19,7 @@
     <string name="app_name">Autofill Sample</string>
     <string name="settings_name">Autofill Settings</string>
     <string name="navigation_button_custom_virtual_view_login_label">Sample Login Using a Custom Virtual View</string>
+    <string name="navigation_button_credit_card_label">Sample Credit Card Check Out Using EditTexts</string>
     <string name="navigation_button_spinners_credit_card_label">Sample Credit Card Check Out Using Spinners</string>
     <string name="navigation_button_edittext_login_label">Sample Login Using EditTexts</string>
     <string name="navigation_button_autocomplete_login_label">Sample Login Using AutoCompleteTextViews</string>
@@ -27,6 +28,7 @@
     <string name="navigation_button_date_picker_credit_card_label">Sample Credit Card Check Out Using Date Picker</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>
     <string name="username_label">Username</string>
     <string name="password_label">Password</string>
     <string name="welcome_text">Success!</string>
@@ -90,6 +92,9 @@
         virtual children out of the box, it is necessary implement certain Autofill-specific methods
         and interface directly with AutofillManager.
     </string>
+    <string name="credit_card_info">This is a sample credit card checkout page that uses
+        EditTexts to input data into the form.
+    </string>
     <string name="spinners_credit_card_info">This is a sample credit card checkout page that uses
         EditTexts and Spinners to input data into the form. While EditTexts are optimized out of the
         box, Spinners can require a small amount of work when using a custom array adapter.
@@ -126,6 +131,17 @@
         credit card info) and can be used to make sure that only one partition can be autofilled
         at time.
     </string>
+    <string name="anti_pattern_credit_card_info">This is a sample credit card checkout page that
+        uses a standard EditText fields to represent the full credit card expiration date (which
+        is tagged with the View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE hint).
+        This is an anti-pattern because its autofill type is View.AUTOFILL_TYPE_TEXT,
+        which makes it harder for the autofill service to figure out how to fill them.
+        For example, should a month/year date be represent as "04/2020", "4/2020" or "4/20"?
+        Or perhaps the year comes first, so it could be "2020/04", "2020/4" or "20/4"?
+        The proper way to represent a View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE field
+        is through a View.AUTOFILL_TYPE_DATE value, which is what the other credit card sample
+        activities use.
+    </string>
 
     <string name="partition_credentials">Credentials</string>
     <string name="partition_credit_card">Credit Card</string>
@@ -133,6 +149,9 @@
     <string name="message_autofill_ok">Autofilled partition \'%1$s\'</string>
     <string name="message_autofill_no_partitions">No partition for id %1$d on %2$s</string>
     <string name="message_autofill_blocked">Blocked cross-partitions: %1$s</string>
+    <string name="message_autofill_readonly">Ignoring autofill on read-only field %1$s</string>
+    <string name="message_autofill_invalid">INVALID</string>
+    <string name="message_credit_card_expiration_type">Representing expiration dates as %1$s</string>
 
     <plurals name="welcome_page_countdown">
         <item quantity="one">Automatically return to main page in %d second.</item>