aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Sigelbaum <sigelbaum@google.com>2017-05-31 00:30:47 -0700
committerDouglas Sigelbaum <sigelbaum@google.com>2017-05-31 11:45:41 -0700
commit389ba153ecf015cbff36764e9c07f12ac3a0353d (patch)
tree284aa0a3355b15801ab5a1e171ff541e1ee850ba
parentda2273924314181bb5e7e50fc52effc2f00aff1b (diff)
downloadandroid-389ba153ecf015cbff36764e9c07f12ac3a0353d.tar.gz
Changes to get Kotlin and Java autofill samples in sync.
Also fixed a crash when using datasetAuth. Impacted both samples. Bug: 38182790 Test: manual Change-Id: I1bfb00e3a22708d7bcbaade2aff3b00789499fe7
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml17
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.java25
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java120
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java14
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.java1
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java2
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java23
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java41
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java14
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java35
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.java (renamed from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java)64
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java37
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java10
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java89
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.java85
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java135
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java6
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml17
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt19
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt1
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.kt8
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.kt20
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.kt3
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.kt8
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.kt10
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.kt10
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.kt (renamed from input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/MutableAutofillValue.kt)4
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml24
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml25
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml22
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml21
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml24
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml29
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml1
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml32
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml42
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/README.md192
-rw-r--r--input/autofill/AutofillFramework/template-params.xml12
38 files changed, 451 insertions, 791 deletions
diff --git a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
index 28d9c0b5..eb1f43c2 100644
--- a/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/AndroidManifest.xml
@@ -32,7 +32,6 @@
android:taskAffinity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -40,32 +39,17 @@
android:name=".app.LoginActivity"
android:label="AF StandardLogin"
android:taskAffinity=".LoginActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<activity
android:name=".app.VirtualLoginActivity"
android:label="AF VirtualLogin"
android:taskAffinity=".VirtualLoginActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<activity android:name=".app.WelcomeActivity" />
<activity
android:name=".app.CreditCardActivity"
android:label="AF CreditCard"
android:taskAffinity=".CreditCardActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<!--
Including launcher icon for Autofill Settings to convenience.
@@ -78,7 +62,6 @@
android:taskAffinity=".SettingsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
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 a82de4c8..7feb3936 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
@@ -21,19 +21,12 @@ import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
-import android.widget.Button;
import android.widget.Spinner;
import com.example.android.autofillframework.R;
public class CreditCardActivity extends AppCompatActivity {
- private Spinner mCcExpirationDaySpinner;
- private Spinner mCcExpirationMonthSpinner;
- private Spinner mCcExpirationYearSpinner;
- private Button mSubmitButton;
- private Button mClearButton;
-
public static Intent getStartActivityIntent(Context context) {
Intent intent = new Intent(context, CreditCardActivity.class);
return intent;
@@ -45,11 +38,9 @@ public class CreditCardActivity extends AppCompatActivity {
setContentView(R.layout.credit_card_activity);
- mSubmitButton = (Button) findViewById(R.id.submit);
- mClearButton = (Button) findViewById(R.id.clear);
- mCcExpirationDaySpinner = (Spinner) findViewById(R.id.expirationDay);
- mCcExpirationMonthSpinner = (Spinner) findViewById(R.id.expirationMonth);
- mCcExpirationYearSpinner = (Spinner) findViewById(R.id.expirationYear);
+ Spinner ccExpirationDaySpinner = findViewById(R.id.expirationDay);
+ Spinner ccExpirationMonthSpinner = findViewById(R.id.expirationMonth);
+ Spinner ccExpirationYearSpinner = findViewById(R.id.expirationYear);
// Create an ArrayAdapter using the string array and a default spinner layout
ArrayAdapter<CharSequence> dayAdapter = ArrayAdapter.createFromResource
@@ -57,25 +48,25 @@ public class CreditCardActivity extends AppCompatActivity {
// 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);
+ ccExpirationDaySpinner.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);
+ ccExpirationMonthSpinner.setAdapter(monthAdapter);
ArrayAdapter<CharSequence> yearAdapter = ArrayAdapter.createFromResource
(this, R.array.year_array, android.R.layout.simple_spinner_item);
yearAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mCcExpirationYearSpinner.setAdapter(yearAdapter);
+ ccExpirationYearSpinner.setAdapter(yearAdapter);
- mSubmitButton.setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.submit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
submit();
}
});
- mClearButton.setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resetFields();
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 dc09de53..e2140c4f 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
@@ -47,48 +47,35 @@ public class CustomVirtualView extends View {
private static final String TAG = "CustomView";
- private static int nextId;
-
- private final ArrayList<Line> mLines = new ArrayList<>();
- private final SparseArray<Item> mItems = new SparseArray<>();
- private final AutofillManager mAfm;
+ private static final int TOP_MARGIN = 100;
+ private static final int LEFT_MARGIN = 100;
+ private static final int TEXT_HEIGHT = 90;
+ private static final int VERTICAL_GAP = 10;
+ private static final int LINE_HEIGHT = TEXT_HEIGHT + VERTICAL_GAP;
+ private static final int UNFOCUSED_COLOR = Color.BLACK;
+ private static final int FOCUSED_COLOR = Color.RED;
+ private static int sNextId;
+
+ private final ArrayList<Line> mVirtualViewGroups = new ArrayList<>();
+ private final SparseArray<Item> mVirtualViews = new SparseArray<>();
+ private final AutofillManager mAutofillManager;
private Line mFocusedLine;
private Paint mTextPaint;
- private int mTextHeight;
- private int mTopMargin;
- private int mLeftMargin;
- private int mVerticalGap;
- private int mLineLength;
- private int mFocusedColor;
- private int mUnfocusedColor;
private Line mUsernameLine;
private Line mPasswordLine;
public CustomVirtualView(Context context, AttributeSet attrs) {
super(context, attrs);
-
- mAfm = context.getSystemService(AutofillManager.class);
-
+ mAutofillManager = context.getSystemService(AutofillManager.class);
mTextPaint = new Paint();
-
- mUnfocusedColor = Color.BLACK;
- mFocusedColor = Color.RED;
mTextPaint.setStyle(Style.FILL);
- mTopMargin = 100;
- mLeftMargin = 100;
- mTextHeight = 90;
- mVerticalGap = 10;
-
- mLineLength = mTextHeight + mVerticalGap;
- mTextPaint.setTextSize(mTextHeight);
+ mTextPaint.setTextSize(TEXT_HEIGHT);
mUsernameLine = addLine("usernameField", context.getString(R.string.username_label),
new String[]{View.AUTOFILL_HINT_USERNAME}, " ", true);
mPasswordLine = addLine("passwordField", context.getString(R.string.password_label),
new String[]{View.AUTOFILL_HINT_PASSWORD}, " ", false);
-
- Log.d(TAG, "Text height: " + mTextHeight);
}
@Override
@@ -99,19 +86,17 @@ public class CustomVirtualView extends View {
// AutofillValues in the list.
Log.d(TAG, "autoFill(): " + values);
for (int i = 0; i < values.size(); i++) {
- final int id = values.keyAt(i);
- final AutofillValue value = values.valueAt(i);
- final Item item = mItems.get(id);
- if (item == null) {
+ 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) {
Log.w(TAG, "No item for id " + id);
- return;
- }
- if (!item.editable) {
+ } else {
Log.w(TAG, "Item for id " + id + " is not editable: " + item);
- return;
}
- // Set the item's text to the text wrapped in the AutofillValue.
- item.text = value.getTextValue();
}
postInvalidate();
}
@@ -122,14 +107,14 @@ public class CustomVirtualView extends View {
// Build a ViewStructure that will get passed to the AutofillService by the framework
// when it is time to find autofill suggestions.
structure.setClassName(getClass().getName());
- int childrenSize = mItems.size();
+ int childrenSize = mVirtualViews.size();
Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags + ", items = "
+ childrenSize + ", extras: " + bundleToString(structure.getExtras()));
int index = structure.addChildCount(childrenSize);
// Traverse through the view hierarchy, including virtual child views. For each view, we
// need to set the relevant autofill metadata and add it to the ViewStructure.
for (int i = 0; i < childrenSize; i++) {
- Item item = mItems.valueAt(i);
+ Item item = mVirtualViews.valueAt(i);
Log.d(TAG, "Adding new child at index " + index + ": " + item);
ViewStructure child = structure.newChild(index);
child.setAutofillId(structure, item.id);
@@ -149,14 +134,14 @@ public class CustomVirtualView extends View {
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- Log.d(TAG, "onDraw: " + mLines.size() + " lines; canvas:" + canvas);
+ Log.d(TAG, "onDraw: " + mVirtualViewGroups.size() + " lines; canvas:" + canvas);
float x;
- float y = mTopMargin + mLineLength;
- for (int i = 0; i < mLines.size(); i++) {
- x = mLeftMargin;
- Line line = mLines.get(i);
+ float y = TOP_MARGIN + LINE_HEIGHT;
+ for (int i = 0; i < mVirtualViewGroups.size(); i++) {
+ x = LEFT_MARGIN;
+ Line line = mVirtualViewGroups.get(i);
Log.v(TAG, "Drawing '" + line + "' at " + x + "x" + y);
- mTextPaint.setColor(line.fieldTextItem.focused ? mFocusedColor : mUnfocusedColor);
+ mTextPaint.setColor(line.fieldTextItem.focused ? FOCUSED_COLOR : UNFOCUSED_COLOR);
String readOnlyText = line.labelItem.text + ": [";
String writeText = line.fieldTextItem.text + "]";
// Paints the label first...
@@ -164,23 +149,23 @@ public class CustomVirtualView extends View {
// ...then paints the edit text and sets the proper boundary
float deltaX = mTextPaint.measureText(readOnlyText);
x += deltaX;
- line.bounds.set((int) x, (int) (y - mLineLength),
+ line.bounds.set((int) x, (int) (y - LINE_HEIGHT),
(int) (x + mTextPaint.measureText(writeText)), (int) y);
Log.d(TAG, "setBounds(" + x + ", " + y + "): " + line.bounds);
canvas.drawText(writeText, x, y, mTextPaint);
- y += mLineLength;
+ y += LINE_HEIGHT;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
- Log.d(TAG, "Touched: y=" + y + ", range=" + mLineLength + ", top=" + mTopMargin);
- int lowerY = mTopMargin;
+ Log.d(TAG, "Touched: y=" + y + ", range=" + LINE_HEIGHT + ", top=" + TOP_MARGIN);
+ int lowerY = TOP_MARGIN;
int upperY = -1;
- for (int i = 0; i < mLines.size(); i++) {
- upperY = lowerY + mLineLength;
- Line line = mLines.get(i);
+ for (int i = 0; i < mVirtualViewGroups.size(); i++) {
+ upperY = lowerY + LINE_HEIGHT;
+ Line line = mVirtualViewGroups.get(i);
Log.d(TAG, "Line " + i + " ranges from " + lowerY + " to " + upperY);
if (lowerY <= y && y <= upperY) {
if (mFocusedLine != null) {
@@ -193,7 +178,7 @@ public class CustomVirtualView extends View {
invalidate();
break;
}
- lowerY += mLineLength;
+ lowerY += LINE_HEIGHT;
}
return super.onTouchEvent(event);
}
@@ -212,11 +197,12 @@ public class CustomVirtualView extends View {
postInvalidate();
}
- private Line addLine(String idEntry, String label, String[] hints, String text, boolean sanitized) {
+ private Line addLine(String idEntry, String label, String[] hints, String text,
+ boolean sanitized) {
Line line = new Line(idEntry, label, hints, text, sanitized);
- mLines.add(line);
- mItems.put(line.labelItem.id, line.labelItem);
- mItems.put(line.fieldTextItem.id, line.fieldTextItem);
+ mVirtualViewGroups.add(line);
+ mVirtualViews.put(line.labelItem.id, line.labelItem);
+ mVirtualViews.put(line.fieldTextItem.id, line.fieldTextItem);
return line;
}
@@ -230,7 +216,8 @@ public class CustomVirtualView extends View {
private CharSequence text;
private boolean focused = false;
- Item(Line line, int id, String[] hints, int type, CharSequence text, boolean editable, boolean sanitized) {
+ Item(Line line, int id, String[] hints, int type, CharSequence text, boolean editable,
+ boolean sanitized) {
this.line = line;
this.id = id;
this.text = text;
@@ -261,27 +248,30 @@ public class CustomVirtualView extends View {
private Line(String idEntry, String label, String[] hints, String text, boolean sanitized) {
this.idEntry = idEntry;
- this.labelItem = new Item(this, ++nextId, null, AUTOFILL_TYPE_NONE, label, false, true);
- this.fieldTextItem = new Item(this, ++nextId, hints, AUTOFILL_TYPE_TEXT, text, true, sanitized);
+ this.labelItem = new Item(this, ++sNextId, null, AUTOFILL_TYPE_NONE, label,
+ false, true);
+ this.fieldTextItem = new Item(this, ++sNextId, hints, AUTOFILL_TYPE_TEXT, text,
+ true, sanitized);
}
void changeFocus(boolean focused) {
fieldTextItem.focused = focused;
if (focused) {
- final Rect absBounds = getAbsCoordinates();
+ Rect absBounds = getAbsCoordinates();
Log.d(TAG, "focus gained on " + fieldTextItem.id + "; absBounds=" + absBounds);
- mAfm.notifyViewEntered(CustomVirtualView.this, fieldTextItem.id, absBounds);
+ mAutofillManager.notifyViewEntered(CustomVirtualView.this, fieldTextItem.id,
+ absBounds);
} else {
Log.d(TAG, "focus lost on " + fieldTextItem.id);
- mAfm.notifyViewExited(CustomVirtualView.this, fieldTextItem.id);
+ mAutofillManager.notifyViewExited(CustomVirtualView.this, fieldTextItem.id);
}
}
private Rect getAbsCoordinates() {
// Must offset the boundaries so they're relative to the CustomView.
- final int offset[] = new int[2];
+ int offset[] = new int[2];
getLocationOnScreen(offset);
- final Rect absBounds = new Rect(bounds.left + offset[0],
+ Rect absBounds = new Rect(bounds.left + offset[0],
bounds.top + offset[1],
bounds.right + offset[0], bounds.bottom + offset[1]);
Log.v(TAG, "getAbsCoordinates() for " + fieldTextItem.id + ": bounds=" + bounds
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java
index 3ec87da0..ec7ab581 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java
@@ -20,7 +20,6 @@ import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
-import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
@@ -30,8 +29,6 @@ public class LoginActivity extends AppCompatActivity {
private EditText mUsernameEditText;
private EditText mPasswordEditText;
- private Button mLoginButton;
- private Button mClearButton;
public static Intent getStartActivityIntent(Context context) {
Intent intent = new Intent(context, LoginActivity.class);
@@ -43,18 +40,15 @@ public class LoginActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity);
-
- mLoginButton = (Button) findViewById(R.id.login);
- mClearButton = (Button) findViewById(R.id.clear);
- mUsernameEditText = (EditText) findViewById(R.id.usernameField);
- mPasswordEditText = (EditText) findViewById(R.id.passwordField);
- mLoginButton.setOnClickListener(new View.OnClickListener() {
+ mUsernameEditText = findViewById(R.id.usernameField);
+ mPasswordEditText = findViewById(R.id.passwordField);
+ findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
- mClearButton.setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.clear).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
resetFields();
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 4b27010b..4e7a6caa 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
@@ -15,7 +15,6 @@
*/
package com.example.android.autofillframework.app;
-import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java
index 5a4a4f4e..0c787090 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.java
@@ -15,7 +15,6 @@
*/
package com.example.android.autofillframework.app;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -70,6 +69,7 @@ public class VirtualLoginActivity extends AppCompatActivity {
if (valid) {
Intent intent = WelcomeActivity.getStartActivityIntent(VirtualLoginActivity.this);
startActivity(intent);
+ finish();
} else {
Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show();
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java
index 768b2ee3..d170c9e3 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java
@@ -29,12 +29,11 @@ import android.text.Editable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.service.datasource.LocalAutofillRepository;
+import com.example.android.autofillframework.service.datasource.SharedPrefsAutofillRepository;
import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
import com.example.android.autofillframework.service.model.ClientFormData;
import com.example.android.autofillframework.service.settings.MyPreferences;
@@ -58,8 +57,6 @@ public class AuthActivity extends Activity {
private static int sDatasetPendingIntentId = 0;
private EditText mMasterPassword;
- private Button mCancel;
- private Button mLogin;
private Intent mReplyIntent;
static IntentSender getAuthIntentSenderForResponse(Context context) {
@@ -79,20 +76,16 @@ public class AuthActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
setContentView(R.layout.auth_activity);
- mCancel = findViewById(R.id.cancel);
- mLogin = findViewById(R.id.login);
mMasterPassword = findViewById(R.id.master_password);
- mLogin.setOnClickListener(new OnClickListener() {
+ findViewById(R.id.login).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
login();
}
});
-
- mCancel.setOnClickListener(new OnClickListener() {
+ findViewById(R.id.cancel).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onFailure();
@@ -133,20 +126,20 @@ public class AuthActivity extends Activity {
boolean forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true);
AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
StructureParser parser = new StructureParser(structure);
- parser.parse();
+ parser.parseForFill();
AutofillFieldsCollection autofillFields = parser.getAutofillFields();
- int saveTypes = parser.getSaveTypes();
+ int saveTypes = autofillFields.getSaveType();
mReplyIntent = new Intent();
HashMap<String, ClientFormData> clientFormDataMap =
- LocalAutofillRepository.getInstance(this).getClientFormData
+ SharedPrefsAutofillRepository.getInstance(this).getClientFormData
(autofillFields.getFocusedHints(), autofillFields.getAllHints());
if (forResponse) {
setResponseIntent(AutofillHelper.newResponse
- (this, false, autofillFields, saveTypes, clientFormDataMap));
+ (this, false, autofillFields, clientFormDataMap));
} else {
String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
setDatasetIntent(AutofillHelper.newDataset
- (this, autofillFields, clientFormDataMap.get(datasetName)));
+ (this, autofillFields, clientFormDataMap.get(datasetName), false));
}
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java
index 460729e6..f460538a 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java
@@ -43,15 +43,21 @@ public final class AutofillHelper {
* client View.
*/
public static Dataset newDataset(Context context,
- AutofillFieldsCollection autofillFields, ClientFormData clientFormData) {
- Dataset.Builder datasetBuilder = new Dataset.Builder
- (newRemoteViews(context.getPackageName(), clientFormData.getDatasetName()));
- boolean setValueAtLeastOnce = clientFormData.applyToFields(autofillFields, datasetBuilder);
- if (setValueAtLeastOnce) {
- return datasetBuilder.build();
- } else {
- return null;
+ AutofillFieldsCollection autofillFields, ClientFormData clientFormData, boolean datasetAuth) {
+ String datasetName = clientFormData.getDatasetName();
+ if (datasetName != null) {
+ Dataset.Builder datasetBuilder = new Dataset.Builder
+ (newRemoteViews(context.getPackageName(), datasetName));
+ if (datasetAuth) {
+ IntentSender sender = AuthActivity.getAuthIntentSenderForDataset(context, datasetName);
+ datasetBuilder.setAuthentication(sender);
+ }
+ boolean setValueAtLeastOnce = clientFormData.applyToFields(autofillFields, datasetBuilder);
+ if (setValueAtLeastOnce) {
+ return datasetBuilder.build();
+ }
}
+ return null;
}
public static RemoteViews newRemoteViews(String packageName, String remoteViewsText) {
@@ -65,32 +71,25 @@ public final class AutofillHelper {
* be sent back to the client View.
*/
public static FillResponse newResponse(Context context,
- boolean datasetAuth, AutofillFieldsCollection autofillFields, int saveType,
+ boolean datasetAuth, AutofillFieldsCollection autofillFields,
HashMap<String, ClientFormData> clientFormDataMap) {
FillResponse.Builder responseBuilder = new FillResponse.Builder();
if (clientFormDataMap != null) {
Set<String> datasetNames = clientFormDataMap.keySet();
for (String datasetName : datasetNames) {
ClientFormData clientFormData = clientFormDataMap.get(datasetName);
- if (datasetAuth) {
- Dataset.Builder datasetBuilder =
- new Dataset.Builder(newRemoteViews
- (context.getPackageName(), clientFormData.getDatasetName()));
- IntentSender sender = AuthActivity
- .getAuthIntentSenderForDataset(context, clientFormData.getDatasetName());
- datasetBuilder.setAuthentication(sender);
- responseBuilder.addDataset(datasetBuilder.build());
- } else {
- Dataset dataset = newDataset(context, autofillFields, clientFormData);
+ if (clientFormData != null) {
+ Dataset dataset = newDataset(context, autofillFields, clientFormData, datasetAuth);
if (dataset != null) {
responseBuilder.addDataset(dataset);
}
}
}
}
- if (saveType != 0) {
+ if (autofillFields.getSaveType() != 0) {
AutofillId[] autofillIds = autofillFields.getAutofillIds();
- responseBuilder.setSaveInfo(new SaveInfo.Builder(saveType, autofillIds).build());
+ responseBuilder.setSaveInfo
+ (new SaveInfo.Builder(autofillFields.getSaveType(), autofillIds).build());
return responseBuilder.build();
} else {
Log.d(TAG, "These fields are not meant to be saved by autofill.");
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java
index 61e42050..ead8b963 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java
@@ -30,7 +30,7 @@ import android.util.Log;
import android.widget.RemoteViews;
import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.service.datasource.LocalAutofillRepository;
+import com.example.android.autofillframework.service.datasource.SharedPrefsAutofillRepository;
import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
import com.example.android.autofillframework.service.model.ClientFormData;
import com.example.android.autofillframework.service.settings.MyPreferences;
@@ -77,10 +77,8 @@ public class MyAutofillService extends AutofillService {
});
// Parse AutoFill data in Activity
StructureParser parser = new StructureParser(structure);
- parser.parse();
+ parser.parseForFill();
AutofillFieldsCollection autofillFields = parser.getAutofillFields();
- int saveTypes = parser.getSaveTypes();
-
FillResponse.Builder responseBuilder = new FillResponse.Builder();
// Check user's settings for authenticating Responses and Datasets.
boolean responseAuth = MyPreferences.getInstance(this).isResponseAuth();
@@ -96,10 +94,10 @@ public class MyAutofillService extends AutofillService {
} else {
boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth();
HashMap<String, ClientFormData> clientFormDataMap =
- LocalAutofillRepository.getInstance(this).getClientFormData
+ SharedPrefsAutofillRepository.getInstance(this).getClientFormData
(autofillFields.getFocusedHints(), autofillFields.getAllHints());
FillResponse response = AutofillHelper.newResponse
- (this, datasetAuth, autofillFields, saveTypes, clientFormDataMap);
+ (this, datasetAuth, autofillFields, clientFormDataMap);
callback.onSuccess(response);
}
}
@@ -111,9 +109,9 @@ public class MyAutofillService extends AutofillService {
final Bundle data = request.getClientState();
Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data));
StructureParser parser = new StructureParser(structure);
- parser.parse();
+ parser.parseForSave();
ClientFormData clientFormData = parser.getClientFormData();
- LocalAutofillRepository.getInstance(this).saveClientFormData(clientFormData);
+ SharedPrefsAutofillRepository.getInstance(this).saveClientFormData(clientFormData);
}
@Override
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java
index b6294449..6d81a59b 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java
@@ -23,14 +23,14 @@ import android.util.Log;
import com.example.android.autofillframework.service.model.AutofillField;
import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
import com.example.android.autofillframework.service.model.ClientFormData;
-import com.example.android.autofillframework.service.model.SavedAutofillValue;
+import com.example.android.autofillframework.service.model.SavableAutofillData;
import static com.example.android.autofillframework.CommonUtil.TAG;
/**
* Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
- * AssistStructure from the client Activity, representing its View hierarchy. In this
- * sample, it parses the hierarchy and records
+ * AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
+ * parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
*/
final class StructureParser {
private final AutofillFieldsCollection mAutofillFields = new AutofillFieldsCollection();
@@ -42,31 +42,42 @@ final class StructureParser {
}
+ public void parseForFill() {
+ parse(true);
+ }
+
+ public void parseForSave() {
+ parse(false);
+ }
+
/**
* Traverse AssistStructure and add ViewNode metadata to a flat list.
*/
- void parse() {
+ private void parse(boolean forFill) {
Log.d(TAG, "Parsing structure for " + mStructure.getActivityComponent());
int nodes = mStructure.getWindowNodeCount();
mClientFormData = new ClientFormData();
for (int i = 0; i < nodes; i++) {
WindowNode node = mStructure.getWindowNodeAt(i);
ViewNode view = node.getRootViewNode();
- parseLocked(view);
+ parseLocked(forFill, view);
}
}
- private void parseLocked(ViewNode viewNode) {
+ private void parseLocked(boolean forFill, ViewNode viewNode) {
if (viewNode.getAutofillHints() != null && viewNode.getAutofillHints().length > 0) {
//TODO check to make sure hints are supported by service.
- mAutofillFields.add(new AutofillField(viewNode));
- mClientFormData
- .set(viewNode.getAutofillHints(), SavedAutofillValue.fromViewNode(viewNode));
+ if (forFill) {
+ mAutofillFields.add(new AutofillField(viewNode));
+ } else {
+ mClientFormData.setAutofillValuesForHints
+ (viewNode.getAutofillHints(), new SavableAutofillData(viewNode));
+ }
}
int childrenSize = viewNode.getChildCount();
if (childrenSize > 0) {
for (int i = 0; i < childrenSize; i++) {
- parseLocked(viewNode.getChildAt(i));
+ parseLocked(forFill, viewNode.getChildAt(i));
}
}
}
@@ -75,10 +86,6 @@ final class StructureParser {
return mAutofillFields;
}
- public int getSaveTypes() {
- return mAutofillFields.getSaveType();
- }
-
public ClientFormData getClientFormData() {
return mClientFormData;
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.java
index 8336fe1e..44758fcb 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.java
@@ -20,37 +20,34 @@ import android.content.SharedPreferences;
import android.util.ArraySet;
import com.example.android.autofillframework.service.model.ClientFormData;
-
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.google.gson.Gson;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
/**
- * Singleton autofill data repository, that stores autofill fields to SharedPreferences.
- * DISCLAIMER, you should not store sensitive fields like user data unencrypted. This is only done
- * here for simplicity and learning purposes.
+ * Singleton autofill data repository that stores autofill fields to SharedPreferences.
+ * Disclaimer: you should not store sensitive fields like user data unencrypted. This is done
+ * here only for simplicity and learning purposes.
*/
-public class LocalAutofillRepository implements AutofillRepository {
+public class SharedPrefsAutofillRepository implements AutofillRepository {
private static final String SHARED_PREF_KEY = "com.example.android.autofillframework.service";
private static final String CLIENT_FORM_DATA_KEY = "loginCredentialDatasets";
private static final String DATASET_NUMBER_KEY = "datasetNumber";
- private static LocalAutofillRepository sInstance;
+ private static SharedPrefsAutofillRepository sInstance;
private final SharedPreferences mPrefs;
- // TODO prepend with autofill data set in Settings.
- private LocalAutofillRepository(Context context) {
+ private SharedPrefsAutofillRepository(Context context) {
mPrefs = context.getApplicationContext()
.getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE);
}
- public static LocalAutofillRepository getInstance(Context context) {
+ public static SharedPrefsAutofillRepository getInstance(Context context) {
if (sInstance == null) {
- sInstance = new LocalAutofillRepository(context);
+ sInstance = new SharedPrefsAutofillRepository(context);
}
return sInstance;
}
@@ -58,40 +55,37 @@ public class LocalAutofillRepository implements AutofillRepository {
@Override
public HashMap<String, ClientFormData> getClientFormData(List<String> focusedAutofillHints,
List<String> allAutofillHints) {
- try {
- // TODO use sqlite instead.
- boolean hasDataForFocusedAutofillHints = false;
- HashMap<String, ClientFormData> clientFormDataMap = new HashMap<>();
- Set<String> clientFormDataStringSet = getAllAutofillDataStringSet();
- for (String clientFormDataString : clientFormDataStringSet) {
- ClientFormData clientFormData = ClientFormData
- .fromJson(new JSONObject(clientFormDataString));
- if (clientFormData != null) {
- if (clientFormData.helpsWithHints(focusedAutofillHints)) {
- hasDataForFocusedAutofillHints = true;
- }
- if (clientFormData.helpsWithHints(allAutofillHints)) {
- clientFormDataMap.put(clientFormData.getDatasetName(), clientFormData);
- }
+ boolean hasDataForFocusedAutofillHints = false;
+ HashMap<String, ClientFormData> clientFormDataMap = new HashMap<>();
+ Set<String> clientFormDataStringSet = getAllAutofillDataStringSet();
+ for (String clientFormDataString : clientFormDataStringSet) {
+ ClientFormData clientFormData = new Gson().fromJson(clientFormDataString, ClientFormData.class);
+ if (clientFormData != null) {
+ if (clientFormData.helpsWithHints(focusedAutofillHints)) {
+ // Saved data has data relevant to at least 1 of the hints associated with the
+ // View in focus.
+ hasDataForFocusedAutofillHints = true;
+ }
+ if (clientFormData.helpsWithHints(allAutofillHints)) {
+ // Saved data has data relevant to at least 1 of these hints associated with any
+ // of the Views in the hierarchy.
+ clientFormDataMap.put(clientFormData.getDatasetName(), clientFormData);
}
}
- if (hasDataForFocusedAutofillHints) {
- return clientFormDataMap;
- } else {
- return null;
- }
- } catch (JSONException e) {
+ }
+ if (hasDataForFocusedAutofillHints) {
+ return clientFormDataMap;
+ } else {
return null;
}
}
@Override
public void saveClientFormData(ClientFormData clientFormData) {
- //TODO use sqlite instead.
String datasetName = "dataset-" + getDatasetNumber();
clientFormData.setDatasetName(datasetName);
Set<String> allAutofillData = getAllAutofillDataStringSet();
- allAutofillData.add(clientFormData.toJson().toString());
+ allAutofillData.add(new Gson().toJson(clientFormData));
saveAllAutofillDataStringSet(allAutofillData);
incrementDatasetNumber();
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java
index 4d4de2bc..710112e1 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java
@@ -15,39 +15,38 @@
*/
package com.example.android.autofillframework.service.model;
-import android.app.assist.AssistStructure;
+import android.app.assist.AssistStructure.ViewNode;
import android.service.autofill.SaveInfo;
import android.view.View;
import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
/**
- * Class that represents a field that can be autofilled. It will contain a description
- * (what type data the field holds), an AutoFillId (an ID unique to the rest of the ViewStructure),
- * and a value (what data is currently in the field).
+ * A stripped down version of a {@link ViewNode} that contains only autofill-relevant metadata. It
+ * also contains a {@code mSaveType} flag that is calculated based on the {@link ViewNode}]'s
+ * autofill hints.
*/
public class AutofillField {
private int mSaveType = 0;
- private String[] mHints;
- private AutofillId mId;
+ private String[] mAutofillHints;
+ private AutofillId mAutofillId;
private int mAutofillType;
private String[] mAutofillOptions;
private boolean mFocused;
- public AutofillField(AssistStructure.ViewNode view) {
- mId = view.getAutofillId();
- setHints(view.getAutofillHints());
+ public AutofillField(ViewNode view) {
+ mAutofillId = view.getAutofillId();
mAutofillType = view.getAutofillType();
mAutofillOptions = view.getAutofillOptions();
mFocused = view.isFocused();
+ setHints(view.getAutofillHints());
}
public String[] getHints() {
- return mHints;
+ return mAutofillHints;
}
public void setHints(String[] hints) {
- mHints = hints;
+ mAutofillHints = hints;
updateSaveTypeFromHints();
}
@@ -56,17 +55,17 @@ public class AutofillField {
}
public AutofillId getId() {
- return mId;
- }
-
- public void setId(AutofillId id) {
- mId = id;
+ return mAutofillId;
}
public int getAutofillType() {
return mAutofillType;
}
+ /**
+ * When the {@link ViewNode} is a list that the user needs to choose a string from (i.e. a
+ * spinner), this is called to return the index of a specific item in the list.
+ */
public int getAutofillOptionIndex(String value) {
for (int i = 0; i < mAutofillOptions.length; i++) {
if (mAutofillOptions[i].equals(value)) {
@@ -82,10 +81,10 @@ public class AutofillField {
private void updateSaveTypeFromHints() {
mSaveType = 0;
- if (mHints == null) {
+ if (mAutofillHints == null) {
return;
}
- for (String hint : mHints) {
+ for (String hint : mAutofillHints) {
switch (hint) {
case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE:
case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY:
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java
index 0354b989..a346f350 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java
@@ -22,18 +22,22 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+/**
+ * Data structure that stores a collection of {@code AutofillField}s. Contains all of the client's
+ * {@code View} hierarchy autofill-relevant metadata.
+ */
public final class AutofillFieldsCollection {
private final List<AutofillId> mAutofillIds = new ArrayList<>();
private final HashMap<String, List<AutofillField>> mAutofillHintsToFieldsMap = new HashMap<>();
private final List<String> mAllAutofillHints = new ArrayList<>();
private final List<String> mFocusedAutofillHints = new ArrayList<>();
- private int size = 0;
+ private int mSize = 0;
private int mSaveType = 0;
public void add(AutofillField autofillField) {
mSaveType |= autofillField.getSaveType();
- size++;
+ mSize++;
mAutofillIds.add(autofillField.getId());
List<String> hintsList = Arrays.asList(autofillField.getHints());
mAllAutofillHints.addAll(hintsList);
@@ -53,7 +57,7 @@ public final class AutofillFieldsCollection {
}
public AutofillId[] getAutofillIds() {
- return mAutofillIds.toArray(new AutofillId[size]);
+ return mAutofillIds.toArray(new AutofillId[mSize]);
}
public List<AutofillField> getFieldsForHint(String hint) {
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java
index 3658519e..15b5bea3 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java
@@ -22,78 +22,48 @@ import android.view.View;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
-import java.util.Set;
+
+import static com.example.android.autofillframework.CommonUtil.TAG;
/**
* ClientFormData is the model that holds all of the data on a client app's page, plus the dataset
* name associated with it.
*/
public final class ClientFormData {
- private static final String TAG = "ClientFormData";
- private final HashMap<String, SavedAutofillValue> hintMap;
- private String datasetName;
+ private final HashMap<String, SavableAutofillData> mHintMap;
+ private String mDatasetName;
public ClientFormData() {
- this(null, new HashMap<String, SavedAutofillValue>());
- }
-
- public ClientFormData(String datasetName, HashMap<String, SavedAutofillValue> hintMap) {
- this.hintMap = hintMap;
- this.datasetName = datasetName;
+ this(null, new HashMap<String, SavableAutofillData>());
}
- public static ClientFormData fromJson(JSONObject jsonObject) {
- HashMap<String, SavedAutofillValue> hintMap = new HashMap<>();
- try {
- String datasetName = jsonObject.has("datasetName") ?
- jsonObject.getString("datasetName") : null;
- JSONObject valuesJson = jsonObject.getJSONObject("values");
- Iterator<String> hints = valuesJson.keys();
- while (hints.hasNext()) {
- String hint = hints.next();
- JSONObject valueAsJson = valuesJson
- .getJSONObject(hint);
- if (valueAsJson != null) {
- SavedAutofillValue savedAutofillValue = SavedAutofillValue.fromJson(valueAsJson);
- hintMap.put(hint, savedAutofillValue);
- }
- }
- return new ClientFormData(datasetName, hintMap);
- } catch (JSONException e) {
- Log.d(TAG, e.getMessage());
- return null;
- }
+ public ClientFormData(String datasetName, HashMap<String, SavableAutofillData> hintMap) {
+ mHintMap = hintMap;
+ mDatasetName = datasetName;
}
/**
* Returns the name of the {@link Dataset}.
*/
public String getDatasetName() {
- return this.datasetName;
+ return mDatasetName;
}
/**
* Sets the {@link Dataset} name.
*/
public void setDatasetName(String datasetName) {
- this.datasetName = datasetName;
+ mDatasetName = datasetName;
}
/**
* Sets values for a list of hints.
*/
- public void set(@NonNull String[] autofillHints, @NonNull SavedAutofillValue autofillValue) {
- if (autofillHints.length < 1) {
- return;
- }
+ public void setAutofillValuesForHints(@NonNull String[] autofillHints, @NonNull SavableAutofillData autofillValue) {
for (int i = 0; i < autofillHints.length; i++) {
- hintMap.put(autofillHints[i], autofillValue);
+ mHintMap.put(autofillHints[i], autofillValue);
}
}
@@ -112,8 +82,8 @@ public final class ClientFormData {
continue;
}
for (int autofillFieldIndex = 0; autofillFieldIndex < autofillFields.size(); autofillFieldIndex++) {
- SavedAutofillValue savedAutofillValue = hintMap.get(hint);
- if (savedAutofillValue == null) {
+ SavableAutofillData savableAutofillData = mHintMap.get(hint);
+ if (savableAutofillData == null) {
continue;
}
AutofillField autofillField = autofillFields.get(autofillFieldIndex);
@@ -121,29 +91,29 @@ public final class ClientFormData {
int autofillType = autofillField.getAutofillType();
switch (autofillType) {
case View.AUTOFILL_TYPE_LIST:
- int listValue = autofillField.getAutofillOptionIndex(savedAutofillValue.getTextValue());
+ int listValue = autofillField.getAutofillOptionIndex(savableAutofillData.getTextValue());
if (listValue != -1) {
datasetBuilder.setValue(autofillId, AutofillValue.forList(listValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_DATE:
- long dateValue = savedAutofillValue.getDateValue();
- if (dateValue != -1) {
+ Long dateValue = savableAutofillData.getDateValue();
+ if (dateValue != null) {
datasetBuilder.setValue(autofillId, AutofillValue.forDate(dateValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_TEXT:
- String textValue = savedAutofillValue.getTextValue();
+ String textValue = savableAutofillData.getTextValue();
if (textValue != null) {
datasetBuilder.setValue(autofillId, AutofillValue.forText(textValue));
setValueAtLeastOnce = true;
}
break;
case View.AUTOFILL_TYPE_TOGGLE:
- if (savedAutofillValue.hasToggleValue()) {
- boolean toggleValue = savedAutofillValue.getToggleValue();
+ Boolean toggleValue = savableAutofillData.getToggleValue();
+ if (toggleValue != null) {
datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggleValue));
setValueAtLeastOnce = true;
}
@@ -158,27 +128,10 @@ public final class ClientFormData {
return setValueAtLeastOnce;
}
- public JSONObject toJson() {
- JSONObject jsonObject = new JSONObject();
- try {
- jsonObject.put("datasetName", datasetName != null ? datasetName : JSONObject.NULL);
- JSONObject jsonValues = new JSONObject();
- Set<String> hints = hintMap.keySet();
- for (String hint : hints) {
- SavedAutofillValue value = hintMap.get(hint);
- jsonValues.put(hint, value != null ? value.toJson() : JSONObject.NULL);
- }
- jsonObject.put("values", jsonValues);
- } catch (JSONException e) {
- Log.e(TAG, e.getMessage());
- }
- return jsonObject;
- }
-
public boolean helpsWithHints(List<String> autofillHints) {
for (int i = 0; i < autofillHints.size(); i++) {
String autofillHint = autofillHints.get(i);
- if (hintMap.get(autofillHint) != null && !hintMap.get(autofillHint).isNull()) {
+ if (mHintMap.get(autofillHint) != null && !mHintMap.get(autofillHint).isNull()) {
return true;
}
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.java
new file mode 100644
index 00000000..e706511e
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.java
@@ -0,0 +1,85 @@
+/*
+ * 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.service.model;
+
+import android.app.assist.AssistStructure;
+import android.view.autofill.AutofillValue;
+
+/**
+ * JSON serializable data class containing the same data as an {@link AutofillValue}.
+ */
+public class SavableAutofillData {
+ private String mTextValue = null;
+ private Long mDateValue = null;
+ private Boolean mToggleValue = null;
+
+ public SavableAutofillData(AssistStructure.ViewNode viewNode) {
+ AutofillValue autofillValue = viewNode.getAutofillValue();
+ if (autofillValue != null) {
+ if (autofillValue.isList()) {
+ String[] autofillOptions = viewNode.getAutofillOptions();
+ int index = autofillValue.getListValue();
+ if (autofillOptions != null && autofillOptions.length > 0) {
+ mTextValue = autofillOptions[index];
+ }
+ } else if (autofillValue.isDate()) {
+ mDateValue = autofillValue.getDateValue();
+ } else if (autofillValue.isText()) {
+ // Using toString of AutofillValue.getTextValue in order to save it to
+ // SharedPreferences.
+ mTextValue = autofillValue.getTextValue().toString();
+ }
+ }
+ }
+
+ public String getTextValue() {
+ return mTextValue;
+ }
+
+ public Long getDateValue() {
+ return mDateValue;
+ }
+
+ public Boolean getToggleValue() {
+ return mToggleValue;
+ }
+
+ public boolean isNull() {
+ return mTextValue == null && mDateValue == null && mToggleValue == null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ SavableAutofillData that = (SavableAutofillData) o;
+
+ if (mTextValue != null ? !mTextValue.equals(that.mTextValue) : that.mTextValue != null)
+ return false;
+ if (mDateValue != null ? !mDateValue.equals(that.mDateValue) : that.mDateValue != null)
+ return false;
+ return mToggleValue != null ? mToggleValue.equals(that.mToggleValue) : that.mToggleValue == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mTextValue != null ? mTextValue.hashCode() : 0;
+ result = 31 * result + (mDateValue != null ? mDateValue.hashCode() : 0);
+ result = 31 * result + (mToggleValue != null ? mToggleValue.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java
deleted file mode 100644
index 73e0c81e..00000000
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * 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.service.model;
-
-import android.app.assist.AssistStructure;
-import android.util.Log;
-import android.view.autofill.AutofillValue;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-public class SavedAutofillValue {
- private static final String TAG = "SavedAutofillValue";
- private String textValue = null;
- private Long dateValue = -1L;
- private Boolean toggleValue = false;
- private boolean hasToggleValue = false;
-
- public static SavedAutofillValue fromJson(JSONObject jsonObject) {
- if (jsonObject == null) {
- return null;
- }
- try {
- SavedAutofillValue savedAutofillValue = new SavedAutofillValue();
-
- savedAutofillValue.textValue =
- !jsonObject.isNull("textValue") ? jsonObject.getString("textValue") : null;
- savedAutofillValue.dateValue =
- !jsonObject.isNull("dateValue") ? jsonObject.getLong("dateValue") : null;
- savedAutofillValue.setToggleValue
- (!jsonObject.isNull("toggleValue") ? jsonObject.getBoolean("toggleValue") : null);
- return savedAutofillValue;
- } catch (JSONException e) {
- Log.e(TAG, e.getMessage());
- return null;
- }
- }
-
- public static SavedAutofillValue fromViewNode(AssistStructure.ViewNode viewNode) {
- SavedAutofillValue savedAutofillValue = new SavedAutofillValue();
- AutofillValue autofillValue = viewNode.getAutofillValue();
- if (autofillValue != null) {
- if (autofillValue.isList()) {
- String[] autofillOptions = viewNode.getAutofillOptions();
- int index = autofillValue.getListValue();
- if (autofillOptions != null && autofillOptions.length > 0) {
- savedAutofillValue.textValue = autofillOptions[index];
- }
- } else if (autofillValue.isDate()) {
- savedAutofillValue.dateValue = autofillValue.getDateValue();
- } else if (autofillValue.isText()) {
- // Using toString of AutofillValue.getTextValue in order to save it to
- // SharedPreferences.
- savedAutofillValue.textValue = autofillValue.getTextValue().toString();
- }
- }
- return savedAutofillValue;
- }
-
- public JSONObject toJson() {
- JSONObject jsonObject = new JSONObject();
- try {
- jsonObject.put("textValue", textValue != null ? textValue : JSONObject.NULL);
- jsonObject.put("dateValue", dateValue != null ? dateValue : JSONObject.NULL);
- jsonObject.put("toggleValue", toggleValue != null ? toggleValue : JSONObject.NULL);
- return jsonObject;
- } catch (JSONException e) {
- Log.e(TAG, e.getMessage());
- return null;
- }
- }
-
- public String getTextValue() {
- return textValue;
- }
-
- public long getDateValue() {
- return dateValue;
- }
-
-
- public boolean getToggleValue() {
- return toggleValue;
- }
-
- public void setToggleValue(Boolean toggleValue) {
- this.toggleValue = toggleValue;
- hasToggleValue = toggleValue != null;
- }
-
-
- public boolean isNull() {
- return textValue == null && dateValue == -1L && !hasToggleValue;
- }
-
- public boolean hasToggleValue() {
- return hasToggleValue;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- SavedAutofillValue that = (SavedAutofillValue) o;
-
- if (textValue != null ? !textValue.equals(that.textValue) : that.textValue != null)
- return false;
- if (dateValue != null ? !dateValue.equals(that.dateValue) : that.dateValue != null)
- return false;
- return toggleValue != null ? toggleValue.equals(that.toggleValue) : that.toggleValue == null;
-
- }
-
- @Override
- public int hashCode() {
- int result = textValue != null ? textValue.hashCode() : 0;
- result = 31 * result + (dateValue != null ? dateValue.hashCode() : 0);
- result = 31 * result + (toggleValue != null ? toggleValue.hashCode() : 0);
- return result;
- }
-}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java
index 6387d36b..ee461b0e 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java
@@ -29,14 +29,13 @@ import android.widget.Switch;
import android.widget.TextView;
import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.service.datasource.LocalAutofillRepository;
+import com.example.android.autofillframework.service.datasource.SharedPrefsAutofillRepository;
public class SettingsActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
setContentView(R.layout.settings_activity);
final MyPreferences preferences = MyPreferences.getInstance(this);
setupSettingsSwitch(R.id.settings_auth_responses_container,
@@ -68,7 +67,6 @@ public class SettingsActivity extends AppCompatActivity {
buildClearDataDialog().show();
}
});
-
setupSettingsButton(R.id.settings_auth_credentials_container,
R.id.settings_auth_credentials_label,
R.id.settings_auth_credentials_icon,
@@ -92,7 +90,7 @@ public class SettingsActivity extends AppCompatActivity {
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- LocalAutofillRepository.getInstance
+ SharedPrefsAutofillRepository.getInstance
(SettingsActivity.this).clear();
MyPreferences.getInstance(SettingsActivity.this)
.clearCredentials();
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
index 28d9c0b5..eb1f43c2 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
@@ -32,7 +32,6 @@
android:taskAffinity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@@ -40,32 +39,17 @@
android:name=".app.LoginActivity"
android:label="AF StandardLogin"
android:taskAffinity=".LoginActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<activity
android:name=".app.VirtualLoginActivity"
android:label="AF VirtualLogin"
android:taskAffinity=".VirtualLoginActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<activity android:name=".app.WelcomeActivity" />
<activity
android:name=".app.CreditCardActivity"
android:label="AF CreditCard"
android:taskAffinity=".CreditCardActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
-
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
</activity>
<!--
Including launcher icon for Autofill Settings to convenience.
@@ -78,7 +62,6 @@
android:taskAffinity=".SettingsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
-
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt
index 86dc9b25..bef16afe 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt
@@ -70,20 +70,21 @@ class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, a
}
override fun autofill(values: SparseArray<AutofillValue>) {
- // User has just selected a Dataset from the list of Autofill suggestions and the Dataset's
- // AutofillValue gets passed into this method.
+ // 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
+ // AutofillValues in the list.
Log.d(TAG, "autofill(): " + values)
for (i in 0..values.size() - 1) {
val id = values.keyAt(i)
val value = values.valueAt(i)
-
- mItems[id]?.let {
- if (!it.editable) {
- Log.w(TAG, "Item for autofillId $id is not editable: $it")
- return@autofill
+ mItems[id]?.let { item ->
+ if (item.editable) {
+ // Set the item's text to the text wrapped in the AutofillValue.
+ item.text = value.textValue
+ } else {
+ Log.w(TAG, "Item for autofillId $id is not editable: $item")
}
- // Set the item's text to the text wrapped in the AutofillValue.
- it.text = value.textValue
}
}
postInvalidate()
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt
index c6eb721f..52081afd 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt
@@ -51,6 +51,7 @@ class VirtualLoginActivity : AppCompatActivity() {
if (valid) {
val intent = WelcomeActivity.getStartActivityIntent(this@VirtualLoginActivity)
startActivity(intent)
+ finish()
} else {
Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show()
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.kt
index 12be45bb..d102a7e9 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.kt
@@ -97,8 +97,8 @@ class AuthActivity : Activity() {
} else {
val datasetName = intent.getStringExtra(EXTRA_DATASET_NAME)
clientFormDataMap?.let {
- it[datasetName]?.let {
- AutofillHelper.newDataset(this, autofillFields, it)?.let(this::setDatasetIntent)
+ it[datasetName]?.let { clientFormData ->
+ AutofillHelper.newDataset(this, autofillFields, clientFormData, false)?.let(this::setDatasetIntent)
}
}
}
@@ -115,7 +115,7 @@ class AuthActivity : Activity() {
companion object {
// Unique autofillId for dataset intents.
- private var sDatasetPendingIntentId = 0
+ private var datasetPendingIntentId = 0
internal fun getAuthIntentSenderForResponse(context: Context): IntentSender {
val intent = Intent(context, AuthActivity::class.java)
@@ -127,7 +127,7 @@ class AuthActivity : Activity() {
val intent = Intent(context, AuthActivity::class.java)
intent.putExtra(EXTRA_DATASET_NAME, datasetName)
intent.putExtra(EXTRA_FOR_RESPONSE, false)
- return PendingIntent.getActivity(context, ++sDatasetPendingIntentId, intent,
+ return PendingIntent.getActivity(context, ++datasetPendingIntentId, intent,
PendingIntent.FLAG_CANCEL_CURRENT).intentSender
}
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.kt
index b7470d7b..58666ef4 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.kt
@@ -37,10 +37,14 @@ object AutofillHelper {
* client View.
*/
fun newDataset(context: Context, autofillFields: AutofillFieldsCollection,
- clientFormData: ClientFormData): Dataset? {
+ clientFormData: ClientFormData, datasetAuth: Boolean): Dataset? {
clientFormData.datasetName?.let { datasetName ->
val datasetBuilder = Dataset.Builder(newRemoteViews(context.packageName, datasetName))
val setValueAtLeastOnce = clientFormData.applyToFields(autofillFields, datasetBuilder)
+ if (datasetAuth) {
+ val sender = AuthActivity.getAuthIntentSenderForDataset(context, datasetName)
+ datasetBuilder.setAuthentication(sender)
+ }
if (setValueAtLeastOnce) {
return datasetBuilder.build()
}
@@ -65,18 +69,8 @@ object AutofillHelper {
clientFormDataMap?.keys?.let { datasetNames ->
for (datasetName in datasetNames) {
clientFormDataMap[datasetName]?.let { clientFormData ->
- if (datasetAuth) {
- clientFormData.datasetName?.let {
- val datasetBuilder = Dataset.Builder(newRemoteViews(context.packageName, it))
- val sender = AuthActivity
- .getAuthIntentSenderForDataset(context, it)
- datasetBuilder.setAuthentication(sender)
- responseBuilder.addDataset(datasetBuilder.build())
- }
- } else {
- val dataset = newDataset(context, autofillFields, clientFormData)
- dataset?.let(responseBuilder::addDataset)
- }
+ val dataset = newDataset(context, autofillFields, clientFormData, datasetAuth)
+ dataset?.let(responseBuilder::addDataset)
}
}
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.kt
index 9bc2a8c2..41712bd1 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.kt
@@ -76,7 +76,8 @@ class MyAutofillService : AutofillService() {
callback.onSuccess(responseBuilder.build())
} else {
val datasetAuth = MyPreferences.isDatasetAuth(this)
- val clientFormDataMap = SharedPrefsAutofillRepository.getClientFormData(this, autofillFields.focusedAutofillHints, autofillFields.allAutofillHints)
+ val clientFormDataMap = SharedPrefsAutofillRepository.getClientFormData(this,
+ autofillFields.focusedAutofillHints, autofillFields.allAutofillHints)
val response = AutofillHelper.newResponse(this, datasetAuth, autofillFields, clientFormDataMap)
callback.onSuccess(response)
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.kt
index e05a2d51..2cb37eea 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.kt
@@ -22,12 +22,12 @@ import com.example.android.autofillframework.CommonUtil.TAG
import com.example.android.autofillframework.service.model.AutofillField
import com.example.android.autofillframework.service.model.AutofillFieldsCollection
import com.example.android.autofillframework.service.model.ClientFormData
-import com.example.android.autofillframework.service.model.MutableAutofillValue
+import com.example.android.autofillframework.service.model.SavableAutofillData
/**
* Parser for an AssistStructure object. This is invoked when the Autofill Service receives an
- * AssistStructure from the client Activity, representing its View hierarchy. In this
- * sample, it parses the hierarchy and records
+ * AssistStructure from the client Activity, representing its View hierarchy. In this sample, it
+ * parses the hierarchy and collects autofill metadata from {@link ViewNode}s along the way.
*/
internal class StructureParser(private val mStructure: AssistStructure) {
val autofillFields = AutofillFieldsCollection()
@@ -64,7 +64,7 @@ internal class StructureParser(private val mStructure: AssistStructure) {
autofillFields.add(AutofillField(viewNode))
} else {
clientFormData.setAutofillValuesForHints(viewNode.autofillHints,
- MutableAutofillValue(viewNode))
+ SavableAutofillData(viewNode))
}
}
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.kt
index a7eadc96..e2fb8708 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.kt
@@ -24,9 +24,9 @@ import com.google.gson.reflect.TypeToken
/**
- * Singleton autofill data repository, that stores autofill fields to SharedPreferences.
- * DISCLAIMER, you should not store sensitive fields like user data unencrypted. This is only done
- * here for simplicity and learning purposes.
+ * Singleton autofill data repository that stores autofill fields to SharedPreferences.
+ * Disclaimer: you should not store sensitive fields like user data unencrypted. This is done
+ * here only for simplicity and learning purposes.
*/
object SharedPrefsAutofillRepository : AutofillRepository {
private val SHARED_PREF_KEY = "com.example.android.autofillframework.service"
@@ -46,9 +46,13 @@ object SharedPrefsAutofillRepository : AutofillRepository {
val type = object : TypeToken<ClientFormData>() {}.type
Gson().fromJson<ClientFormData>(clientFormDataString, type)?.let { clientFormData ->
if (clientFormData.helpsWithHints(focusedAutofillHints)) {
+ // Saved data has data relevant to at least 1 of the hints associated with the
+ // View in focus.
hasDataForFocusedAutofillHints = true
clientFormData.datasetName?.let { datasetName ->
if (clientFormData.helpsWithHints(allAutofillHints)) {
+ // Saved data has data relevant to at least 1 of these hints associated with any
+ // of the Views in the hierarchy.
clientFormDataMap.put(datasetName, clientFormData)
}
}
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.kt
index 78980aaa..2dc844ae 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.kt
@@ -28,16 +28,16 @@ import java.util.HashMap
* dataset name associated with it.
*/
class ClientFormData constructor(var datasetName: String? = null,
- private val hintMap: HashMap<String, MutableAutofillValue> = HashMap<String, MutableAutofillValue>()) {
+ private val hintMap: HashMap<String, SavableAutofillData> = HashMap<String, SavableAutofillData>()) {
private val TAG = "ClientFormData"
/**
* Sets values for a list of autofillHints.
*/
- fun setAutofillValuesForHints(autofillHints: Array<String>, autofillValue: MutableAutofillValue) {
+ fun setAutofillValuesForHints(autofillHints: Array<String>, autofillData: SavableAutofillData) {
autofillHints.forEach { hint ->
- hintMap[hint] = autofillValue
+ hintMap[hint] = autofillData
}
}
@@ -86,6 +86,10 @@ class ClientFormData constructor(var datasetName: String? = null,
return setValueAtLeastOnce
}
+ /**
+ * Returns whether this model contains autofill data that is relevant to any of the
+ * autofillHints that are passed in.
+ */
fun helpsWithHints(autofillHints: List<String>): Boolean {
for (autofillHint in autofillHints) {
hintMap[autofillHint]?.let { savedAutofillValue ->
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/MutableAutofillValue.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.kt
index 702a1358..44baebfa 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/MutableAutofillValue.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavableAutofillData.kt
@@ -19,9 +19,9 @@ import android.app.assist.AssistStructure
import android.view.autofill.AutofillValue
/**
- * Mutable, JSON serializable data class containing the same data as an [AutofillValue].
+ * JSON serializable data class containing the same data as an [AutofillValue].
*/
-class MutableAutofillValue(viewNode: AssistStructure.ViewNode) {
+class SavableAutofillData(viewNode: AssistStructure.ViewNode) {
var textValue: String? = null
var dateValue: Long? = null
var toggleValue: Boolean? = null
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml
deleted file mode 100644
index 22074a2b..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<!--
- Copyright 2013 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.
- -->
-
-<resources>
-
- <!-- Semantic definitions -->
-
- <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
- <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml
deleted file mode 100644
index 03d19741..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
- Copyright 2013 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.
- -->
-
-<resources>
-
- <style name="Widget.SampleMessage">
- <item name="android:textAppearance">?android:textAppearanceLarge</item>
- <item name="android:lineSpacingMultiplier">1.2</item>
- <item name="android:shadowDy">-6.5</item>
- </style>
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml
deleted file mode 100644
index 8c1ea66f..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- Copyright 2013 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.
- -->
-
-<resources>
-
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Holo.Light" />
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml
deleted file mode 100644
index 8b6ec3f8..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 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.
--->
-
-<resources>
-
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml
deleted file mode 100644
index c778e4f9..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 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.
--->
-
-<resources>
-
- <!-- Activity themes -->
- <style name="Theme.Base" parent="android:Theme.Material.Light">
- </style>
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml
deleted file mode 100644
index ddf8b07d..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2013 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.
--->
-
-<resources>
- <string name="app_name">AutofillFramework</string>
- <string name="intro_message">
- <![CDATA[
-
-
- This sample app demos the Autofill feature introduced in Android O.
-
-
- ]]>
- </string>
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
index b5611ae1..f9448a74 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
@@ -16,6 +16,7 @@
-->
<resources>
+ <string name="app_name">Autofill Sample</string>
<string name="settings_name">Autofill Settings</string>
<string name="username_label">Username</string>
<string name="password_label">Password</string>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml
deleted file mode 100644
index 39e710b5..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<!--
- Copyright 2013 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.
- -->
-
-<resources>
-
- <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
-
- <dimen name="margin_tiny">4dp</dimen>
- <dimen name="margin_small">8dp</dimen>
- <dimen name="margin_medium">16dp</dimen>
- <dimen name="margin_large">32dp</dimen>
- <dimen name="margin_huge">64dp</dimen>
-
- <!-- Semantic definitions -->
-
- <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
- <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml
deleted file mode 100644
index 6e7d593d..00000000
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<!--
- Copyright 2013 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.
- -->
-
-<resources>
-
- <!-- Activity themes -->
-
- <style name="Theme.Base" parent="android:Theme.Light" />
-
- <style name="Theme.Sample" parent="Theme.Base" />
-
- <style name="AppTheme" parent="Theme.Sample" />
- <!-- Widget styling -->
-
- <style name="Widget" />
-
- <style name="Widget.SampleMessage">
- <item name="android:textAppearance">?android:textAppearanceMedium</item>
- <item name="android:lineSpacingMultiplier">1.1</item>
- </style>
-
- <style name="Widget.SampleMessageTile">
- <item name="android:background">@drawable/tile</item>
- <item name="android:shadowColor">#7F000000</item>
- <item name="android:shadowDy">-3.5</item>
- <item name="android:shadowRadius">2</item>
- </style>
-
-</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/README.md b/input/autofill/AutofillFramework/kotlinApp/README.md
index 53e4a7f0..9b060561 100644
--- a/input/autofill/AutofillFramework/kotlinApp/README.md
+++ b/input/autofill/AutofillFramework/kotlinApp/README.md
@@ -1,63 +1,98 @@
-Android AutofillFramework Sample (Kotlin)
+
+Android AutofillFramework Sample
===================================
This sample demonstrates the use of the Autofill Framework. It includes implementations of client
-Activities that want to be autofilled, and a Service that can provide autofill data to client
-Activities. For simplicity, this sample's service uses mock data to autofill what it thinks are
-username and password text fields.
+Activities with views that should be autofilled, and a Service that can provide autofill data to
+client Activities.
Introduction
------------
This sample demonstrates the use of the Autofill framework from the service side and the client
-side. In practice, only a small handful of apps will develop Autofill services because a device will
-only have one service as default at a time. However, all apps targeting O with any autofillable
-fields should follow the necessary steps to ensure their Views can be autofilled. Most of the time,
-there is little to no extra code involved, but the use of custom views and views with virtual child
-views requires more work.
-
-The sample's Autofill service is implemented to parse the client's view hierarchy in search of text
-fields that it has data for. If such text fields exist in the hierarchy, the service sends data
-suggestions to the client to fill in those text fields. In this basic sample, it will only look for
-views whose resource IDs are "usernameField" and "passwordField" and will send mock credential data
-accordingly. A real Autofill service would attempt to autofill more than just login credentials and
-would be able to fill in other view types in addition to text fields (e.g. spinners, checkboxes,
-etc.). It would also use more advanced heuristics to determine what data to send to which views.
-
-To set the device's default Autofill service to the one in the sample, edit
-**Settings** > **Apps &amp; Notifications** > **Default Apps** > **Auto-fill app** and select the
-sample app. To edit the service's settings, open the **Autofill Settings** launcher icon. Here, you
-can set whether you want to enable authentication on the entire Autofill Response or just on
-individual datasets. You can also set the number of mock datasets that are sent to the client app.
-
-The client side of the app has two Activities that each have a username field and a password field.
-One of the Activities uses standard views and the other Activity uses a custom view with virtual
-children. The standard views do not require any extra code to allow autofill. The following code
-example shows the `View` method you have to override in order to provide view hierarchy data to the
-Autofill service. This is triggered when the `View` goes into focus and Android invokes an Autofill
-request.
-
-```java
-/*
-This method is responsible for building the ViewStructure that gets passed to the AutoFillService
-by the framework when it is time to find Autofill suggestions. To do this, it should traverse
-through its view hierarchy and add views to the ViewStructure on the way.
-*/
-@Override
-public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) {
- structure.setClassName(getClass().getName());
- int childrenSize = mItems.size();
- int index = structure.addChildCount(childrenSize);
- for (int i = 0; i < childrenSize; i++) {
- Item item = mItems.valueAt(i);
- ViewStructure child = structure.newChild(index, item.id, flags);
- child.setSanitized(item.sanitized);
- child.setText(item.text);
- child.setAutoFillValue(AutoFillValue.forText(item.text));
- child.setFocused(item.line.focused);
- child.setId(item.id, getContext().getPackageName(), null, item.line.idEntry);
- index++;
- }
+side. In practice, only a small handful of apps will develop Autofill services because a device
+will only have one service as default at a time, and there is just a small number of 3rd-party apps
+providing these services (typically password managers). However, all apps targeting O with any
+autofillable fields should follow the necessary steps to 1) ensure their views can be autofilled
+and 2) optimize their autofill performance. Most of the time, there is little to no extra code
+involved, but the use of custom views and views with virtual child views requires more work.
+
+The sample's Autofill service is implemented to parse the client's view hierarchy in search of
+autofillable fields that it has data for. If such fields exist in the hierarchy, the service sends
+data suggestions to the client to autofill those fields. The client uses the following attributes
+to specify autofill properties: `importantForAutofill`, `autofillHints`, and `autofillType`.
+`importantForAutofill` specifies whether the view is autofillable. `autofillHints` is a list of
+strings that hint to the service **what** data to fill the view with. This sample service only
+supports the hints listed [here](https://developer.android.com/reference/android/view/View.html#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE)
+with the prefix AUTOFILL_HINT_*. `autofillType` tells the service the type of data it expects to
+receive (i.e. a list index, a date, or a string). Specifying `autofillType` is only necessary
+when implementing a custom view since all of the provided widgets in the UI toolkit do this for you.
+
+To set the device's default Autofill service to the one in the sample, edit **Settings** >
+**System** > **Languages &amp; Input** > **Advanced** > **Auto-fill service** and select the sample
+app. To edit the service's settings, tap the settings icon next to the **Auto-fill service** list
+item or open the **Autofill Settings** launcher icon.. Here, you can set whether you want to enable
+authentication on the entire autofill Response or just on individual autofill datasets. You should
+also set the master password to “unlock” authenticated autofill data with.
+
+**Note:** This sample service stores all autofill data in SharedPreferences and thus is not secure.
+Be careful about what you store when experimenting with the sample because anyone with root access
+to your device will be able to view your autofill data.
+
+The client side of the app has three Activities that each have autofillable fields. The first
+Activity uses standard views to comprise a login form. Very little needs to be done by the client
+app to ensure the views get autofilled properly. The second Activity uses a custom view with
+virtual children, meaning some autofillable child views are not known to the View hierarchy to be
+child views. Supporting autofill on these child views is a little more involved.
+
+The following code snippet shows how to signal to the autofill service that a specific
+autofillable virtual view has come into focus:
+
+```kotlin
+class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, attrs) {
+...
+ // Cache AutofillManager system service
+ private val autofillManager: AutofillManager = context.getSystemService(AutofillManager::class.java)
+...
+ // Notify service which virtual view has come into focus.
+ autofillManager.notifyViewEntered(this@CustomVirtualView, id, absBounds)
+...
+ // Notify service that a virtual view has left focus.
+ autofillManager.notifyViewExited(this@CustomVirtualView, id)
+}
+```
+
+Now that the autofillable view has signaled to the service that it has been autofilled, it needs
+to provide the virtual view hierarchy to the Autofill service. This is done out of the box for
+views part of the UI toolkit, but you need to implement this yourself if you have the view has
+virtual child views. The following code example shows the `View` method you have to override in
+order to provide this view hierarchy data to the Autofill service.
+
+```kotlin
+override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {
+ // Build a ViewStructure to pack in AutoFillService requests.
+ structure.setClassName(javaClass.name)
+ val childrenSize = mItems.size()
+ Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags + ", items = "
+ + childrenSize + ", extras: " + bundleToString(structure.extras))
+ var index = structure.addChildCount(childrenSize)
+ // Traverse through the view hierarchy, including virtual child views. For each view, we
+ // need to set the relevant autofill metadata and add it to the ViewStructure.
+ for (i in 0..childrenSize - 1) {
+ val item = mItems.valueAt(i)
+ Log.d(TAG, "Adding new child at index $index: $item")
+ val child = structure.newChild(index)
+ child.setAutofillId(structure, item.id)
+ child.setAutofillHints(item.hints)
+ child.setAutofillType(item.type)
+ child.setDataIsSensitive(!item.sanitized)
+ child.text = item.text
+ child.setAutofillValue(AutofillValue.forText(item.text))
+ child.setFocused(item.focused)
+ child.setId(item.id, context.packageName, null, item.line.idEntry)
+ child.setClassName(item.className)
+ index++
+ }
}
```
@@ -65,30 +100,29 @@ After the service processes the Autofill request and sends back a series of Auto
(wrapped in a `Response` object), the user can pick which `Dataset` they want to autofill their
views with. When a `Dataset` is selected, this method is invoked for all of the views that were
associated with that `Dataset` by the service. For example, the `Dataset` might contain Autofill
-values for username, password, birthday, and address. This method would then be invoked on all four
-of those fields. The following code example shows how the sample app implements the method to
-deliver a UI update to the appropriate child view after the user makes their selection.
-
-```java
-/*
-User has just selected a Dataset from the list of Autofill suggestions and the Dataset's
-AutoFillValue gets passed into this method. This method updates the UI based on the data
-in the AutoFillValue.
-*/
-@Override
-public void autoFillVirtual(int id, AutoFillValue value) {
- Item item = mItems.get(id);
- if (item == null) {
- // ID not recognized so no-op.
- return;
- }
- if (!item.editable) {
- // Component is not editable so no-op.
- return;
- }
- // Set the virtual child view's text to the text wrapped in the AutoFillValue.
- item.text = value.getTextValue();
- postInvalidate();
+values for username, password, birthday, and address. This method would then be invoked on all
+four of those fields. The following code example shows how the sample app implements the method
+to deliver a UI update to the appropriate child view after the user makes their selection.
+
+```kotlin
+override fun autofill(values: SparseArray<AutofillValue>) {
+ // 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
+ // AutofillValues in the list.
+ for (i in 0..values.size() - 1) {
+ val id = values.keyAt(i)
+ val value = values.valueAt(i)
+ mItems[id]?.let { item ->
+ if (item.editable) {
+ // Set the item's text to the text wrapped in the AutofillValue.
+ item.text = value.textValue
+ } else {
+ // Component not editable, so no-op.
+ }
+ }
+ }
+ postInvalidate()
}
```
@@ -96,8 +130,10 @@ Pre-requisites
--------------
- Android SDK Preview O
-- Android Build Tools v25.0.3
-- Android Support Repository
+- Android Studio 3.0+
+- Android Build Tools v26+
+- Android Support Repository v26+
+- Gradle v3.0+
Screenshots
-------------
diff --git a/input/autofill/AutofillFramework/template-params.xml b/input/autofill/AutofillFramework/template-params.xml
index 5bac84df..e1cc3252 100644
--- a/input/autofill/AutofillFramework/template-params.xml
+++ b/input/autofill/AutofillFramework/template-params.xml
@@ -212,12 +212,14 @@ public void autofill(SparseArray<AutofillValue> values) {
final int id = values.keyAt(i);
final AutofillValue value = values.valueAt(i);
final Item item = mItems.get(id);
- if (item == null || !item.editable) {
- // Component either not found or is not editable, so no-op.
- return;
+ 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) {
+ // Component not found, so no-op.
+ } else {
+ // Component not editable, so no-op.
}
- // Set the item's text to the text wrapped in the AutofillValue.
- item.text = value.getTextValue();
}
postInvalidate();
}