diff options
author | Douglas Sigelbaum <sigelbaum@google.com> | 2017-05-23 12:08:20 -0700 |
---|---|---|
committer | Douglas Sigelbaum <sigelbaum@google.com> | 2017-05-30 18:01:15 -0700 |
commit | 458ac0f88ad2d524fe5644af3855eee6fd27e092 (patch) | |
tree | b63c0cd360587224b1ebae3b443f45fcba7698ce /input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example | |
parent | 16b2ad6e79136b52236644d438fb954cd6ad08fa (diff) | |
download | android-458ac0f88ad2d524fe5644af3855eee6fd27e092.tar.gz |
Converted Autofill sample service to kotlin.
Also modified some kotlin in Autofill client activities.
Bug: 38182790
Test: manual
Change-Id: I09938ae3edf83c7a26855c376a96f69a67f542c1
Diffstat (limited to 'input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example')
28 files changed, 1088 insertions, 1459 deletions
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java deleted file mode 100644 index 54049902..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java +++ /dev/null @@ -1,54 +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; - -import android.os.Bundle; - -import java.util.Arrays; -import java.util.Set; - -public final class CommonUtil { - - public static final String TAG = "AutofillSample"; - - public static final String EXTRA_DATASET_NAME = "dataset_name"; - public static final String EXTRA_FOR_RESPONSE = "for_response"; - - private static void bundleToString(StringBuilder builder, Bundle data) { - final Set<String> keySet = data.keySet(); - builder.append("[Bundle with ").append(keySet.size()).append(" keys:"); - for (String key : keySet) { - builder.append(' ').append(key).append('='); - Object value = data.get(key); - if ((value instanceof Bundle)) { - bundleToString(builder, (Bundle) value); - } else { - builder.append((value instanceof Object[]) - ? Arrays.toString((Object[]) value) : value); - } - } - builder.append(']'); - } - - public static String bundleToString(Bundle data) { - if (data == null) { - return "N/A"; - } - final StringBuilder builder = new StringBuilder(); - bundleToString(builder, data); - return builder.toString(); - } -}
\ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.kt new file mode 100644 index 00000000..e2839e92 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.kt @@ -0,0 +1,52 @@ +/* + * 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 + +import android.os.Bundle +import java.util.Arrays + +object CommonUtil { + + val TAG = "AutofillSample" + + val EXTRA_DATASET_NAME = "dataset_name" + val EXTRA_FOR_RESPONSE = "for_response" + + private fun bundleToString(builder: StringBuilder, data: Bundle) { + val keySet = data.keySet() + builder.append("[Bundle with ").append(keySet.size).append(" keys:") + for (key in keySet) { + builder.append(' ').append(key).append('=') + val value = data.get(key) + if (value is Bundle) { + bundleToString(builder, value) + } else { + val string = if (value is Array<*>) Arrays.toString(value) else value + builder.append(string) + } + } + builder.append(']') + } + + fun bundleToString(data: Bundle?): String { + if (data == null) { + return "N/A" + } + val builder = StringBuilder() + bundleToString(builder, data) + return builder.toString() + } +}
\ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.kt index 46fdb72a..9c4121aa 100644 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.kt +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.kt @@ -27,7 +27,7 @@ class CreditCardActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - + setContentView(R.layout.credit_card_activity) // Create an ArrayAdapter using the string array and a default spinner layout val dayAdapter = ArrayAdapter.createFromResource(this, R.array.day_array, android.R.layout.simple_spinner_item) @@ -57,13 +57,12 @@ class CreditCardActivity : AppCompatActivity() { * any new data. */ private fun submit() { - val intent = WelcomeActivity.getStartActivityIntent(this@CreditCardActivity) + val intent = WelcomeActivity.getStartActivityIntent(this) startActivity(intent) finish() } companion object { - fun getStartActivityIntent(context: Context): Intent { val intent = Intent(context, CreditCardActivity::class.java) return intent 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 5f66364c..193008b5 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 @@ -72,21 +72,19 @@ 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. - Log.d(TAG, "autoFill(): " + values) + Log.d(TAG, "autofill(): " + values) for (i in 0..values.size() - 1) { val id = values.keyAt(i) val value = values.valueAt(i) - val item = mItems.get(id) - if (item == null) { - Log.w(TAG, "No item for id " + id) - return - } - if (!item.editable) { - Log.w(TAG, "Item for id $id is not editable: $item") - return + + mItems[id]?.let { + if (!it.editable) { + Log.w(TAG, "Item for autofillId $id is not editable: $it") + return@autofill + } + // Set the item's text to the text wrapped in the AutofillValue. + it.text = value.textValue } - // Set the item's text to the text wrapped in the AutofillValue. - item.text = value.textValue } postInvalidate() } @@ -121,9 +119,8 @@ class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, a Log.d(TAG, "onDraw: " + mLines.size + " lines; canvas:" + canvas) var x: Float var y = (mTopMargin + mLineLength).toFloat() - for (i in mLines.indices) { + for (line in mLines) { x = mLeftMargin.toFloat() - val line = mLines[i] Log.v(TAG, "Drawing '" + line + "' at " + x + "x" + y) mTextPaint.color = if (line.fieldTextItem.focused) mFocusedColor else mUnfocusedColor val readOnlyText = line.labelItem.text.toString() + ": [" @@ -184,7 +181,9 @@ class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, a return line } - private class Item internal constructor(val line: Line, val id: Int, val hints: Array<String>?, val type: Int, var text: CharSequence, val editable: Boolean, val sanitized: Boolean) { + private class Item internal constructor(val line: Line, val id: Int, val hints: Array<String>?, + val type: Int, var text: CharSequence, + val editable: Boolean, val sanitized: Boolean) { var focused = false override fun toString(): String { @@ -198,7 +197,8 @@ class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, a get() = if (editable) EditText::class.java.name else TextView::class.java.name } - private inner class Line constructor(val idEntry: String, label: String, hints: Array<String>, text: String, sanitized: Boolean) { + private inner class Line constructor(val idEntry: String, label: String, hints: Array<String>, + text: String, sanitized: Boolean) { // Boundaries of the text field, relative to the CustomView internal val bounds = Rect() diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt index ca146019..bf3fe874 100644 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt @@ -24,8 +24,8 @@ import android.widget.Toast import com.example.android.autofillframework.R import kotlinx.android.synthetic.main.login_activity.* + class LoginActivity : AppCompatActivity() { - val TAG = "LoginActivity" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java deleted file mode 100644 index 768b2ee3..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java +++ /dev/null @@ -1,160 +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; - -import android.app.Activity; -import android.app.PendingIntent; -import android.app.assist.AssistStructure; -import android.content.Context; -import android.content.Intent; -import android.content.IntentSender; -import android.os.Bundle; -import android.service.autofill.Dataset; -import android.service.autofill.FillResponse; -import android.support.annotation.Nullable; -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.model.AutofillFieldsCollection; -import com.example.android.autofillframework.service.model.ClientFormData; -import com.example.android.autofillframework.service.settings.MyPreferences; - -import java.util.HashMap; - -import static android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE; -import static android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT; -import static com.example.android.autofillframework.CommonUtil.EXTRA_DATASET_NAME; -import static com.example.android.autofillframework.CommonUtil.EXTRA_FOR_RESPONSE; -import static com.example.android.autofillframework.CommonUtil.TAG; - -/** - * This Activity controls the UI for logging in to the Autofill service. - * It is launched when an Autofill Response or specific Dataset within the Response requires - * authentication to access. It bundles the result in an Intent. - */ -public class AuthActivity extends Activity { - - // Unique id for dataset intents. - private static int sDatasetPendingIntentId = 0; - - private EditText mMasterPassword; - private Button mCancel; - private Button mLogin; - private Intent mReplyIntent; - - static IntentSender getAuthIntentSenderForResponse(Context context) { - final Intent intent = new Intent(context, AuthActivity.class); - return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) - .getIntentSender(); - } - - static IntentSender getAuthIntentSenderForDataset(Context context, String datasetName) { - final Intent intent = new Intent(context, AuthActivity.class); - intent.putExtra(EXTRA_DATASET_NAME, datasetName); - intent.putExtra(EXTRA_FOR_RESPONSE, false); - return PendingIntent.getActivity(context, ++sDatasetPendingIntentId, intent, - PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); - } - - @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() { - @Override - public void onClick(View v) { - login(); - } - - }); - - mCancel.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - onFailure(); - AuthActivity.this.finish(); - } - }); - } - - private void login() { - Editable password = mMasterPassword.getText(); - if (password.toString() - .equals(MyPreferences.getInstance(AuthActivity.this).getMasterPassword())) { - onSuccess(); - } else { - Toast.makeText(this, "Password incorrect", Toast.LENGTH_SHORT).show(); - onFailure(); - } - finish(); - } - - @Override - public void finish() { - if (mReplyIntent != null) { - setResult(RESULT_OK, mReplyIntent); - } else { - setResult(RESULT_CANCELED); - } - super.finish(); - } - - private void onFailure() { - Log.w(TAG, "Failed auth."); - mReplyIntent = null; - } - - private void onSuccess() { - Intent intent = getIntent(); - boolean forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true); - AssistStructure structure = intent.getParcelableExtra(EXTRA_ASSIST_STRUCTURE); - StructureParser parser = new StructureParser(structure); - parser.parse(); - AutofillFieldsCollection autofillFields = parser.getAutofillFields(); - int saveTypes = parser.getSaveTypes(); - mReplyIntent = new Intent(); - HashMap<String, ClientFormData> clientFormDataMap = - LocalAutofillRepository.getInstance(this).getClientFormData - (autofillFields.getFocusedHints(), autofillFields.getAllHints()); - if (forResponse) { - setResponseIntent(AutofillHelper.newResponse - (this, false, autofillFields, saveTypes, clientFormDataMap)); - } else { - String datasetName = intent.getStringExtra(EXTRA_DATASET_NAME); - setDatasetIntent(AutofillHelper.newDataset - (this, autofillFields, clientFormDataMap.get(datasetName))); - } - } - - 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/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 new file mode 100644 index 00000000..b8c3f8b1 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.kt @@ -0,0 +1,134 @@ +/* + * 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 + +import android.app.Activity +import android.app.PendingIntent +import android.app.assist.AssistStructure +import android.content.Context +import android.content.Intent +import android.content.IntentSender +import android.os.Bundle +import android.service.autofill.Dataset +import android.service.autofill.FillResponse +import android.util.Log +import android.view.autofill.AutofillManager.EXTRA_ASSIST_STRUCTURE +import android.view.autofill.AutofillManager.EXTRA_AUTHENTICATION_RESULT +import android.widget.Toast +import com.example.android.autofillframework.CommonUtil.EXTRA_DATASET_NAME +import com.example.android.autofillframework.CommonUtil.EXTRA_FOR_RESPONSE +import com.example.android.autofillframework.CommonUtil.TAG +import com.example.android.autofillframework.R +import com.example.android.autofillframework.service.datasource.SharedPrefsAutofillRepository +import com.example.android.autofillframework.service.settings.MyPreferences +import kotlinx.android.synthetic.main.auth_activity.cancel +import kotlinx.android.synthetic.main.auth_activity.login +import kotlinx.android.synthetic.main.auth_activity.master_password + +/** + * This Activity controls the UI for logging in to the Autofill service. + * It is launched when an Autofill Response or specific Dataset within the Response requires + * authentication to access. It bundles the result in an Intent. + */ +class AuthActivity : Activity() { + + private var mReplyIntent: Intent? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.auth_activity) + login.setOnClickListener { login() } + cancel.setOnClickListener { + onFailure() + this@AuthActivity.finish() + } + } + + private fun login() { + val password = master_password.text + if (password.toString() == MyPreferences.getMasterPassword(this@AuthActivity)) { + onSuccess() + } else { + Toast.makeText(this, "Password incorrect", Toast.LENGTH_SHORT).show() + onFailure() + } + finish() + } + + override fun finish() { + if (mReplyIntent != null) { + setResult(Activity.RESULT_OK, mReplyIntent) + } else { + setResult(Activity.RESULT_CANCELED) + } + super.finish() + } + + private fun onFailure() { + Log.w(TAG, "Failed auth.") + mReplyIntent = null + } + + private fun onSuccess() { + val intent = intent + val forResponse = intent.getBooleanExtra(EXTRA_FOR_RESPONSE, true) + val structure = intent.getParcelableExtra<AssistStructure>(EXTRA_ASSIST_STRUCTURE) + val parser = StructureParser(structure) + parser.parseForFill() + val autofillFields = parser.autofillFields + mReplyIntent = Intent() + val clientFormDataMap = SharedPrefsAutofillRepository + .getClientFormData(this, autofillFields.focusedAutofillHints, autofillFields.allAutofillHints) + if (forResponse) { + AutofillHelper.newResponse(this, false, autofillFields, clientFormDataMap)?.let(this::setResponseIntent) + } else { + val datasetName = intent.getStringExtra(EXTRA_DATASET_NAME) + clientFormDataMap?.let { + it[datasetName]?.let { + AutofillHelper.newDataset(this, autofillFields, it)?.let(this::setDatasetIntent) + } + } + } + } + + private fun setResponseIntent(fillResponse: FillResponse) { + mReplyIntent?.putExtra(EXTRA_AUTHENTICATION_RESULT, fillResponse) + } + + private fun setDatasetIntent(dataset: Dataset) { + mReplyIntent?.putExtra(EXTRA_AUTHENTICATION_RESULT, dataset) + } + + companion object { + + // Unique autofillId for dataset intents. + private var sDatasetPendingIntentId = 0 + + internal fun getAuthIntentSenderForResponse(context: Context): IntentSender { + val intent = Intent(context, AuthActivity::class.java) + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT) + .intentSender + } + + internal fun getAuthIntentSenderForDataset(context: Context, datasetName: String): IntentSender { + 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, + PendingIntent.FLAG_CANCEL_CURRENT).intentSender + } + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java deleted file mode 100644 index 460729e6..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java +++ /dev/null @@ -1,100 +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; - -import android.content.Context; -import android.content.IntentSender; -import android.service.autofill.Dataset; -import android.service.autofill.FillResponse; -import android.service.autofill.SaveInfo; -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.model.AutofillFieldsCollection; -import com.example.android.autofillframework.service.model.ClientFormData; - -import java.util.HashMap; -import java.util.Set; - -import static com.example.android.autofillframework.CommonUtil.TAG; - -/** - * This is a class containing helper methods for building Autofill Datasets and Responses. - */ -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 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; - } - } - - public static RemoteViews newRemoteViews(String packageName, String remoteViewsText) { - RemoteViews presentation = new RemoteViews(packageName, R.layout.list_item); - presentation.setTextViewText(R.id.text1, remoteViewsText); - return presentation; - } - - /** - * 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 newResponse(Context context, - boolean datasetAuth, AutofillFieldsCollection autofillFields, int saveType, - 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 (dataset != null) { - responseBuilder.addDataset(dataset); - } - } - } - } - if (saveType != 0) { - AutofillId[] autofillIds = autofillFields.getAutofillIds(); - responseBuilder.setSaveInfo(new SaveInfo.Builder(saveType, autofillIds).build()); - return responseBuilder.build(); - } else { - Log.d(TAG, "These fields are not meant to be saved by autofill."); - return null; - } - } -} 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 new file mode 100644 index 00000000..b7470d7b --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.kt @@ -0,0 +1,93 @@ +/* + * 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 + +import android.content.Context +import android.service.autofill.Dataset +import android.service.autofill.FillResponse +import android.service.autofill.SaveInfo +import android.util.Log +import android.widget.RemoteViews +import com.example.android.autofillframework.CommonUtil.TAG +import com.example.android.autofillframework.R +import com.example.android.autofillframework.service.model.AutofillFieldsCollection +import com.example.android.autofillframework.service.model.ClientFormData +import java.util.HashMap + +/** + * This is a class containing helper methods for building Autofill Datasets and Responses. + */ +object AutofillHelper { + + /** + * Wraps autofill data in a [Dataset] object which can then be sent back to the + * client View. + */ + fun newDataset(context: Context, autofillFields: AutofillFieldsCollection, + clientFormData: ClientFormData): Dataset? { + clientFormData.datasetName?.let { datasetName -> + val datasetBuilder = Dataset.Builder(newRemoteViews(context.packageName, datasetName)) + val setValueAtLeastOnce = clientFormData.applyToFields(autofillFields, datasetBuilder) + if (setValueAtLeastOnce) { + return datasetBuilder.build() + } + } + return null + } + + fun newRemoteViews(packageName: String, remoteViewsText: String): RemoteViews { + val presentation = RemoteViews(packageName, R.layout.list_item) + presentation.setTextViewText(R.id.text1, remoteViewsText) + return presentation + } + + /** + * Wraps autofill data in a [FillResponse] object (essentially a series of Datasets) which can + * then be sent back to the client View. + */ + fun newResponse(context: Context, + datasetAuth: Boolean, autofillFields: AutofillFieldsCollection, + clientFormDataMap: HashMap<String, ClientFormData>?): FillResponse? { + val responseBuilder = FillResponse.Builder() + 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) + } + } + } + } + if (autofillFields.saveType != 0) { + val autofillIds = autofillFields.autofillIds + responseBuilder.setSaveInfo(SaveInfo.Builder(autofillFields.saveType, + autofillIds.toTypedArray()).build()) + return responseBuilder.build() + } else { + Log.d(TAG, "These fields are not meant to be saved by autofill.") + return null + } + } +}
\ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java deleted file mode 100644 index 61e42050..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java +++ /dev/null @@ -1,128 +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; - -import android.app.assist.AssistStructure; -import android.content.IntentSender; -import android.os.Bundle; -import android.os.CancellationSignal; -import android.service.autofill.AutofillService; -import android.service.autofill.FillCallback; -import android.service.autofill.FillContext; -import android.service.autofill.FillRequest; -import android.service.autofill.FillResponse; -import android.service.autofill.SaveCallback; -import android.service.autofill.SaveRequest; -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.model.AutofillFieldsCollection; -import com.example.android.autofillframework.service.model.ClientFormData; -import com.example.android.autofillframework.service.settings.MyPreferences; - -import java.util.HashMap; -import java.util.List; - -import static com.example.android.autofillframework.CommonUtil.TAG; -import static com.example.android.autofillframework.CommonUtil.bundleToString; - -public class MyAutofillService extends AutofillService { - - @Override - public void onFillRequest(AssistStructure assistStructure, Bundle bundle, int i, - CancellationSignal cancellationSignal, FillCallback fillCallback) { - /* Deprecated, ignore */ - } - - @Override - public void onSaveRequest(AssistStructure assistStructure, Bundle bundle, - SaveCallback saveCallback) { - /* Deprecated, ignore */ - } - - @Override - public void onFillRequest(FillRequest request, CancellationSignal cancellationSignal, - FillCallback callback) { - AssistStructure structure = request.getStructure(); - final Bundle data = request.getClientState(); - Log.d(TAG, "onFillRequest(): data=" + bundleToString(data)); - - // Temporary hack for disabling autofill for components in this autofill service. - // i.e. we don't want to autofill components in AuthActivity. - if (structure.getActivityComponent().toShortString() - .contains("com.example.android.autofillframework.service")) { - callback.onSuccess(null); - return; - } - cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() { - @Override - public void onCancel() { - Log.w(TAG, "Cancel autofill not implemented in this sample."); - } - }); - // Parse AutoFill data in Activity - StructureParser parser = new StructureParser(structure); - parser.parse(); - 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(); - if (responseAuth) { - // If the entire Autofill Response is authenticated, AuthActivity is used - // to generate Response. - IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this); - RemoteViews presentation = AutofillHelper - .newRemoteViews(getPackageName(), getString(R.string.autofill_sign_in_prompt)); - responseBuilder - .setAuthentication(autofillFields.getAutofillIds(), sender, presentation); - callback.onSuccess(responseBuilder.build()); - } else { - boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth(); - HashMap<String, ClientFormData> clientFormDataMap = - LocalAutofillRepository.getInstance(this).getClientFormData - (autofillFields.getFocusedHints(), autofillFields.getAllHints()); - FillResponse response = AutofillHelper.newResponse - (this, datasetAuth, autofillFields, saveTypes, clientFormDataMap); - callback.onSuccess(response); - } - } - - @Override - public void onSaveRequest(SaveRequest request, SaveCallback callback) { - List<FillContext> context = request.getFillContexts(); - final AssistStructure structure = context.get(context.size() - 1).getStructure(); - final Bundle data = request.getClientState(); - Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data)); - StructureParser parser = new StructureParser(structure); - parser.parse(); - ClientFormData clientFormData = parser.getClientFormData(); - LocalAutofillRepository.getInstance(this).saveClientFormData(clientFormData); - } - - @Override - public void onConnected() { - Log.d(TAG, "onConnected"); - } - - @Override - public void onDisconnected() { - Log.d(TAG, "onDisconnected"); - } -} 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 new file mode 100644 index 00000000..9bc2a8c2 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.kt @@ -0,0 +1,102 @@ +/* + * 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 + +import android.app.assist.AssistStructure +import android.os.Bundle +import android.os.CancellationSignal +import android.service.autofill.AutofillService +import android.service.autofill.FillCallback +import android.service.autofill.FillRequest +import android.service.autofill.FillResponse +import android.service.autofill.SaveCallback +import android.service.autofill.SaveRequest +import android.util.Log +import com.example.android.autofillframework.CommonUtil.TAG +import com.example.android.autofillframework.CommonUtil.bundleToString +import com.example.android.autofillframework.R +import com.example.android.autofillframework.service.datasource.SharedPrefsAutofillRepository +import com.example.android.autofillframework.service.settings.MyPreferences + +class MyAutofillService : AutofillService() { + + override fun onFillRequest(assistStructure: AssistStructure, bundle: Bundle, i: Int, + cancellationSignal: CancellationSignal, fillCallback: FillCallback) { + /* Deprecated, ignore */ + } + + override fun onSaveRequest(assistStructure: AssistStructure, bundle: Bundle, + saveCallback: SaveCallback) { + /* Deprecated, ignore */ + } + + override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal, + callback: FillCallback) { + val structure = request.structure + val data = request.clientState + Log.d(TAG, "onFillRequest(): data=" + bundleToString(data)) + + // Temporary hack for disabling autofill for components in this autofill service. + // i.e. we don't want to autofill components in AuthActivity. + if (structure.activityComponent.toShortString() + .contains("com.example.android.autofillframework.service")) { + callback.onSuccess(null) + return + } + cancellationSignal.setOnCancelListener { Log.w(TAG, "Cancel autofill not implemented in this sample.") } + // Parse AutoFill data in Activity + val parser = StructureParser(structure) + parser.parseForFill() + val autofillFields = parser.autofillFields + + val responseBuilder = FillResponse.Builder() + // Check user's settings for authenticating Responses and Datasets. + val responseAuth = MyPreferences.isResponseAuth(this) + if (responseAuth) { + // If the entire Autofill Response is authenticated, AuthActivity is used + // to generate Response. + val sender = AuthActivity.getAuthIntentSenderForResponse(this) + val presentation = AutofillHelper + .newRemoteViews(packageName, getString(R.string.autofill_sign_in_prompt)) + responseBuilder + .setAuthentication(autofillFields.autofillIds.toTypedArray(), sender, presentation) + callback.onSuccess(responseBuilder.build()) + } else { + val datasetAuth = MyPreferences.isDatasetAuth(this) + val clientFormDataMap = SharedPrefsAutofillRepository.getClientFormData(this, autofillFields.focusedAutofillHints, autofillFields.allAutofillHints) + val response = AutofillHelper.newResponse(this, datasetAuth, autofillFields, clientFormDataMap) + callback.onSuccess(response) + } + } + + override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { + val context = request.fillContexts + val structure = context[context.size - 1].structure + val data = request.clientState + Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data)) + val parser = StructureParser(structure) + parser.parseForSave() + SharedPrefsAutofillRepository.saveClientFormData(this, parser.clientFormData) + } + + override fun onConnected() { + Log.d(TAG, "onConnected") + } + + override fun onDisconnected() { + Log.d(TAG, "onDisconnected") + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java deleted file mode 100644 index b6294449..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java +++ /dev/null @@ -1,85 +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; - -import android.app.assist.AssistStructure; -import android.app.assist.AssistStructure.ViewNode; -import android.app.assist.AssistStructure.WindowNode; -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 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 - */ -final class StructureParser { - private final AutofillFieldsCollection mAutofillFields = new AutofillFieldsCollection(); - private final AssistStructure mStructure; - private ClientFormData mClientFormData; - - StructureParser(AssistStructure structure) { - mStructure = structure; - - } - - /** - * Traverse AssistStructure and add ViewNode metadata to a flat list. - */ - void parse() { - 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); - } - } - - private void parseLocked(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)); - } - int childrenSize = viewNode.getChildCount(); - if (childrenSize > 0) { - for (int i = 0; i < childrenSize; i++) { - parseLocked(viewNode.getChildAt(i)); - } - } - } - - public AutofillFieldsCollection getAutofillFields() { - return mAutofillFields; - } - - public int getSaveTypes() { - return mAutofillFields.getSaveType(); - } - - public ClientFormData getClientFormData() { - return mClientFormData; - } -} 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 new file mode 100644 index 00000000..e05a2d51 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.kt @@ -0,0 +1,78 @@ +/* + * 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 + +import android.app.assist.AssistStructure +import android.app.assist.AssistStructure.ViewNode +import android.util.Log +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 + +/** + * 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 + */ +internal class StructureParser(private val mStructure: AssistStructure) { + val autofillFields = AutofillFieldsCollection() + var clientFormData: ClientFormData = ClientFormData() + private set + + + fun parseForFill() { + parse(true) + } + + fun parseForSave() { + parse(false) + } + + /** + * Traverse AssistStructure and add ViewNode metadata to a flat list. + */ + private fun parse(forFill: Boolean) { + Log.d(TAG, "Parsing structure for " + mStructure.activityComponent) + val nodes = mStructure.windowNodeCount + clientFormData = ClientFormData() + for (i in 0..nodes - 1) { + val node = mStructure.getWindowNodeAt(i) + val view = node.rootViewNode + parseLocked(forFill, view) + } + } + + private fun parseLocked(forFill: Boolean, viewNode: ViewNode) { + viewNode.autofillHints?.let { autofillHints -> + if (autofillHints.isNotEmpty()) { + if (forFill) { + autofillFields.add(AutofillField(viewNode)) + } else { + clientFormData.setAutofillValuesForHints(viewNode.autofillHints, + MutableAutofillValue(viewNode)) + } + } + } + val childrenSize = viewNode.childCount + if (childrenSize > 0) { + for (i in 0..childrenSize - 1) { + parseLocked(forFill, viewNode.getChildAt(i)) + } + } + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.kt index 8de8b647..f335187e 100644 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.kt @@ -13,29 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.example.android.autofillframework.service.datasource; +package com.example.android.autofillframework.service.datasource -import com.example.android.autofillframework.service.model.ClientFormData; +import android.content.Context +import com.example.android.autofillframework.service.model.ClientFormData +import java.util.HashMap -import java.util.HashMap; -import java.util.List; - -public interface AutofillRepository { +interface AutofillRepository { /** * Gets saved ClientFormData that contains some objects that can autofill fields with these - * {@code autofillHints}. + * `autofillHints`. */ - HashMap<String, ClientFormData> getClientFormData(List<String> focusedAutofillHints, - List<String> allAutofillHints); + fun getClientFormData(context: Context, focusedAutofillHints: List<String>, + allAutofillHints: List<String>): HashMap<String, ClientFormData>? /** * Saves LoginCredential under this datasetName. */ - void saveClientFormData(ClientFormData clientFormData); + fun saveClientFormData(context: Context, clientFormData: ClientFormData) /** * Clears all data. */ - void clear(); + fun clear(context: Context) } diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java deleted file mode 100644 index 8336fe1e..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java +++ /dev/null @@ -1,127 +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.datasource; - -import android.content.Context; -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 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. - */ -public class LocalAutofillRepository 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 final SharedPreferences mPrefs; - - // TODO prepend with autofill data set in Settings. - private LocalAutofillRepository(Context context) { - mPrefs = context.getApplicationContext() - .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE); - } - - public static LocalAutofillRepository getInstance(Context context) { - if (sInstance == null) { - sInstance = new LocalAutofillRepository(context); - } - return sInstance; - } - - @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); - } - } - } - if (hasDataForFocusedAutofillHints) { - return clientFormDataMap; - } else { - return null; - } - } catch (JSONException e) { - 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()); - saveAllAutofillDataStringSet(allAutofillData); - incrementDatasetNumber(); - } - - @Override - public void clear() { - mPrefs.edit().remove(CLIENT_FORM_DATA_KEY).apply(); - } - - private Set<String> getAllAutofillDataStringSet() { - return mPrefs.getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet<String>()); - } - - private void saveAllAutofillDataStringSet(Set<String> allAutofillDataStringSet) { - mPrefs.edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply(); - } - - /** - * 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/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 new file mode 100644 index 00000000..a7eadc96 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/SharedPrefsAutofillRepository.kt @@ -0,0 +1,101 @@ +/* + * 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.datasource + +import android.content.Context +import android.content.SharedPreferences +import android.util.ArraySet +import com.example.android.autofillframework.service.model.ClientFormData +import com.google.gson.Gson +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. + */ +object SharedPrefsAutofillRepository : AutofillRepository { + private val SHARED_PREF_KEY = "com.example.android.autofillframework.service" + private val CLIENT_FORM_DATA_KEY = "loginCredentialDatasets" + private val DATASET_NUMBER_KEY = "datasetNumber" + + private fun getPrefs(context: Context): SharedPreferences { + return context.applicationContext.getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE) + } + + override fun getClientFormData(context: Context, focusedAutofillHints: List<String>, + allAutofillHints: List<String>): HashMap<String, ClientFormData>? { + var hasDataForFocusedAutofillHints = false + val clientFormDataMap = HashMap<String, ClientFormData>() + val clientFormDataStringSet = getAllAutofillDataStringSet(context) + for (clientFormDataString in clientFormDataStringSet) { + val type = object : TypeToken<ClientFormData>() {}.type + Gson().fromJson<ClientFormData>(clientFormDataString, type)?.let { clientFormData -> + if (clientFormData.helpsWithHints(focusedAutofillHints)) { + hasDataForFocusedAutofillHints = true + clientFormData.datasetName?.let { datasetName -> + if (clientFormData.helpsWithHints(allAutofillHints)) { + clientFormDataMap.put(datasetName, clientFormData) + } + } + } + } + } + if (hasDataForFocusedAutofillHints) { + return clientFormDataMap + } else { + return null + } + } + + override fun saveClientFormData(context: Context, clientFormData: ClientFormData) { + val datasetName = "dataset-" + getDatasetNumber(context) + clientFormData.datasetName = datasetName + val allAutofillData = getAllAutofillDataStringSet(context) + allAutofillData.add(Gson().toJson(clientFormData).toString()) + saveAllAutofillDataStringSet(context, allAutofillData) + incrementDatasetNumber(context) + } + + override fun clear(context: Context) { + getPrefs(context).edit().remove(CLIENT_FORM_DATA_KEY).remove(DATASET_NUMBER_KEY).apply() + } + + private fun getAllAutofillDataStringSet(context: Context): MutableSet<String> { + return getPrefs(context).getStringSet(CLIENT_FORM_DATA_KEY, ArraySet<String>()) + } + + private fun saveAllAutofillDataStringSet(context: Context, allAutofillDataStringSet: Set<String>) { + getPrefs(context).edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply() + } + + /** + * For simplicity, datasets will be named in the form "dataset-X" where X means + * this was the Xth dataset saved. + */ + private fun getDatasetNumber(context: Context): Int { + return getPrefs(context).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 fun incrementDatasetNumber(context: Context) { + getPrefs(context).edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber(context) + 1).apply() + } +}
\ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java deleted file mode 100644 index 77f0a182..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java +++ /dev/null @@ -1,119 +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.service.autofill.SaveInfo; -import android.view.View; -import android.view.autofill.AutofillId; - -/** - * 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). - */ -public class AutofillField { - private int mSaveType = 0; - private String[] mHints; - private AutofillId mId; - private int mAutofillType; - private String[] mAutofillOptions; - private boolean mFocused; - - public AutofillField(AssistStructure.ViewNode view) { - mId = view.getAutofillId(); - setHints(view.getAutofillHints()); - mAutofillType = view.getAutofillType(); - mAutofillOptions = view.getAutofillOptions(); - mFocused = view.isFocused(); - } - - public String[] getHints() { - return mHints; - } - - public void setHints(String[] hints) { - mHints = hints; - updateSaveTypeFromHints(); - } - - public int getSaveType() { - return mSaveType; - } - - public AutofillId getId() { - return mId; - } - - public void setId(AutofillId id) { - mId = id; - } - - public int getAutofillType() { - return mAutofillType; - } - - public int getAutofillOptionIndex(String value) { - for (int i = 0; i < mAutofillOptions.length; i++) { - if (mAutofillOptions[i].equals(value)) { - return i; - } - } - return -1; - } - - public boolean isFocused() { - return mFocused; - } - - private void updateSaveTypeFromHints() { - mSaveType = 0; - if (mHints == null) { - return; - } - for (String hint : mHints) { - switch (hint) { - case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE: - case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY: - case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH: - case View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR: - case View.AUTOFILL_HINT_CREDIT_CARD_NUMBER: - case View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE: - mSaveType |= SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD; - break; - case View.AUTOFILL_HINT_EMAIL_ADDRESS: - mSaveType |= SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS; - break; - case View.AUTOFILL_HINT_PHONE: - case View.AUTOFILL_HINT_NAME: - mSaveType |= SaveInfo.SAVE_DATA_TYPE_GENERIC; - break; - case View.AUTOFILL_HINT_PASSWORD: - mSaveType |= SaveInfo.SAVE_DATA_TYPE_PASSWORD; - mSaveType &= ~SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS; - mSaveType &= ~SaveInfo.SAVE_DATA_TYPE_USERNAME; - break; - case View.AUTOFILL_HINT_POSTAL_ADDRESS: - case View.AUTOFILL_HINT_POSTAL_CODE: - mSaveType |= SaveInfo.SAVE_DATA_TYPE_ADDRESS; - break; - case View.AUTOFILL_HINT_USERNAME: - mSaveType |= SaveInfo.SAVE_DATA_TYPE_USERNAME; - break; - } - } - } -} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.kt new file mode 100644 index 00000000..315299be --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.kt @@ -0,0 +1,82 @@ +/* + * 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.ViewNode; +import android.service.autofill.SaveInfo +import android.view.View +import android.view.autofill.AutofillId + +/** + * A stripped down version of a [ViewNode] that contains only autofill-relevant metadata. It also + * contains a `saveType` flag that is calculated based on the [ViewNode]'s autofill hints. + */ +class AutofillField(view: ViewNode) { + var saveType = 0 + private set + + val autofillHints: Array<String> = view.autofillHints + val autofillId: AutofillId = view.autofillId + val autofillType: Int = view.autofillType + val autofillOptions: Array<String>? = view.autofillOptions + val isFocused: Boolean = view.isFocused + + init { + updateSaveTypeFromHints() + } + + /** + * When the [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. + */ + fun getAutofillOptionIndex(value: String): Int? { + return autofillOptions?.indexOf(value) + } + + private fun updateSaveTypeFromHints() { + saveType = 0 + for (hint in autofillHints) { + when (hint) { + View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE, + View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY, + View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH, + View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR, + View.AUTOFILL_HINT_CREDIT_CARD_NUMBER, + View.AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE -> { + saveType = saveType or SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD + } + View.AUTOFILL_HINT_EMAIL_ADDRESS -> { + saveType = saveType or SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS + } + View.AUTOFILL_HINT_PHONE, View.AUTOFILL_HINT_NAME -> { + saveType = saveType or SaveInfo.SAVE_DATA_TYPE_GENERIC + } + View.AUTOFILL_HINT_PASSWORD -> { + saveType = saveType or SaveInfo.SAVE_DATA_TYPE_PASSWORD + saveType = saveType and SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS.inv() + saveType = saveType and SaveInfo.SAVE_DATA_TYPE_USERNAME.inv() + } + View.AUTOFILL_HINT_POSTAL_ADDRESS, + View.AUTOFILL_HINT_POSTAL_CODE -> { + saveType = saveType or SaveInfo.SAVE_DATA_TYPE_ADDRESS + } + View.AUTOFILL_HINT_USERNAME -> { + saveType = saveType or SaveInfo.SAVE_DATA_TYPE_USERNAME + } + } + } + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java deleted file mode 100644 index 0354b989..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java +++ /dev/null @@ -1,70 +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.view.autofill.AutofillId; - -import java.util.ArrayList; -import java.util.Arrays; -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 final List<String> mAllAutofillHints = new ArrayList<>(); - private final List<String> mFocusedAutofillHints = new ArrayList<>(); - private int size = 0; - private int mSaveType = 0; - - public void add(AutofillField autofillField) { - mSaveType |= autofillField.getSaveType(); - size++; - mAutofillIds.add(autofillField.getId()); - List<String> hintsList = Arrays.asList(autofillField.getHints()); - mAllAutofillHints.addAll(hintsList); - if (autofillField.isFocused()) { - mFocusedAutofillHints.addAll(hintsList); - } - 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 List<String> getFocusedHints() { - return mFocusedAutofillHints; - } - - public List<String> getAllHints() { - return mAllAutofillHints; - } -} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.kt new file mode 100644 index 00000000..371934da --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.kt @@ -0,0 +1,51 @@ +/* + * 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 + +/** + * Data structure that stores a collection of `AutofillField`s. Contains all of the client's `View` + * hierarchy autofill-relevant metadata. + */ +data class AutofillFieldsCollection(val autofillIds: ArrayList<AutofillId> = ArrayList<AutofillId>(), + val allAutofillHints: ArrayList<String> = ArrayList<String>(), + val focusedAutofillHints: ArrayList<String> = ArrayList<String>()) { + + private val autofillHintsToFieldsMap = HashMap<String, MutableList<AutofillField>>() + var saveType = 0 + private set + + fun add(autofillField: AutofillField) { + saveType = saveType or autofillField.saveType + autofillIds.add(autofillField.autofillId) + val hintsList = autofillField.autofillHints + allAutofillHints.addAll(hintsList) + if (autofillField.isFocused) { + focusedAutofillHints.addAll(hintsList) + } + autofillField.autofillHints.forEach { autofillHint -> + autofillHintsToFieldsMap[autofillHint] = autofillHintsToFieldsMap[autofillHint] ?: ArrayList<AutofillField>() + autofillHintsToFieldsMap[autofillHint]?.add(autofillField) + } + } + + fun getFieldsForHint(hint: String): MutableList<AutofillField>? { + return autofillHintsToFieldsMap[hint] + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java deleted file mode 100644 index aa57e935..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java +++ /dev/null @@ -1,184 +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.service.autofill.Dataset; -import android.support.annotation.NonNull; -import android.util.Log; -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; - -/** - * 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; - - public ClientFormData() { - this(null, new HashMap<String, SavedAutofillValue>()); - } - - public ClientFormData(String datasetName, HashMap<String, SavedAutofillValue> hintMap) { - this.hintMap = hintMap; - this.datasetName = datasetName; - } - - 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; - } - } - - /** - * Returns the name of the {@link Dataset}. - */ - public String getDatasetName() { - return this.datasetName; - } - - /** - * Sets the {@link Dataset} name. - */ - public void setDatasetName(String datasetName) { - this.datasetName = datasetName; - } - - /** - * Sets values for a list of hints. - */ - public void set(@NonNull String[] autofillHints, @NonNull SavedAutofillValue autofillValue) { - if (autofillHints.length < 1) { - return; - } - for (int i = 0; i < autofillHints.length; i++) { - hintMap.put(autofillHints[i], autofillValue); - } - } - - /** - * Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId} - * in a {@code AutofillFieldsCollection}. - */ - public boolean applyToFields(AutofillFieldsCollection autofillFieldsCollection, - Dataset.Builder datasetBuilder) { - boolean setValueAtLeastOnce = false; - List<String> allHints = autofillFieldsCollection.getAllHints(); - for (int hintIndex = 0; hintIndex < allHints.size(); hintIndex++) { - String hint = allHints.get(hintIndex); - List<AutofillField> autofillFields = autofillFieldsCollection.getFieldsForHint(hint); - if (autofillFields == null) { - continue; - } - for (int autofillFieldIndex = 0; autofillFieldIndex < autofillFields.size(); autofillFieldIndex++) { - AutofillField autofillField = autofillFields.get(autofillFieldIndex); - AutofillId autofillId = autofillField.getId(); - int autofillType = autofillField.getAutofillType(); - SavedAutofillValue savedAutofillValue = hintMap.get(hint); - switch (autofillType) { - case View.AUTOFILL_TYPE_LIST: - int listValue = autofillField.getAutofillOptionIndex(savedAutofillValue.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) { - datasetBuilder.setValue(autofillId, AutofillValue.forDate(dateValue)); - setValueAtLeastOnce = true; - } - break; - case View.AUTOFILL_TYPE_TEXT: - String textValue = savedAutofillValue.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(); - datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggleValue)); - setValueAtLeastOnce = true; - } - break; - case View.AUTOFILL_TYPE_NONE: - default: - Log.w(TAG, "Invalid autofill type - " + autofillType); - break; - } - } - } - 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()) { - return true; - } - } - return false; - } -} 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 new file mode 100644 index 00000000..78980aaa --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.kt @@ -0,0 +1,99 @@ +/* + * 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.util.Log +import android.view.View +import android.view.autofill.AutofillId +import android.view.autofill.AutofillValue +import java.util.HashMap + + +/** + * ClientFormData is the model that represents all of the form data on a client app's page, plus the + * dataset name associated with it. + */ +class ClientFormData constructor(var datasetName: String? = null, + private val hintMap: HashMap<String, MutableAutofillValue> = HashMap<String, MutableAutofillValue>()) { + + private val TAG = "ClientFormData" + + /** + * Sets values for a list of autofillHints. + */ + fun setAutofillValuesForHints(autofillHints: Array<String>, autofillValue: MutableAutofillValue) { + autofillHints.forEach { hint -> + hintMap[hint] = autofillValue + } + } + + /** + * Populates a [Dataset.Builder] with appropriate values for each [AutofillId] + * in a `AutofillFieldsCollection`. + */ + fun applyToFields(autofillFieldsCollection: AutofillFieldsCollection, + datasetBuilder: Dataset.Builder): Boolean { + var setValueAtLeastOnce = false + for (hint in autofillFieldsCollection.allAutofillHints) { + val autofillFields = autofillFieldsCollection.getFieldsForHint(hint) ?: continue + for (autofillField in autofillFields) { + val autofillId = autofillField.autofillId + val autofillType = autofillField.autofillType + val savedAutofillValue = hintMap[hint] + when (autofillType) { + View.AUTOFILL_TYPE_LIST -> { + savedAutofillValue?.textValue?.let(autofillField::getAutofillOptionIndex)?.let { index -> + datasetBuilder.setValue(autofillId, AutofillValue.forList(index)) + setValueAtLeastOnce = true + } + } + View.AUTOFILL_TYPE_DATE -> { + savedAutofillValue?.dateValue?.let { date -> + datasetBuilder.setValue(autofillId, AutofillValue.forDate(date)) + setValueAtLeastOnce = true + } + } + View.AUTOFILL_TYPE_TEXT -> { + savedAutofillValue?.textValue?.let { text -> + datasetBuilder.setValue(autofillId, AutofillValue.forText(text)) + setValueAtLeastOnce = true + } + } + View.AUTOFILL_TYPE_TOGGLE -> { + savedAutofillValue?.toggleValue?.let { toggle -> + datasetBuilder.setValue(autofillId, AutofillValue.forToggle(toggle)) + setValueAtLeastOnce = true + } + } + else -> Log.w(TAG, "Invalid autofill type - " + autofillType) + } + } + } + return setValueAtLeastOnce + } + + fun helpsWithHints(autofillHints: List<String>): Boolean { + for (autofillHint in autofillHints) { + hintMap[autofillHint]?.let { savedAutofillValue -> + if (!savedAutofillValue.isNull()) { + return true + } + } + } + return false + } +} 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/MutableAutofillValue.kt new file mode 100644 index 00000000..702a1358 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/MutableAutofillValue.kt @@ -0,0 +1,52 @@ +/* + * 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 + +/** + * Mutable, JSON serializable data class containing the same data as an [AutofillValue]. + */ +class MutableAutofillValue(viewNode: AssistStructure.ViewNode) { + var textValue: String? = null + var dateValue: Long? = null + var toggleValue: Boolean? = null + + init { + viewNode.autofillValue?.let { autofillValue -> + if (autofillValue.isList) { + val index = autofillValue.listValue + viewNode.autofillOptions?.let { autofillOptions -> + if (autofillOptions.size > index) { + textValue = autofillOptions[index] + } + } + } else if (autofillValue.isDate) { + dateValue = autofillValue.dateValue + } else if (autofillValue.isText) { + // Using toString of AutofillValue.getTextValue in order to save it to + // SharedPreferences. + textValue = autofillValue.textValue.toString() + } else { + } + } + } + + fun isNull(): Boolean { + return textValue == null && dateValue == null && toggleValue == null + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java deleted file mode 100644 index 73e0c81e..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/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/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java deleted file mode 100644 index 3926530e..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java +++ /dev/null @@ -1,91 +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.settings; - -import android.content.Context; -import android.content.SharedPreferences; -import android.service.autofill.Dataset; -import android.service.autofill.FillResponse; -import android.support.annotation.NonNull; - -public class MyPreferences { - private static final String TAG = "MyPreferences"; - - private static final String RESPONSE_AUTH_KEY = "response_auth"; - private static final String DATASET_AUTH_KEY = "dataset_auth"; - private static final String MASTER_PASSWORD_KEY = "master_password"; - - private static MyPreferences sInstance; - private final SharedPreferences mPrefs; - - private MyPreferences(Context context) { - mPrefs = context.getApplicationContext().getSharedPreferences("my-settings", - Context.MODE_PRIVATE); - } - - public static MyPreferences getInstance(Context context) { - if (sInstance == null) { - sInstance = new MyPreferences(context); - } - return sInstance; - } - - /** - * Gets whether {@link FillResponse}s should require authentication. - */ - public boolean isResponseAuth() { - return mPrefs.getBoolean(RESPONSE_AUTH_KEY, false); - } - - /** - * Enables/disables authentication for the entire autofill {@link FillResponse}. - */ - public void setResponseAuth(boolean responseAuth) { - mPrefs.edit().putBoolean(RESPONSE_AUTH_KEY, responseAuth).apply(); - } - - /** - * Gets whether {@link Dataset}s should require authentication. - */ - public boolean isDatasetAuth() { - return mPrefs.getBoolean(DATASET_AUTH_KEY, false); - } - - /** - * Enables/disables authentication for individual autofill {@link Dataset}s. - */ - public void setDatasetAuth(boolean datasetAuth) { - mPrefs.edit().putBoolean(DATASET_AUTH_KEY, datasetAuth).apply(); - } - - /** - * Gets autofill master username. - */ - public String getMasterPassword() { - return mPrefs.getString(MASTER_PASSWORD_KEY, null); - } - - /** - * Sets autofill master password. - */ - public void setMasterPassword(@NonNull String masterPassword) { - mPrefs.edit().putString(MASTER_PASSWORD_KEY, masterPassword).apply(); - } - - public void clearCredentials() { - mPrefs.edit().remove(MASTER_PASSWORD_KEY).apply(); - } -} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.kt new file mode 100644 index 00000000..096813f6 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.service.settings + +import android.content.Context +import android.content.SharedPreferences +import android.service.autofill.Dataset +import android.service.autofill.FillResponse + +object MyPreferences { + private val TAG = "MyPreferences" + + private val SHARED_PREF_KEY = "com.example.android.autofillframework.service.settings.MyPreferences" + private val RESPONSE_AUTH_KEY = "response_auth" + private val DATASET_AUTH_KEY = "dataset_auth" + private val MASTER_PASSWORD_KEY = "master_password" + + private fun getPrefs(context: Context): SharedPreferences { + return context.applicationContext.getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE) + } + + /** + * Determines whether [FillResponse]s should require authentication. + */ + fun isResponseAuth(context: Context): Boolean { + return getPrefs(context).getBoolean(RESPONSE_AUTH_KEY, false) + } + + fun setResponseAuth(context: Context, responseAuth: Boolean) { + getPrefs(context).edit().putBoolean(RESPONSE_AUTH_KEY, responseAuth).apply() + } + + /** + * Determines whether [Dataset]s should require authentication. + */ + fun isDatasetAuth(context: Context): Boolean { + return getPrefs(context).getBoolean(DATASET_AUTH_KEY, false) + } + + fun setDatasetAuth(context: Context, datasetAuth: Boolean) { + getPrefs(context).edit().putBoolean(DATASET_AUTH_KEY, datasetAuth).apply() + } + + /** + * Gets autofill master password. + */ + fun getMasterPassword(context: Context): String? { + return getPrefs(context).getString(MASTER_PASSWORD_KEY, null) + } + + /** + * Sets autofill master password. + */ + fun setMasterPassword(context: Context, masterPassword: String) { + getPrefs(context).edit().putString(MASTER_PASSWORD_KEY, masterPassword).apply() + } + + /** + * Removes master password. + */ + fun clearCredentials(context: Context) { + getPrefs(context).edit().remove(MASTER_PASSWORD_KEY).apply() + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java deleted file mode 100644 index 6387d36b..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java +++ /dev/null @@ -1,176 +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.settings; - -import android.content.DialogInterface; -import android.os.Bundle; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.Switch; -import android.widget.TextView; - -import com.example.android.autofillframework.R; -import com.example.android.autofillframework.service.datasource.LocalAutofillRepository; - -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, - R.id.settings_auth_responses_label, - R.id.settings_auth_responses_switch, - preferences.isResponseAuth(), - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - preferences.setResponseAuth(b); - } - }); - setupSettingsSwitch(R.id.settings_auth_datasets_container, - R.id.settings_auth_datasets_label, - R.id.settings_auth_datasets_switch, - preferences.isDatasetAuth(), - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - preferences.setDatasetAuth(b); - } - }); - setupSettingsButton(R.id.settings_clear_data_container, - R.id.settings_clear_data_label, - R.id.settings_clear_data_icon, - new View.OnClickListener() { - @Override - public void onClick(View view) { - buildClearDataDialog().show(); - } - }); - - setupSettingsButton(R.id.settings_auth_credentials_container, - R.id.settings_auth_credentials_label, - R.id.settings_auth_credentials_icon, - new View.OnClickListener() { - @Override - public void onClick(View view) { - if (preferences.getMasterPassword() != null) { - buildCurrentCredentialsDialog().show(); - } else { - buildNewCredentialsDialog().show(); - } - } - }); - } - - private AlertDialog buildClearDataDialog() { - return new AlertDialog.Builder(SettingsActivity.this) - .setMessage(R.string.settings_clear_data_confirmation) - .setTitle(R.string.settings_clear_data_confirmation_title) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - LocalAutofillRepository.getInstance - (SettingsActivity.this).clear(); - MyPreferences.getInstance(SettingsActivity.this) - .clearCredentials(); - dialog.dismiss(); - } - }) - .create(); - } - - private AlertDialog.Builder prepareCredentialsDialog() { - return new AlertDialog.Builder(SettingsActivity.this) - .setTitle(R.string.settings_auth_change_credentials_title) - .setNegativeButton(R.string.cancel, null); - } - - private AlertDialog buildCurrentCredentialsDialog() { - final EditText currentPasswordField = LayoutInflater - .from(SettingsActivity.this) - .inflate(R.layout.settings_authentication_dialog, null) - .findViewById(R.id.master_password_field); - return prepareCredentialsDialog() - .setMessage(R.string.settings_auth_enter_current_password) - .setView(currentPasswordField) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String password = currentPasswordField.getText().toString(); - if (MyPreferences.getInstance(SettingsActivity.this).getMasterPassword() - .equals(password)) { - buildNewCredentialsDialog().show(); - dialog.dismiss(); - } - } - }) - .create(); - } - - private AlertDialog buildNewCredentialsDialog() { - final EditText newPasswordField = LayoutInflater - .from(SettingsActivity.this) - .inflate(R.layout.settings_authentication_dialog, null) - .findViewById(R.id.master_password_field); - return prepareCredentialsDialog() - .setMessage(R.string.settings_auth_enter_new_password) - .setView(newPasswordField) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - String password = newPasswordField.getText().toString(); - MyPreferences.getInstance(SettingsActivity.this).setMasterPassword(password); - dialog.dismiss(); - } - }) - .create(); - } - - private void setupSettingsSwitch(int containerId, int labelId, int switchId, boolean checked, - CompoundButton.OnCheckedChangeListener checkedChangeListener) { - ViewGroup container = (ViewGroup) findViewById(containerId); - String switchLabel = ((TextView) container.findViewById(labelId)).getText().toString(); - final Switch switchView = container.findViewById(switchId); - switchView.setContentDescription(switchLabel); - switchView.setChecked(checked); - container.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - switchView.performClick(); - } - }); - switchView.setOnCheckedChangeListener(checkedChangeListener); - } - - private void setupSettingsButton(int containerId, int labelId, int imageViewId, - final View.OnClickListener onClickListener) { - ViewGroup container = (ViewGroup) findViewById(containerId); - String buttonLabel = ((TextView) container.findViewById(labelId)).getText().toString(); - final ImageView imageView = container.findViewById(imageViewId); - imageView.setContentDescription(buttonLabel); - container.setOnClickListener(onClickListener); - } -} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.kt new file mode 100644 index 00000000..cfa9d82f --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.kt @@ -0,0 +1,139 @@ +/* + * 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.settings + +import android.os.Bundle +import android.support.v7.app.AlertDialog +import android.support.v7.app.AppCompatActivity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.CompoundButton +import android.widget.EditText +import android.widget.ImageView +import android.widget.Switch +import android.widget.TextView +import com.example.android.autofillframework.R +import com.example.android.autofillframework.service.datasource.SharedPrefsAutofillRepository + +class SettingsActivity : AppCompatActivity() { + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.settings_activity) + setupSettingsSwitch(R.id.settings_auth_responses_container, + R.id.settings_auth_responses_label, + R.id.settings_auth_responses_switch, + MyPreferences.isResponseAuth(this), + CompoundButton.OnCheckedChangeListener { compoundButton, b -> + MyPreferences.setResponseAuth(this@SettingsActivity, b) + }) + setupSettingsSwitch(R.id.settings_auth_datasets_container, + R.id.settings_auth_datasets_label, + R.id.settings_auth_datasets_switch, + MyPreferences.isDatasetAuth(this), + CompoundButton.OnCheckedChangeListener { compoundButton, b -> + MyPreferences.setDatasetAuth(this@SettingsActivity, b) + }) + setupSettingsButton(R.id.settings_clear_data_container, + R.id.settings_clear_data_label, + R.id.settings_clear_data_icon, + View.OnClickListener { buildClearDataDialog().show() }) + + setupSettingsButton(R.id.settings_auth_credentials_container, + R.id.settings_auth_credentials_label, + R.id.settings_auth_credentials_icon, + View.OnClickListener { + MyPreferences.getMasterPassword(this@SettingsActivity)?.let { + buildCurrentCredentialsDialog().show() + } ?: buildNewCredentialsDialog().show() + }) + } + + private fun buildClearDataDialog(): AlertDialog { + return AlertDialog.Builder(this@SettingsActivity) + .setMessage(R.string.settings_clear_data_confirmation) + .setTitle(R.string.settings_clear_data_confirmation_title) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.ok) { dialog, which -> + SharedPrefsAutofillRepository.clear(this@SettingsActivity) + MyPreferences.clearCredentials(this@SettingsActivity) + dialog.dismiss() + } + .create() + } + + private fun prepareCredentialsDialog(): AlertDialog.Builder { + return AlertDialog.Builder(this@SettingsActivity) + .setTitle(R.string.settings_auth_change_credentials_title) + .setNegativeButton(R.string.cancel, null) + } + + private fun buildCurrentCredentialsDialog(): AlertDialog { + val currentPasswordField = LayoutInflater + .from(this@SettingsActivity) + .inflate(R.layout.settings_authentication_dialog, null) + .findViewById<EditText>(R.id.master_password_field) + return prepareCredentialsDialog() + .setMessage(R.string.settings_auth_enter_current_password) + .setView(currentPasswordField) + .setPositiveButton(R.string.ok) { dialog, which -> + val password = currentPasswordField.text.toString() + if (MyPreferences.getMasterPassword(this@SettingsActivity) == password) { + buildNewCredentialsDialog().show() + dialog.dismiss() + } + } + .create() + } + + private fun buildNewCredentialsDialog(): AlertDialog { + val newPasswordField = LayoutInflater + .from(this@SettingsActivity) + .inflate(R.layout.settings_authentication_dialog, null) + .findViewById<EditText>(R.id.master_password_field) + return prepareCredentialsDialog() + .setMessage(R.string.settings_auth_enter_new_password) + .setView(newPasswordField) + .setPositiveButton(R.string.ok) { dialog, which -> + val password = newPasswordField.text.toString() + MyPreferences.setMasterPassword(this@SettingsActivity, password) + dialog.dismiss() + } + .create() + } + + private fun setupSettingsSwitch(containerId: Int, labelId: Int, switchId: Int, checked: Boolean, + checkedChangeListener: CompoundButton.OnCheckedChangeListener) { + val container = findViewById(containerId) as ViewGroup + val switchLabel = (container.findViewById<View>(labelId) as TextView).text.toString() + val switchView = container.findViewById<Switch>(switchId) + switchView.contentDescription = switchLabel + switchView.isChecked = checked + container.setOnClickListener { switchView.performClick() } + switchView.setOnCheckedChangeListener(checkedChangeListener) + } + + private fun setupSettingsButton(containerId: Int, labelId: Int, imageViewId: Int, + onClickListener: View.OnClickListener) { + val container = findViewById(containerId) as ViewGroup + val buttonLabel = (container.findViewById<View>(labelId) as TextView).text.toString() + val imageView = container.findViewById<ImageView>(imageViewId) + imageView.contentDescription = buttonLabel + container.setOnClickListener(onClickListener) + } +} |