From 7728483122aeee29bf73078d5e1e169aef6a5a75 Mon Sep 17 00:00:00 2001 From: Douglas Sigelbaum Date: Wed, 31 May 2017 15:43:29 -0700 Subject: Implemented autofill callbacks in kotlin autofill sample. Disable AutoComplete in AutoCompleteTextView when AFF is working. Bug: 38182790 Test: manual Change-Id: If6de507dc2acf2033f2930d8e1c2cae85903b9a5 --- .../Application/src/main/AndroidManifest.xml | 19 ++-- .../autofillframework/app/CustomVirtualView.kt | 6 +- .../android/autofillframework/app/LoginActivity.kt | 76 ------------- .../android/autofillframework/app/MainActivity.kt | 11 +- .../app/StandardAutoCompleteSignInActivity.kt | 119 +++++++++++++++++++++ .../app/StandardSignInActivity.kt | 76 +++++++++++++ .../autofillframework/app/VirtualLoginActivity.kt | 75 ------------- .../autofillframework/app/VirtualSignInActivity.kt | 75 +++++++++++++ .../src/main/res/layout/activity_main.xml | 6 ++ .../layout/login_with_autocomplete_activity.xml | 93 ++++++++++++++++ .../Application/src/main/res/values/strings.xml | 7 ++ 11 files changed, 400 insertions(+), 163 deletions(-) delete mode 100644 input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt create mode 100644 input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.kt create mode 100644 input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.kt delete mode 100644 input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt create mode 100644 input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.kt create mode 100644 input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/login_with_autocomplete_activity.xml (limited to 'input/autofill/AutofillFramework') diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml index eb1f43c2..787a6dba 100644 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml @@ -16,9 +16,9 @@ --> + package="com.example.android.autofillframework" + android:versionCode="1" + android:versionName="1.0"> + android:taskAffinity=".StandardSignInActivity"> + + android:taskAffinity=".VirtualSignInActivity"> + ?, - val type: Int, var text: CharSequence, - val editable: Boolean, val sanitized: Boolean) { + val type: Int, var text: CharSequence, + val editable: Boolean, val sanitized: Boolean) { var focused = false override fun toString(): String { @@ -199,7 +199,7 @@ class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, a } private inner class Line constructor(val idEntry: String, label: String, hints: Array, - text: String, sanitized: Boolean) { + 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 deleted file mode 100644 index 8bdc1c33..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt +++ /dev/null @@ -1,76 +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.app - - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.support.v7.app.AppCompatActivity -import android.widget.Toast -import com.example.android.autofillframework.R -import kotlinx.android.synthetic.main.login_activity.clear -import kotlinx.android.synthetic.main.login_activity.login -import kotlinx.android.synthetic.main.login_activity.passwordField -import kotlinx.android.synthetic.main.login_activity.usernameField - - -class LoginActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.login_activity) - login.setOnClickListener { login() } - clear.setOnClickListener { resetFields() } - } - - private fun resetFields() { - usernameField.setText("") - passwordField.setText("") - } - - /** - * Emulates a login action. - */ - private fun login() { - val username = usernameField.text.toString() - val password = passwordField.text.toString() - val valid = isValidCredentials(username, password) - if (valid) { - val intent = WelcomeActivity.getStartActivityIntent(this@LoginActivity) - startActivity(intent) - finish() - } else { - Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show() - } - } - - /** - * Dummy implementation for demo purposes. A real service should use secure mechanisms to - * authenticate users. - */ - fun isValidCredentials(username: String, password: String): Boolean { - return username == password - } - - companion object { - - fun getStartActivityIntent(context: Context): Intent { - val intent = Intent(context, LoginActivity::class.java) - return intent - } - } -} \ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.kt index 1759de25..c7c458d4 100644 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.kt +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.kt @@ -19,6 +19,7 @@ import android.os.Bundle import android.support.v7.app.AppCompatActivity import com.example.android.autofillframework.R import kotlinx.android.synthetic.main.activity_main.creditCardCheckoutButton +import kotlinx.android.synthetic.main.activity_main.standardLoginWithAutoCompleteButton import kotlinx.android.synthetic.main.activity_main.standardViewSignInButton import kotlinx.android.synthetic.main.activity_main.virtualViewSignInButton @@ -33,6 +34,7 @@ class MainActivity : AppCompatActivity() { standardViewSignInButton.setOnClickListener { standardViewSignIn() } virtualViewSignInButton.setOnClickListener { virtualViewSignIn() } creditCardCheckoutButton.setOnClickListener { creditCardCheckout() } + standardLoginWithAutoCompleteButton.setOnClickListener { standardAutoCompleteSignIn() } } private fun creditCardCheckout() { @@ -41,12 +43,17 @@ class MainActivity : AppCompatActivity() { } private fun standardViewSignIn() { - val intent = LoginActivity.getStartActivityIntent(this) + val intent = StandardSignInActivity.getStartActivityIntent(this) + startActivity(intent) + } + + private fun standardAutoCompleteSignIn() { + val intent = StandardAutoCompleteSignInActivity.getStartActivityIntent(this) startActivity(intent) } private fun virtualViewSignIn() { - val intent = VirtualLoginActivity.getStartActivityIntent(this) + val intent = VirtualSignInActivity.getStartActivityIntent(this) startActivity(intent) } } \ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.kt new file mode 100644 index 00000000..21eb4d70 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardAutoCompleteSignInActivity.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.util.Log +import android.view.View +import android.view.autofill.AutofillManager +import android.widget.ArrayAdapter +import android.widget.AutoCompleteTextView +import android.widget.Toast +import com.example.android.autofillframework.CommonUtil.TAG +import com.example.android.autofillframework.R +import kotlinx.android.synthetic.main.login_with_autocomplete_activity.clear +import kotlinx.android.synthetic.main.login_with_autocomplete_activity.login +import kotlinx.android.synthetic.main.login_with_autocomplete_activity.passwordField +import kotlinx.android.synthetic.main.login_with_autocomplete_activity.usernameField + +class StandardAutoCompleteSignInActivity : AppCompatActivity() { + private var mAutofillReceived = false + private var mAutofillCallback: AutofillManager.AutofillCallback? = null + private var mAutofillManager: AutofillManager? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.login_with_autocomplete_activity) + + login.setOnClickListener { submitLogin() } + clear.setOnClickListener { resetFields() } + mAutofillCallback = MyAutofillCallback() + mAutofillManager = getSystemService(AutofillManager::class.java) + val mockAutocompleteAdapter = ArrayAdapter.createFromResource(this, R.array.mock_autocomplete_sign_in_suggestions, + android.R.layout.simple_dropdown_item_1line) + usernameField.setAdapter(mockAutocompleteAdapter) + } + + override fun onResume() { + super.onResume() + mAutofillManager?.registerCallback(mAutofillCallback) + } + + override fun onPause() { + super.onPause() + mAutofillManager?.unregisterCallback(mAutofillCallback) + } + + private fun resetFields() { + usernameField.setText("") + passwordField.setText("") + } + + /** + * Emulates a login action. + */ + private fun submitLogin() { + val username = usernameField.text.toString() + val password = passwordField.text.toString() + val valid = isValidCredentials(username, password) + if (valid) { + val intent = WelcomeActivity.getStartActivityIntent(this@StandardAutoCompleteSignInActivity) + startActivity(intent) + finish() + } else { + Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show() + } + } + + /** + * Dummy implementation for demo purposes. A real service should use secure mechanisms to + * authenticate users. + */ + fun isValidCredentials(username: String?, password: String?): Boolean { + return username != null && password != null && username == password + } + + private inner class MyAutofillCallback : AutofillManager.AutofillCallback() { + override fun onAutofillEvent(view: View, event: Int) { + super.onAutofillEvent(view, event) + if (view is AutoCompleteTextView) { + when (event) { + AutofillManager.AutofillCallback.EVENT_INPUT_UNAVAILABLE, + AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN -> if (!mAutofillReceived) { + view.showDropDown() + } + AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN -> { + mAutofillReceived = true + view.setAdapter(null) + } + else -> Log.d(TAG, "Unexpected callback: " + event) + } + } + } + } + + companion object { + + fun getStartActivityIntent(context: Context): Intent { + val intent = Intent(context, StandardAutoCompleteSignInActivity::class.java) + return intent + } + } +} \ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.kt new file mode 100644 index 00000000..971bd167 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/StandardSignInActivity.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app + + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.widget.Toast +import com.example.android.autofillframework.R +import kotlinx.android.synthetic.main.login_activity.clear +import kotlinx.android.synthetic.main.login_activity.login +import kotlinx.android.synthetic.main.login_activity.passwordField +import kotlinx.android.synthetic.main.login_activity.usernameField + + +class StandardSignInActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.login_activity) + login.setOnClickListener { login() } + clear.setOnClickListener { resetFields() } + } + + private fun resetFields() { + usernameField.setText("") + passwordField.setText("") + } + + /** + * Emulates a login action. + */ + private fun login() { + val username = usernameField.text.toString() + val password = passwordField.text.toString() + val valid = isValidCredentials(username, password) + if (valid) { + val intent = WelcomeActivity.getStartActivityIntent(this@StandardSignInActivity) + startActivity(intent) + finish() + } else { + Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show() + } + } + + /** + * Dummy implementation for demo purposes. A real service should use secure mechanisms to + * authenticate users. + */ + fun isValidCredentials(username: String, password: String): Boolean { + return username == password + } + + companion object { + + fun getStartActivityIntent(context: Context): Intent { + val intent = Intent(context, StandardSignInActivity::class.java) + return intent + } + } +} \ No newline at end of file diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt deleted file mode 100644 index 52081afd..00000000 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt +++ /dev/null @@ -1,75 +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.app - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.support.v7.app.AppCompatActivity -import android.widget.Toast -import com.example.android.autofillframework.R -import kotlinx.android.synthetic.main.virtual_login_activity.clear -import kotlinx.android.synthetic.main.virtual_login_activity.custom_view -import kotlinx.android.synthetic.main.virtual_login_activity.login - - -class VirtualLoginActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setContentView(R.layout.virtual_login_activity) - - login.setOnClickListener { submitLogin() } - clear.setOnClickListener { resetFields() } - } - - private fun resetFields() { - custom_view.resetFields() - } - - /** - * Emulates a login action. - */ - private fun submitLogin() { - val username = custom_view.usernameText.toString() - val password = custom_view.passwordText.toString() - val valid = isValidCredentials(username, password) - if (valid) { - val intent = WelcomeActivity.getStartActivityIntent(this@VirtualLoginActivity) - startActivity(intent) - finish() - } else { - Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show() - } - } - - /** - * Dummy implementation for demo purposes. A real service should use secure mechanisms to - * authenticate users. - */ - fun isValidCredentials(username: String?, password: String?): Boolean { - return username != null && password != null && username == password - } - - companion object { - - fun getStartActivityIntent(context: Context): Intent { - val intent = Intent(context, VirtualLoginActivity::class.java) - return intent - } - } -} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.kt new file mode 100644 index 00000000..6d86da95 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualSignInActivity.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.autofillframework.app + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.widget.Toast +import com.example.android.autofillframework.R +import kotlinx.android.synthetic.main.virtual_login_activity.clear +import kotlinx.android.synthetic.main.virtual_login_activity.custom_view +import kotlinx.android.synthetic.main.virtual_login_activity.login + + +class VirtualSignInActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.virtual_login_activity) + + login.setOnClickListener { submitLogin() } + clear.setOnClickListener { resetFields() } + } + + private fun resetFields() { + custom_view.resetFields() + } + + /** + * Emulates a login action. + */ + private fun submitLogin() { + val username = custom_view.usernameText.toString() + val password = custom_view.passwordText.toString() + val valid = isValidCredentials(username, password) + if (valid) { + val intent = WelcomeActivity.getStartActivityIntent(this@VirtualSignInActivity) + startActivity(intent) + finish() + } else { + Toast.makeText(this, "Authentication failed.", Toast.LENGTH_SHORT).show() + } + } + + /** + * Dummy implementation for demo purposes. A real service should use secure mechanisms to + * authenticate users. + */ + fun isValidCredentials(username: String?, password: String?): Boolean { + return username != null && password != null && username == password + } + + companion object { + + fun getStartActivityIntent(context: Context): Intent { + val intent = Intent(context, VirtualSignInActivity::class.java) + return intent + } + } +} diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/activity_main.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/activity_main.xml index 26d0657d..b2510b07 100644 --- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/activity_main.xml +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/activity_main.xml @@ -29,6 +29,12 @@ android:layout_height="wrap_content" android:text="@string/standard_view_sign_in" /> +