aboutsummaryrefslogtreecommitdiff
path: root/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app
diff options
context:
space:
mode:
authorDouglas Sigelbaum <sigelbaum@google.com>2017-05-16 22:31:36 -0700
committerDouglas Sigelbaum <sigelbaum@google.com>2017-05-17 00:35:21 -0700
commit08a3592720a23e56c9546cf89abf707372f5abfb (patch)
tree603afca306120848ce00f4cbbc5be26859cfa358 /input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app
parent8fa30fc0cbc6a7e2da9749e66373d70a593fd1ce (diff)
downloadandroid-08a3592720a23e56c9546cf89abf707372f5abfb.tar.gz
Initial kotlin commit for client app in the autofill sample.
Bug: 38182790 Test: manual Change-Id: Ib76173768461528f52297a7acd2f40d532b14d0a
Diffstat (limited to 'input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app')
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.kt88
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt273
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt85
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.kt53
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt77
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.kt38
6 files changed, 614 insertions, 0 deletions
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
new file mode 100644
index 00000000..f2d9a8b4
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CreditCardActivity.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.autofillframework.app
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.ArrayAdapter
+import android.widget.Button
+import android.widget.Spinner
+
+import com.example.android.autofillframework.R
+
+class CreditCardActivity : AppCompatActivity() {
+
+ private var mCcExpirationDaySpinner: Spinner? = null
+ private var mCcExpirationMonthSpinner: Spinner? = null
+ private var mCcExpirationYearSpinner: Spinner? = null
+ private var mSubmitButton: Button? = null
+ private var mClearButton: Button? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.credit_card_activity)
+
+ mSubmitButton = findViewById(R.id.submit) as Button
+ mClearButton = findViewById(R.id.clear) as Button
+ mCcExpirationDaySpinner = findViewById(R.id.expirationDay) as Spinner
+ mCcExpirationMonthSpinner = findViewById(R.id.expirationMonth) as Spinner
+ mCcExpirationYearSpinner = findViewById(R.id.expirationYear) as Spinner
+
+ // 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)
+ // Specify the layout to use when the list of choices appears
+ dayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ // Apply the adapter to the spinner
+ mCcExpirationDaySpinner!!.adapter = dayAdapter
+
+ val monthAdapter = ArrayAdapter.createFromResource(this, R.array.month_array, android.R.layout.simple_spinner_item)
+ monthAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ mCcExpirationMonthSpinner!!.adapter = monthAdapter
+
+ val yearAdapter = ArrayAdapter.createFromResource(this, R.array.year_array, android.R.layout.simple_spinner_item)
+ yearAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+ mCcExpirationYearSpinner!!.adapter = yearAdapter
+
+ mSubmitButton!!.setOnClickListener { submit() }
+ mClearButton!!.setOnClickListener { resetFields() }
+ }
+
+ private fun resetFields() {
+ //TODO
+ }
+
+ /**
+ * Launches new Activity and finishes, triggering an autofill save request if the user entered
+ * any new data.
+ */
+ private fun submit() {
+ val intent = WelcomeActivity.getStartActivityIntent(this@CreditCardActivity)
+ 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
new file mode 100644
index 00000000..c5a7989e
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.kt
@@ -0,0 +1,273 @@
+/*
+ * 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.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Paint.Style
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.util.Log
+import android.util.SparseArray
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewStructure
+import android.view.autofill.AutofillManager
+import android.view.autofill.AutofillValue
+import android.widget.EditText
+import android.widget.TextView
+
+import com.example.android.autofillframework.R
+
+import java.util.ArrayList
+import java.util.Arrays
+
+import com.example.android.autofillframework.CommonUtil.bundleToString
+
+
+/**
+ * Custom View with virtual child views for Username/Password text fields.
+ */
+class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, attrs) {
+
+ private val mLines = ArrayList<Line>()
+ private val mItems = SparseArray<Item>()
+ private val mAfm: AutofillManager
+
+ private var mFocusedLine: Line? = null
+ private val mTextPaint: Paint
+ private val mTextHeight: Int
+ private val mTopMargin: Int
+ private val mLeftMargin: Int
+ private val mVerticalGap: Int
+ private val mLineLength: Int
+ private val mFocusedColor: Int
+ private val mUnfocusedColor: Int
+
+ private val mUsernameLine: Line
+ private val mPasswordLine: Line
+
+ init {
+
+ mAfm = context.getSystemService(AutofillManager::class.java)
+
+ mTextPaint = Paint()
+
+ mUnfocusedColor = Color.BLACK
+ mFocusedColor = Color.RED
+ mTextPaint.style = Style.FILL
+ mTopMargin = 100
+ mLeftMargin = 100
+ mTextHeight = 90
+ mVerticalGap = 10
+
+ mLineLength = mTextHeight + mVerticalGap
+ mTextPaint.textSize = mTextHeight.toFloat()
+ mUsernameLine = addLine("usernameField", context.getString(R.string.username_label),
+ arrayOf(View.AUTOFILL_HINT_USERNAME), " ", true)
+ mPasswordLine = addLine("passwordField", context.getString(R.string.password_label),
+ arrayOf(View.AUTOFILL_HINT_PASSWORD), " ", false)
+
+ Log.d(TAG, "Text height: " + mTextHeight)
+ }
+
+ 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)
+ 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
+ }
+ // Set the item's text to the text wrapped in the AutofillValue.
+ item.text = value.textValue
+ }
+ postInvalidate()
+ }
+
+ override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {
+ // Build a ViewStructure to pack in AutoFillService requests.
+ structure.setClassName(javaClass.name)
+ val childrenSize = mItems.size()
+ Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags + ", items = "
+ + childrenSize + ", extras: " + bundleToString(structure.extras))
+ var index = structure.addChildCount(childrenSize)
+ for (i in 0..childrenSize - 1) {
+ val item = mItems.valueAt(i)
+ Log.d(TAG, "Adding new child at index $index: $item")
+ val child = structure.newChild(index)
+ child.setAutofillId(structure, item.id)
+ child.setAutofillHints(item.hints)
+ child.setAutofillType(item.type)
+ child.setDataIsSensitive(!item.sanitized)
+ child.text = item.text
+ child.setAutofillValue(AutofillValue.forText(item.text))
+ child.setFocused(item.focused)
+ child.setId(item.id, context.packageName, null, item.line.idEntry)
+ child.setClassName(item.className)
+ index++
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ Log.d(TAG, "onDraw: " + mLines.size + " lines; canvas:" + canvas)
+ var x: Float
+ var y = (mTopMargin + mLineLength).toFloat()
+ for (i in mLines.indices) {
+ 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() + ": ["
+ val writeText = line.fieldTextItem.text.toString() + "]"
+ // Paints the label first...
+ canvas.drawText(readOnlyText, x, y, mTextPaint)
+ // ...then paints the edit text and sets the proper boundary
+ val deltaX = mTextPaint.measureText(readOnlyText)
+ x += deltaX
+ line.bounds.set(x.toInt(), (y - mLineLength).toInt(),
+ (x + mTextPaint.measureText(writeText)).toInt(), y.toInt())
+ Log.d(TAG, "setBounds(" + x + ", " + y + "): " + line.bounds)
+ canvas.drawText(writeText, x, y, mTextPaint)
+ y += mLineLength.toFloat()
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ val y = event.y.toInt()
+ Log.d(TAG, "Touched: y=$y, range=$mLineLength, top=$mTopMargin")
+ var lowerY = mTopMargin
+ var upperY = -1
+ for (i in mLines.indices) {
+ upperY = lowerY + mLineLength
+ val line = mLines[i]
+ Log.d(TAG, "Line $i ranges from $lowerY to $upperY")
+ if (lowerY <= y && y <= upperY) {
+ if (mFocusedLine != null) {
+ Log.d(TAG, "Removing focus from " + mFocusedLine!!)
+ mFocusedLine!!.changeFocus(false)
+ }
+ Log.d(TAG, "Changing focus to " + line)
+ mFocusedLine = line
+ mFocusedLine!!.changeFocus(true)
+ invalidate()
+ break
+ }
+ lowerY += mLineLength
+ }
+ return super.onTouchEvent(event)
+ }
+
+ val usernameText: CharSequence
+ get() = mUsernameLine.fieldTextItem.text
+
+ val passwordText: CharSequence
+ get() = mPasswordLine.fieldTextItem.text
+
+ fun resetFields() {
+ mUsernameLine.reset()
+ mPasswordLine.reset()
+ postInvalidate()
+ }
+
+ private fun addLine(idEntry: String, label: String, hints: Array<String>, text: String, sanitized: Boolean): Line {
+ val line = Line(idEntry, label, hints, text, sanitized)
+ mLines.add(line)
+ mItems.put(line.labelItem.id, line.labelItem)
+ mItems.put(line.fieldTextItem.id, line.fieldTextItem)
+ 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) {
+ var focused = false
+
+ override fun toString(): String {
+ return id.toString() + ": " + text + if (editable)
+ " (editable)"
+ else
+ " (read-only)" + if (sanitized) " (sanitized)" else " (sensitive"
+ }
+
+ val className: String
+ 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) {
+
+ // Boundaries of the text field, relative to the CustomView
+ internal val bounds = Rect()
+ var labelItem: Item
+ var fieldTextItem: Item
+
+ init {
+ this.labelItem = Item(this, ++nextId, null, View.AUTOFILL_TYPE_NONE, label, false, true)
+ this.fieldTextItem = Item(this, ++nextId, hints, View.AUTOFILL_TYPE_TEXT, text, true, sanitized)
+ }
+
+ internal fun changeFocus(focused: Boolean) {
+ fieldTextItem.focused = focused
+ if (focused) {
+ val absBounds = absCoordinates
+ Log.d(TAG, "focus gained on " + fieldTextItem.id + "; absBounds=" + absBounds)
+ mAfm.notifyViewEntered(this@CustomVirtualView, fieldTextItem.id, absBounds)
+ } else {
+ Log.d(TAG, "focus lost on " + fieldTextItem.id)
+ mAfm.notifyViewExited(this@CustomVirtualView, fieldTextItem.id)
+ }
+ }
+
+ private // Must offset the boundaries so they're relative to the CustomView.
+ val absCoordinates: Rect
+ get() {
+ val offset = IntArray(2)
+ getLocationOnScreen(offset)
+ val absBounds = Rect(bounds.left + offset[0],
+ bounds.top + offset[1],
+ bounds.right + offset[0], bounds.bottom + offset[1])
+ Log.v(TAG, "getAbsCoordinates() for " + fieldTextItem.id + ": bounds=" + bounds
+ + " offset: " + Arrays.toString(offset) + " absBounds: " + absBounds)
+ return absBounds
+ }
+
+ fun reset() {
+ fieldTextItem.text = " "
+ }
+
+ override fun toString(): String {
+ return "Label: " + labelItem + " Text: " + fieldTextItem + " Focused: " +
+ fieldTextItem.focused
+ }
+ }
+
+ companion object {
+
+ private val TAG = "CustomView"
+
+ private var nextId: Int = 0
+ }
+} \ No newline at end of file
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
new file mode 100644
index 00000000..8a739606
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/LoginActivity.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.autofillframework.app
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.Button
+import android.widget.EditText
+import android.widget.Toast
+
+import com.example.android.autofillframework.R
+
+class LoginActivity : AppCompatActivity() {
+
+ private var mUsernameEditText: EditText? = null
+ private var mPasswordEditText: EditText? = null
+ private var mLoginButton: Button? = null
+ private var mClearButton: Button? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.login_activity)
+
+ mLoginButton = findViewById(R.id.login) as Button
+ mClearButton = findViewById(R.id.clear) as Button
+ mUsernameEditText = findViewById(R.id.usernameField) as EditText
+ mPasswordEditText = findViewById(R.id.passwordField) as EditText
+ mLoginButton!!.setOnClickListener { login() }
+ mClearButton!!.setOnClickListener { resetFields() }
+ }
+
+ private fun resetFields() {
+ mUsernameEditText!!.setText("")
+ mPasswordEditText!!.setText("")
+ }
+
+ /**
+ * Emulates a login action.
+ */
+ private fun login() {
+ val username = mUsernameEditText!!.text.toString()
+ val password = mPasswordEditText!!.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 != null && password != null && 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
new file mode 100644
index 00000000..b47daa18
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/MainActivity.kt
@@ -0,0 +1,53 @@
+/*
+ * 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.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+
+import com.example.android.autofillframework.R
+
+/**
+ * This is used to launch sample activities that showcase autofill.
+ */
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ findViewById(R.id.standardViewSignInButton).setOnClickListener { standardViewSignIn() }
+ findViewById(R.id.virtualViewSignInButton).setOnClickListener { virtualViewSignIn() }
+ findViewById(R.id.creditCardCheckoutButton).setOnClickListener { creditCardCheckout() }
+ }
+
+ private fun creditCardCheckout() {
+ val intent = CreditCardActivity.getStartActivityIntent(this)
+ startActivity(intent)
+ }
+
+ private fun standardViewSignIn() {
+ val intent = LoginActivity.getStartActivityIntent(this)
+ startActivity(intent)
+ }
+
+ private fun virtualViewSignIn() {
+ val intent = VirtualLoginActivity.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/VirtualLoginActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.kt
new file mode 100644
index 00000000..198ff9d5
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/VirtualLoginActivity.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.app
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.View
+import android.widget.Toast
+
+import com.example.android.autofillframework.R
+
+
+class VirtualLoginActivity : AppCompatActivity() {
+
+ private var mCustomVirtualView: CustomVirtualView? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.virtual_login_activity)
+
+ mCustomVirtualView = findViewById(R.id.custom_view) as CustomVirtualView
+ findViewById(R.id.login).setOnClickListener { login() }
+ findViewById(R.id.clear).setOnClickListener { resetFields() }
+ }
+
+ private fun resetFields() {
+ mCustomVirtualView!!.resetFields()
+ }
+
+ /**
+ * Emulates a login action.
+ */
+ private fun login() {
+ val username = mCustomVirtualView!!.usernameText.toString()
+ val password = mCustomVirtualView!!.passwordText.toString()
+ val valid = isValidCredentials(username, password)
+ if (valid) {
+ val intent = WelcomeActivity.getStartActivityIntent(this@VirtualLoginActivity)
+ startActivity(intent)
+ } 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/WelcomeActivity.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.kt
new file mode 100644
index 00000000..01475180
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/app/WelcomeActivity.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+
+import com.example.android.autofillframework.R
+
+class WelcomeActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.welcome_activity)
+ }
+
+ companion object {
+
+ fun getStartActivityIntent(context: Context): Intent {
+ return Intent(context, WelcomeActivity::class.java)
+ }
+ }
+}