aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Sigelbaum <sigelbaum@google.com>2017-05-12 20:39:49 -0700
committerDouglas Sigelbaum <sigelbaum@google.com>2017-05-16 18:06:57 -0700
commit956f35b47d5bb8fb4686bda5b44bc5371361001f (patch)
tree0d6d7d16166625ae0a2dfea8675aac61f8c32ecb
parent5525a586f56baeb2310dbacfe34a96afd3dd3f0f (diff)
downloadandroid-956f35b47d5bb8fb4686bda5b44bc5371361001f.tar.gz
Implemented autofill saving.
Also added basic support for autofilling a client's CC info. Refactored a bit to make this possible. Test: manual Bug: 38182790 Change-Id: I7c70ea6a962bf816a7c527012b3fc6918cdbde91
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java1
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.java5
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java79
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java64
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java46
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java66
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java8
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java69
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java5
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java58
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/CreditCardInfo.java86
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/DatasetModel.java28
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/LoginCredential.java50
-rw-r--r--input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml12
14 files changed, 420 insertions, 157 deletions
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 6b01039e..61197efe 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
@@ -43,7 +43,6 @@ import static com.example.android.autofillframework.CommonUtil.bundleToString;
/**
* Custom View with virtual child views for Username/Password text fields.
*/
-
public class CustomVirtualView extends View {
private static final String TAG = "CustomView";
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 cb7192eb..051572a4 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.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
-import android.view.autofill.AutofillManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
@@ -49,10 +48,6 @@ public class LoginActivity extends Activity {
mClearButton = findViewById(R.id.clear);
mUsernameEditText = findViewById(R.id.usernameField);
mPasswordEditText = findViewById(R.id.passwordField);
- mUsernameEditText.setAutofillHints(View.AUTOFILL_HINT_USERNAME);
- mPasswordEditText.setAutofillHints(View.AUTOFILL_HINT_PASSWORD);
- mUsernameEditText.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES);
- mPasswordEditText.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES);
mLoginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
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 356fa678..0dd3a923 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
@@ -35,11 +35,11 @@ import android.widget.Toast;
import com.example.android.autofillframework.R;
import com.example.android.autofillframework.service.datasource.LocalAutofillRepository;
-import com.example.android.autofillframework.service.model.AutofillField;
+import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
+import com.example.android.autofillframework.service.model.CreditCardInfo;
import com.example.android.autofillframework.service.model.LoginCredential;
import java.util.HashMap;
-import java.util.List;
import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE;
import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT;
@@ -83,9 +83,9 @@ public class AuthActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.auth_activity);
- mCancel = (Button) findViewById(R.id.cancel);
- mLogin = (Button) findViewById(R.id.login);
- mMasterPassword = (EditText) findViewById(R.id.master_password);
+ mCancel = findViewById(R.id.cancel);
+ mLogin = findViewById(R.id.login);
+ mMasterPassword = findViewById(R.id.master_password);
mLogin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -104,6 +104,7 @@ public class AuthActivity extends Activity {
}
private void login() {
+ // TODO set master username/password to Settings.
Editable password = mMasterPassword.getText();
Log.d(TAG, "login: " + password);
if (password.length() == 0) {
@@ -136,32 +137,50 @@ public class AuthActivity extends Activity {
AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE);
StructureParser parser = new StructureParser(structure);
parser.parse();
- List<AutofillField> autofillFields = parser.getAutofillFields();
- HashMap<String, LoginCredential> loginCredentialMap =
- LocalAutofillRepository.getInstance(this).getLoginCredentials();
- int saveType = parser.getSaveTypes();
- if (loginCredentialMap == null || loginCredentialMap.isEmpty()) {
- Log.d(TAG, "No Autofill data found for this Activity.");
- return;
- }
+ AutofillFieldsCollection autofillFields = parser.getAutofillFields();
+ int saveTypes = parser.getSaveTypes();
mReplyIntent = new Intent();
- if (forResponse) {
- // The response protected by auth, so we can send the entire response since we
- // passed auth.
- FillResponse response = AutofillHelper
- .newCredentialResponse(this, false, autofillFields, saveType, loginCredentialMap);
- if (response != null) {
- mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, response);
- }
- } else {
- // The dataset selected by the user was protected by auth, so we can send that dataset.
- String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
- if (loginCredentialMap.containsKey(datasetName)) {
- LoginCredential credential = loginCredentialMap.get(datasetName);
- Dataset dataset = AutofillHelper
- .newCredentialDataset(this, autofillFields, credential);
- mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
- }
+ switch (parser.getClientPageType()) {
+ case StructureParser.CLIENT_PAGE_TYPE_LOGIN:
+ HashMap<String, LoginCredential> loginCredentialMap =
+ LocalAutofillRepository.getInstance(this).getLoginCredentials();
+ if (loginCredentialMap == null || loginCredentialMap.isEmpty()) {
+ Log.d(TAG, "No Autofill data found for this Activity.");
+ return;
+ }
+ if (forResponse) {
+ setResponseIntent(AutofillHelper.newResponse
+ (this, false, autofillFields, saveTypes, loginCredentialMap));
+ } else {
+ String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
+ setDatasetIntent(AutofillHelper.newDataset
+ (this, autofillFields, loginCredentialMap.get(datasetName)));
+ }
+ break;
+ case StructureParser.CLIENT_PAGE_TYPE_CREDIT_CARD_INFO:
+ HashMap<String, CreditCardInfo> creditCardInfoMap =
+ LocalAutofillRepository.getInstance(this).getCreditCardInfo();
+ if (creditCardInfoMap == null || creditCardInfoMap.isEmpty()) {
+ Log.d(TAG, "No Autofill data found for this Activity.");
+ return;
+ }
+ if (forResponse) {
+ setResponseIntent(AutofillHelper.newResponse
+ (this, false, autofillFields, saveTypes, creditCardInfoMap));
+ } else {
+ String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME);
+ setDatasetIntent(AutofillHelper.newDataset
+ (this, autofillFields, creditCardInfoMap.get(datasetName)));
+ }
+ break;
}
}
+
+ private void setResponseIntent(FillResponse fillResponse) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse);
+ }
+
+ private void setDatasetIntent(Dataset dataset) {
+ mReplyIntent.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset);
+ }
}
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 8087843e..38095d31 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
@@ -21,17 +21,14 @@ import android.service.autofill.Dataset;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
import android.util.Log;
-import android.view.View;
import android.view.autofill.AutofillId;
-import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
import com.example.android.autofillframework.R;
-import com.example.android.autofillframework.service.model.AutofillField;
-import com.example.android.autofillframework.service.model.LoginCredential;
+import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
+import com.example.android.autofillframework.service.model.DatasetModel;
import java.util.HashMap;
-import java.util.List;
import java.util.Set;
import static com.example.android.autofillframework.CommonUtil.TAG;
@@ -45,29 +42,11 @@ public final class AutofillHelper {
* Wraps autofill data in a LoginCredential Dataset object which can then be sent back to the
* client View.
*/
- public static Dataset newCredentialDataset(Context context, List<AutofillField> autofillFields, LoginCredential loginCredential) {
+ public static Dataset newDataset(Context context,
+ AutofillFieldsCollection autofillFields, DatasetModel datasetModel) {
Dataset.Builder datasetBuilder = new Dataset.Builder
- (newRemoteViews(context.getPackageName(), loginCredential.getDatasetName()));
- // Not using enhanced for loop on Collection to prevent extra iterator allocation.
- for (int i = 0; i < autofillFields.size(); i++) {
- AutofillField field = autofillFields.get(i);
- boolean shouldBreak = false;
- for (String autofillHint : field.getHints()) {
- switch (autofillHint) {
- case View.AUTOFILL_HINT_USERNAME:
- datasetBuilder.setValue(field.getId(), AutofillValue.forText(loginCredential.getUsername()));
- shouldBreak = true;
- break;
- case View.AUTOFILL_HINT_PASSWORD:
- datasetBuilder.setValue(field.getId(), AutofillValue.forText(loginCredential.getPassword()));
- shouldBreak = true;
- break;
- }
- if (shouldBreak) {
- break;
- }
- }
- }
+ (newRemoteViews(context.getPackageName(), datasetModel.getDatasetName()));
+ datasetModel.applyToFields(autofillFields, datasetBuilder);
return datasetBuilder.build();
}
@@ -81,40 +60,33 @@ public final class AutofillHelper {
* Wraps autofill data in a Response object (essentially a series of Datasets) which can then
* be sent back to the client View.
*/
- public static FillResponse newCredentialResponse(Context context, boolean datasetAuth,
- List<AutofillField> autofillFields, int saveType,
- HashMap<String, LoginCredential> loginCredentialMap) {
+ public static <T extends DatasetModel> FillResponse newResponse(Context context,
+ boolean datasetAuth, AutofillFieldsCollection autofillFields, int saveType,
+ HashMap<String, T> datasetModelMap) {
FillResponse.Builder responseBuilder = new FillResponse.Builder();
- Set<String> datasetNames = loginCredentialMap.keySet();
+ Set<String> datasetNames = datasetModelMap.keySet();
for (String datasetName : datasetNames) {
- LoginCredential loginCredential = loginCredentialMap.get(datasetName);
+ T datasetModel = datasetModelMap.get(datasetName);
if (datasetAuth) {
Dataset.Builder datasetBuilder =
- new Dataset.Builder(newRemoteViews(context.getPackageName(), loginCredential.getDatasetName()));
- IntentSender sender =
- AuthActivity.getAuthIntentSenderForDataset(context, loginCredential.getDatasetName());
+ new Dataset.Builder(newRemoteViews
+ (context.getPackageName(), datasetModel.getDatasetName()));
+ IntentSender sender = AuthActivity
+ .getAuthIntentSenderForDataset(context, datasetModel.getDatasetName());
datasetBuilder.setAuthentication(sender);
responseBuilder.addDataset(datasetBuilder.build());
} else {
- Dataset dataset = newCredentialDataset(context, autofillFields, loginCredential);
+ Dataset dataset = newDataset(context, autofillFields, datasetModel);
responseBuilder.addDataset(dataset);
}
}
if (saveType != 0) {
- AutofillId[] autofillIds = getAutofillIds(autofillFields);
+ AutofillId[] autofillIds = autofillFields.getAutofillIds();
responseBuilder.setSaveInfo(new SaveInfo.Builder(saveType, autofillIds).build());
return responseBuilder.build();
} else {
- Log.d(TAG, "No Autofill data found.");
+ Log.d(TAG, "These fields are not meant to be saved by autofill.");
return null;
}
}
-
- public static AutofillId[] getAutofillIds(List<AutofillField> autofillFields) {
- AutofillId[] autofillIds = new AutofillId[autofillFields.size()];
- for (int i = 0; i < autofillFields.size(); i++) {
- autofillIds[i] = autofillFields.get(i).getId();
- }
- return autofillIds;
- }
}
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 aa0d38e3..c259cb0b 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
@@ -27,18 +27,18 @@ import android.service.autofill.FillResponse;
import android.service.autofill.SaveCallback;
import android.service.autofill.SaveRequest;
import android.util.Log;
-import android.view.autofill.AutofillId;
import android.widget.RemoteViews;
import com.example.android.autofillframework.R;
import com.example.android.autofillframework.service.datasource.LocalAutofillRepository;
-import com.example.android.autofillframework.service.model.AutofillField;
+import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
+import com.example.android.autofillframework.service.model.CreditCardInfo;
+import com.example.android.autofillframework.service.model.DatasetModel;
import com.example.android.autofillframework.service.model.LoginCredential;
import com.example.android.autofillframework.service.settings.MyPreferences;
import java.util.HashMap;
import java.util.List;
-import java.util.Set;
import static com.example.android.autofillframework.CommonUtil.TAG;
import static com.example.android.autofillframework.CommonUtil.bundleToString;
@@ -80,32 +80,38 @@ public class MyAutofillService extends AutofillService {
// Parse AutoFill data in Activity
StructureParser parser = new StructureParser(structure);
parser.parse();
- List<AutofillField> autofillFields = parser.getAutofillFields();
- HashMap<String, LoginCredential> loginCredentialMap =
- LocalAutofillRepository.getInstance(this).getLoginCredentials();
- AutofillId[] autofillIds = AutofillHelper.getAutofillIds(autofillFields);
+ AutofillFieldsCollection autofillFields = parser.getAutofillFields();
int saveTypes = parser.getSaveTypes();
FillResponse.Builder responseBuilder = new FillResponse.Builder();
- // Check user's settings for authenticating Responses and Datasets
+ // Check user's settings for authenticating Responses and Datasets.
boolean responseAuth = MyPreferences.getInstance(this).isResponseAuth();
if (responseAuth) {
// If the entire Autofill Response is authenticated, AuthActivity is used
- // to generate Response
+ // to generate Response.
IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this);
RemoteViews presentation = AutofillHelper
.newRemoteViews(getPackageName(), getString(R.string.autofill_sign_in_prompt));
- responseBuilder.setAuthentication
- (autofillIds, sender, presentation);
+ responseBuilder
+ .setAuthentication(autofillFields.getAutofillIds(), sender, presentation);
callback.onSuccess(responseBuilder.build());
} else {
boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth();
FillResponse response = null;
- if (parser.isLoginPage()) {
- response = AutofillHelper.newCredentialResponse(
- this, datasetAuth, autofillFields, saveTypes, loginCredentialMap);
+ switch (parser.getClientPageType()) {
+ case StructureParser.CLIENT_PAGE_TYPE_LOGIN:
+ HashMap<String, LoginCredential> loginCredentialMap =
+ LocalAutofillRepository.getInstance(this).getLoginCredentials();
+ response = AutofillHelper.newResponse
+ (this, datasetAuth, autofillFields, saveTypes, loginCredentialMap);
+ break;
+ case StructureParser.CLIENT_PAGE_TYPE_CREDIT_CARD_INFO:
+ HashMap<String, CreditCardInfo> creditCardInfoMap =
+ LocalAutofillRepository.getInstance(this).getCreditCardInfo();
+ response = AutofillHelper.newResponse
+ (this, datasetAuth, autofillFields, saveTypes, creditCardInfoMap);
+ break;
}
-
callback.onSuccess(response);
}
}
@@ -118,7 +124,15 @@ public class MyAutofillService extends AutofillService {
Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data));
StructureParser parser = new StructureParser(structure);
parser.parse();
- List<AutofillField> autofillFields = parser.getAutofillFields();
+ DatasetModel datasetModel = parser.getDatasetModel();
+ switch (parser.getClientPageType()) {
+ case StructureParser.CLIENT_PAGE_TYPE_LOGIN:
+ LocalAutofillRepository.getInstance(this).saveLoginCredential(datasetModel);
+ break;
+ case StructureParser.CLIENT_PAGE_TYPE_CREDIT_CARD_INFO:
+ LocalAutofillRepository.getInstance(this).saveCreditCardInfo(datasetModel);
+ break;
+ }
}
@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 ea3f7630..b9a1becc 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
@@ -21,12 +21,11 @@ import android.app.assist.AssistStructure.WindowNode;
import android.util.Log;
import android.view.View;
-import com.example.android.autofillframework.service.datasource.AutofillRepository;
import com.example.android.autofillframework.service.model.AutofillField;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
+import com.example.android.autofillframework.service.model.AutofillFieldsCollection;
+import com.example.android.autofillframework.service.model.CreditCardInfo;
+import com.example.android.autofillframework.service.model.DatasetModel;
+import com.example.android.autofillframework.service.model.LoginCredential;
import static com.example.android.autofillframework.CommonUtil.TAG;
@@ -38,13 +37,17 @@ import static com.example.android.autofillframework.CommonUtil.TAG;
*/
final class StructureParser {
- private AssistStructure structure;
- private int mSaveTypes = 0;
- private List<AutofillField> mAutofillFields;
+ public static final int CLIENT_PAGE_TYPE_UNRECOGNIZED = 0;
+ public static final int CLIENT_PAGE_TYPE_LOGIN = 1;
+ public static final int CLIENT_PAGE_TYPE_CREDIT_CARD_INFO = 2;
+
+ private final AutofillFieldsCollection mAutofillFields = new AutofillFieldsCollection();
+ private final AssistStructure structure;
+ private int mClientPageType = CLIENT_PAGE_TYPE_UNRECOGNIZED;
+ private DatasetModel mDatasetModel;
StructureParser(AssistStructure structure) {
this.structure = structure;
- mAutofillFields = new ArrayList<>();
}
/**
@@ -59,15 +62,7 @@ final class StructureParser {
ViewNode view = node.getRootViewNode();
parseLocked(view);
}
- updateSaveTypes();
- }
-
- private void updateSaveTypes() {
- mSaveTypes = 0;
- for (int i = 0; i < mAutofillFields.size(); i++) {
- AutofillField field = mAutofillFields.get(i);
- mSaveTypes |= field.getSaveType();
- }
+ updateClientPageType();
}
private void parseLocked(ViewNode view) {
@@ -84,19 +79,38 @@ final class StructureParser {
}
}
- public List<AutofillField> getAutofillFields() {
+ public AutofillFieldsCollection getAutofillFields() {
return mAutofillFields;
}
public int getSaveTypes() {
- return mSaveTypes;
+ return mAutofillFields.getSaveType();
}
- /**
- * Is this View structure
- */
- public boolean isLoginPage() {
- // TODO remove this.
- return true;
+ private void updateClientPageType() {
+ if (mAutofillFields.containsHint(View.AUTOFILL_HINT_USERNAME) &&
+ mAutofillFields.containsHint(View.AUTOFILL_HINT_PASSWORD)) {
+ mClientPageType = CLIENT_PAGE_TYPE_LOGIN;
+ // This only takes the value from one view with USERNAME hint.
+ // In a real service we would save all of the username values.
+ String username = mAutofillFields.getFieldsForHint
+ (View.AUTOFILL_HINT_USERNAME).get(0).getValue();
+ String password = mAutofillFields.getFieldsForHint
+ (View.AUTOFILL_HINT_PASSWORD).get(0).getValue();
+ mDatasetModel = new LoginCredential(username, password);
+ } else if (mAutofillFields.containsHint(View.AUTOFILL_HINT_CREDIT_CARD_NUMBER)) {
+ mClientPageType = CLIENT_PAGE_TYPE_CREDIT_CARD_INFO;
+ String creditCardNumber = mAutofillFields.getFieldsForHint
+ (View.AUTOFILL_HINT_CREDIT_CARD_NUMBER).get(0).getValue();
+ mDatasetModel = new CreditCardInfo(creditCardNumber);
+ }
+ }
+
+ public int getClientPageType() {
+ return mClientPageType;
+ }
+
+ public DatasetModel getDatasetModel() {
+ return mDatasetModel;
}
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java
index b10bc8ef..9c9bcfc7 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java
@@ -15,6 +15,8 @@
*/
package com.example.android.autofillframework.service.datasource;
+import com.example.android.autofillframework.service.model.CreditCardInfo;
+import com.example.android.autofillframework.service.model.DatasetModel;
import com.example.android.autofillframework.service.model.LoginCredential;
import java.util.HashMap;
@@ -29,5 +31,9 @@ public interface AutofillRepository {
/**
* Saves LoginCredential under this datasetName.
*/
- void saveLoginCredential(LoginCredential loginCredential);
+ void saveLoginCredential(DatasetModel loginCredential);
+
+ HashMap<String, CreditCardInfo> getCreditCardInfo();
+
+ void saveCreditCardInfo(DatasetModel creditCardInfo);
}
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/LocalAutofillRepository.java
index 6c00c81f..789d2824 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/LocalAutofillRepository.java
@@ -4,6 +4,8 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.util.ArraySet;
+import com.example.android.autofillframework.service.model.CreditCardInfo;
+import com.example.android.autofillframework.service.model.DatasetModel;
import com.example.android.autofillframework.service.model.LoginCredential;
import org.json.JSONException;
@@ -19,15 +21,17 @@ import java.util.Set;
*/
public class LocalAutofillRepository implements AutofillRepository {
private static final String SHARED_PREF_KEY = "com.example.android.autofillframework.service";
- private static final String LOGIN_CREDENTIAL_DATASETS_KEY = "loginCredentialDatasetNames";
+ private static final String LOGIN_CREDENTIAL_DATASETS_KEY = "loginCredentialDatasets";
+ private static final String CREDIT_CARD_INFO_DATASETS_KEY = "creditCardInfoDatasets";
+ private static final String DATASET_NUMBER_KEY = "datasetNumber";
private static LocalAutofillRepository sInstance;
private final SharedPreferences mPrefs;
- private int datasetNumber = 0;
+ // TODO prepend with autofill data set in Settings.
private LocalAutofillRepository(Context context) {
- mPrefs = context.getSharedPreferences(SHARED_PREF_KEY,
+ mPrefs = context.getApplicationContext().getSharedPreferences(SHARED_PREF_KEY,
Context.MODE_PRIVATE);
}
@@ -58,12 +62,57 @@ public class LocalAutofillRepository implements AutofillRepository {
}
@Override
- public void saveLoginCredential(LoginCredential loginCredential) {
- String datasetName = "dataset-" + datasetNumber++;
- loginCredential.setDatasetName(datasetName);
- Set<String> loginCredentialStringSet =
- mPrefs.getStringSet(LOGIN_CREDENTIAL_DATASETS_KEY, new ArraySet<String>());
- loginCredentialStringSet.add(loginCredential.toJson().toString());
- mPrefs.edit().putStringSet(LOGIN_CREDENTIAL_DATASETS_KEY, loginCredentialStringSet).apply();
+ public HashMap<String, CreditCardInfo> getCreditCardInfo() {
+ try {
+ HashMap<String, CreditCardInfo> creditCardInfoMap = new HashMap<>();
+ Set<String> creditCardInfoStringSet =
+ mPrefs.getStringSet(CREDIT_CARD_INFO_DATASETS_KEY, new ArraySet<String>());
+ for (String creditCardInfoString : creditCardInfoStringSet) {
+ CreditCardInfo creditCardInfo = CreditCardInfo
+ .fromJson(new JSONObject(creditCardInfoString));
+ if (creditCardInfo != null) {
+ creditCardInfoMap.put(creditCardInfo.getDatasetName(), creditCardInfo);
+ }
+ }
+ return creditCardInfoMap;
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public void saveLoginCredential(DatasetModel loginCredential) {
+ saveDatasetModel(loginCredential, LOGIN_CREDENTIAL_DATASETS_KEY);
+ }
+
+ @Override
+ public void saveCreditCardInfo(DatasetModel creditCardInfo) {
+ saveDatasetModel(creditCardInfo, CREDIT_CARD_INFO_DATASETS_KEY);
+ }
+
+ private void saveDatasetModel(DatasetModel datasetModel, String key) {
+ String datasetName = "dataset-" + getDatasetNumber();
+ datasetModel.setDatasetName(datasetName);
+ Set<String> datasetModelStringSet =
+ mPrefs.getStringSet(key, new ArraySet<String>());
+ datasetModelStringSet.add(datasetModel.toJson().toString());
+ mPrefs.edit().putStringSet(key, datasetModelStringSet).apply();
+ incrementDatasetNumber();
+ }
+
+ /**
+ * For simplicity, datasets will be named in the form "dataset-X" where X means
+ * this was the Xth dataset saved.
+ */
+ private int getDatasetNumber() {
+ return mPrefs.getInt(DATASET_NUMBER_KEY, 0);
+ }
+
+ /**
+ * Every time a dataset is saved, this should be called to increment the dataset number.
+ * (only important for this service's dataset naming scheme).
+ */
+ private void incrementDatasetNumber() {
+ mPrefs.edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber() + 1).apply();
}
} \ No newline at end of file
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 648eb797..af676a6c 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
@@ -30,7 +30,6 @@ public class AutofillField {
private String[] hints;
private AutofillId id;
- // For simplicity, we will only support text values.
// TODO support multiple value types.
private String value;
@@ -39,6 +38,8 @@ public class AutofillField {
public void setFrom(AssistStructure.ViewNode view) {
id = view.getAutofillId();
+ // TODO support multiple value types.
+ value = view.getText().toString();
setHints(view.getAutofillHints());
}
@@ -78,7 +79,7 @@ public class AutofillField {
private void updateSaveTypeFromHints() {
saveType = 0;
- if(hints == null) {
+ if (hints == null) {
return;
}
for (String hint : hints) {
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
new file mode 100644
index 00000000..d03dd717
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java
@@ -0,0 +1,58 @@
+/*
+ * 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.view.autofill.AutofillId;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public final class AutofillFieldsCollection {
+
+ private final List<AutofillId> mAutofillIds = new ArrayList<>();
+ private final HashMap<String, List<AutofillField>> mAutofillHintsToFieldsMap = new HashMap<>();
+ private int size = 0;
+ private int mSaveType = 0;
+
+ public void add(AutofillField autofillField) {
+ mSaveType |= autofillField.getSaveType();
+ size++;
+ mAutofillIds.add(autofillField.getId());
+ for (String hint : autofillField.getHints()) {
+ if (mAutofillHintsToFieldsMap.get(hint) == null) {
+ mAutofillHintsToFieldsMap.put(hint, new ArrayList<AutofillField>());
+ }
+ mAutofillHintsToFieldsMap.get(hint).add(autofillField);
+ }
+ }
+
+ public int getSaveType() {
+ return mSaveType;
+ }
+
+ public AutofillId[] getAutofillIds() {
+ return mAutofillIds.toArray(new AutofillId[size]);
+ }
+
+ public List<AutofillField> getFieldsForHint(String hint) {
+ return mAutofillHintsToFieldsMap.get(hint);
+ }
+
+ public boolean containsHint(String hint) {
+ return !getFieldsForHint(hint).isEmpty();
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/CreditCardInfo.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/CreditCardInfo.java
new file mode 100644
index 00000000..db7a616e
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/CreditCardInfo.java
@@ -0,0 +1,86 @@
+/*
+ * 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.service.autofill.Dataset;
+import android.view.View;
+import android.view.autofill.AutofillValue;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.List;
+
+/**
+ * Predefined model of data on an autofillable credit card info page.
+ */
+public class CreditCardInfo implements DatasetModel {
+ private String creditCardNumber;
+ private String datasetName;
+
+ public CreditCardInfo(String creditCardNumber) {
+ this(null, creditCardNumber);
+ }
+
+ public CreditCardInfo(String datasetName, String creditCardNumber) {
+ this.datasetName = datasetName;
+ this.creditCardNumber = creditCardNumber;
+ }
+
+ public static CreditCardInfo fromJson(JSONObject jsonObject) {
+ try {
+ String creditCardNumber = jsonObject.getString("creditCardNumber");
+ String datasetName = jsonObject.getString("datasetName");
+ return new CreditCardInfo(datasetName, creditCardNumber);
+ } catch (JSONException e) {
+ return null;
+ }
+ }
+
+ public JSONObject toJson() {
+ JSONObject jsonObject = new JSONObject();
+ try {
+ jsonObject.put("creditCardNumber", creditCardNumber);
+ jsonObject.put("datasetName", datasetName);
+ } catch (JSONException e) {
+ return null;
+ }
+ return jsonObject;
+ }
+
+ @Override
+ public String getDatasetName() {
+ return datasetName;
+ }
+
+ @Override
+ public void setDatasetName(String datasetName) {
+ this.datasetName = datasetName;
+ }
+
+ @Override
+ public void applyToFields(AutofillFieldsCollection autofillFieldsCollection,
+ Dataset.Builder datasetBuilder) {
+ List<AutofillField> creditCardNumberFields =
+ autofillFieldsCollection.getFieldsForHint(View.AUTOFILL_HINT_CREDIT_CARD_NUMBER);
+
+ for (int i = 0; i < creditCardNumberFields.size(); i++) {
+ AutofillField field = creditCardNumberFields.get(i);
+ datasetBuilder.setValue(field.getId(),
+ AutofillValue.forText(creditCardNumber));
+ }
+ }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/DatasetModel.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/DatasetModel.java
index 218e916b..1ece3c1c 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/DatasetModel.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/DatasetModel.java
@@ -15,11 +15,33 @@
*/
package com.example.android.autofillframework.service.model;
+import android.service.autofill.Dataset;
+import android.view.autofill.AutofillId;
+
+import org.json.JSONObject;
+
/**
- * Blueprint for Objects that are the underlying data model for Autofill Datasets. In this basic
- * sample, they are only required to have a Dataset name that displays in the list of Autofill
- * suggestions that the user picks from.
+ * Blueprint for Objects that are the underlying data model for Autofill Datasets.
+ * Implementations should contain values that are meant to populate autofillable fields.
*/
public interface DatasetModel {
+
+ /**
+ * Returns the name of the {@link Dataset}.
+ */
String getDatasetName();
+
+ /**
+ * Sets the {@link Dataset} name.
+ */
+ void setDatasetName(String datasetName);
+
+ /**
+ * Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId}
+ * in a {@code AutofillFieldsCollection}.
+ */
+ void applyToFields(AutofillFieldsCollection autofillFieldsCollection,
+ Dataset.Builder datasetBuilder);
+
+ JSONObject toJson();
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/LoginCredential.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/LoginCredential.java
index 742c7582..83057157 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/LoginCredential.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/service/model/LoginCredential.java
@@ -16,49 +16,71 @@
package com.example.android.autofillframework.service.model;
+import android.service.autofill.Dataset;
+import android.view.View;
+import android.view.autofill.AutofillValue;
+
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.List;
+
+/**
+ * Predefined model of data on an autofillable login page.
+ */
public class LoginCredential implements DatasetModel {
private final String username;
private final String password;
private String datasetName;
- LoginCredential(String username, String password) {
+ public LoginCredential(String username, String password) {
this(username, password, null);
}
- LoginCredential(String username, String password, String datasetName) {
+ public LoginCredential(String username, String password, String datasetName) {
this.username = username;
this.password = password;
+ this.datasetName = datasetName;
}
public static LoginCredential fromJson(JSONObject jsonObject) {
- LoginCredential loginCredential = null;
try {
- loginCredential = new LoginCredential(jsonObject.getString("username"),
- jsonObject.getString("password"));
+ return new LoginCredential(jsonObject.getString("username"),
+ jsonObject.getString("password"),
+ jsonObject.getString("datasetName"));
} catch (JSONException e) {
return null;
}
- return loginCredential;
}
@Override
public String getDatasetName() {
- return username;
+ return datasetName;
}
public void setDatasetName(String datasetName) {
this.datasetName = datasetName;
}
- public String getUsername() {
- return username;
- }
+ @Override
+ public void applyToFields(AutofillFieldsCollection autofillFieldsCollection,
+ Dataset.Builder datasetBuilder) {
+ List<AutofillField> usernameFields =
+ autofillFieldsCollection.getFieldsForHint(View.AUTOFILL_HINT_USERNAME);
+ List<AutofillField> passwordFields =
+ autofillFieldsCollection.getFieldsForHint(View.AUTOFILL_HINT_PASSWORD);
+
+ for (int i = 0; i < usernameFields.size(); i++) {
+ AutofillField field = usernameFields.get(i);
+ datasetBuilder.setValue(field.getId(),
+ AutofillValue.forText(username));
+ }
- public String getPassword() {
- return password;
+ for (int i = 0; i < passwordFields.size(); i++) {
+ AutofillField field = passwordFields.get(i);
+ datasetBuilder.setValue(field.getId(),
+ AutofillValue.forText(password));
+ }
}
public JSONObject toJson() {
@@ -66,6 +88,7 @@ public class LoginCredential implements DatasetModel {
try {
jsonObject.put("username", username);
jsonObject.put("password", password);
+ jsonObject.put("datasetName", datasetName);
} catch (JSONException e) {
return null;
}
@@ -81,7 +104,8 @@ public class LoginCredential implements DatasetModel {
if (!username.equals(that.username)) return false;
if (!password.equals(that.password)) return false;
- return datasetName != null ? datasetName.equals(that.datasetName) : that.datasetName == null;
+ return datasetName != null ?
+ datasetName.equals(that.datasetName) : that.datasetName == null;
}
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml b/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml
index abd1aa33..08a7b557 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/layout/login_activity.xml
@@ -37,7 +37,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
- android:text="@string/username_label" />
+ android:text="@string/username_label"
+ android:importantForAutofill="no"/>
<EditText
android:id="@+id/usernameField"
@@ -45,7 +46,8 @@
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:layout_alignBaseline="@+id/usernameLabel"
- android:layout_toEndOf="@id/usernameLabel"/>
+ android:layout_toEndOf="@id/usernameLabel"
+ android:autofillHints="username"/>
<TextView
android:id="@+id/passwordLabel"
@@ -54,7 +56,8 @@
android:text="@string/password_label"
android:layout_below="@+id/usernameLabel"
android:layout_alignStart="@+id/usernameLabel"
- android:layout_marginTop="20dp"/>
+ android:layout_marginTop="20dp"
+ android:importantForAutofill="no"/>
<EditText
android:id="@+id/passwordField"
@@ -62,7 +65,8 @@
android:layout_height="wrap_content"
android:inputType="textPassword"
android:layout_alignBaseline="@+id/passwordLabel"
- android:layout_alignStart="@+id/usernameField"/>
+ android:layout_alignStart="@+id/usernameField"
+ android:autofillHints="password"/>
</RelativeLayout>
<LinearLayout