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>