aboutsummaryrefslogtreecommitdiff
path: root/input/autofill/AutofillFramework/kotlinApp/Application/src
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
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')
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml107
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java54
-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
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java160
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java100
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java128
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java85
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java41
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java127
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java120
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java70
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java184
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java135
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java91
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java176
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/ic_launcher.pngbin0 -> 4199 bytes
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/tile.9.pngbin0 -> 196 bytes
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-mdpi/ic_launcher.pngbin0 -> 2535 bytes
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xhdpi/ic_launcher.pngbin0 -> 6022 bytes
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.pngbin0 -> 11040 bytes
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml20
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_person_black_24dp.xml20
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/activity_main.xml46
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/auth_activity.xml62
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/credit_card_activity.xml108
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/list_item.xml26
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/login_activity.xml91
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_activity.xml143
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_authentication_dialog.xml26
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/virtual_login_activity.xml48
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/welcome_activity.xml30
-rwxr-xr-xinput/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 3323 bytes
-rwxr-xr-xinput/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2036 bytes
-rwxr-xr-xinput/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 4201 bytes
-rwxr-xr-xinput/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 6861 bytes
-rwxr-xr-xinput/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 9672 bytes
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml24
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml25
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml22
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml21
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml24
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml29
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/dimens.xml28
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml106
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/styles.xml42
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml32
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml42
-rw-r--r--input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/xml/autofill_service.xml24
53 files changed, 3231 insertions, 0 deletions
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..28d9c0b5
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/AndroidManifest.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.android.autofillframework"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity
+ android:name=".app.MainActivity"
+ android:label="AF Main"
+ android:taskAffinity=".MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".app.LoginActivity"
+ android:label="AF StandardLogin"
+ android:taskAffinity=".LoginActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".app.VirtualLoginActivity"
+ android:label="AF VirtualLogin"
+ android:taskAffinity=".VirtualLoginActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".app.WelcomeActivity" />
+ <activity
+ android:name=".app.CreditCardActivity"
+ android:label="AF CreditCard"
+ android:taskAffinity=".CreditCardActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <!--
+ Including launcher icon for Autofill Settings to convenience.
+ Not necessary for a real service.
+ -->
+ <activity
+ android:name=".service.settings.SettingsActivity"
+ android:exported="true"
+ android:label="@string/settings_name"
+ android:taskAffinity=".SettingsActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <!--
+ Declare AutofillService implementation; only needed for a small number of apps that will
+ be implementing an AutofillService. Framework parses meta-data and sets the service's
+ Settings Activity based on what the meta-data resource points to.
+ -->
+ <service
+ android:name=".service.MyAutofillService"
+ android:permission="android.permission.BIND_AUTOFILL"
+ android:label="Sample Autofill Service">
+ <meta-data
+ android:name="android.autofill"
+ android:resource="@xml/autofill_service" />
+
+ <intent-filter>
+ <action android:name="android.service.autofill.AutofillService" />
+ </intent-filter>
+ </service>
+
+ <activity android:name=".service.AuthActivity" />
+ </application>
+
+</manifest> \ No newline at end of file
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
new file mode 100644
index 00000000..54049902
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/CommonUtil.java
@@ -0,0 +1,54 @@
+/*
+ * 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/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)
+ }
+ }
+}
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
new file mode 100644
index 00000000..768b2ee3
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AuthActivity.java
@@ -0,0 +1,160 @@
+/*
+ * 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/AutofillHelper.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java
new file mode 100644
index 00000000..460729e6
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/AutofillHelper.java
@@ -0,0 +1,100 @@
+/*
+ * 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/MyAutofillService.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java
new file mode 100644
index 00000000..61e42050
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/MyAutofillService.java
@@ -0,0 +1,128 @@
+/*
+ * 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/StructureParser.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java
new file mode 100644
index 00000000..b6294449
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/StructureParser.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.autofillframework.service;
+
+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/datasource/AutofillRepository.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java
new file mode 100644
index 00000000..8de8b647
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/AutofillRepository.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.example.android.autofillframework.service.model.ClientFormData;
+
+import java.util.HashMap;
+import java.util.List;
+
+public interface AutofillRepository {
+
+ /**
+ * Gets saved ClientFormData that contains some objects that can autofill fields with these
+ * {@code autofillHints}.
+ */
+ HashMap<String, ClientFormData> getClientFormData(List<String> focusedAutofillHints,
+ List<String> allAutofillHints);
+
+ /**
+ * Saves LoginCredential under this datasetName.
+ */
+ void saveClientFormData(ClientFormData clientFormData);
+
+ /**
+ * Clears all data.
+ */
+ void clear();
+}
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
new file mode 100644
index 00000000..8336fe1e
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/datasource/LocalAutofillRepository.java
@@ -0,0 +1,127 @@
+/*
+ * 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/model/AutofillField.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java
new file mode 100644
index 00000000..4d4de2bc
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillField.java
@@ -0,0 +1,120 @@
+/*
+ * 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;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Class that represents a field that can be autofilled. It will contain a description
+ * (what type data the field holds), an AutoFillId (an ID unique to the rest of the ViewStructure),
+ * and a value (what data is currently in the field).
+ */
+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/AutofillFieldsCollection.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java
new file mode 100644
index 00000000..0354b989
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/AutofillFieldsCollection.java
@@ -0,0 +1,70 @@
+/*
+ * 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/ClientFormData.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java
new file mode 100644
index 00000000..aa57e935
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/ClientFormData.java
@@ -0,0 +1,184 @@
+/*
+ * 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/SavedAutofillValue.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java
new file mode 100644
index 00000000..73e0c81e
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/model/SavedAutofillValue.java
@@ -0,0 +1,135 @@
+/*
+ * 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
new file mode 100644
index 00000000..3926530e
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/MyPreferences.java
@@ -0,0 +1,91 @@
+/*
+ * 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/SettingsActivity.java b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java
new file mode 100644
index 00000000..6387d36b
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/service/settings/SettingsActivity.java
@@ -0,0 +1,176 @@
+/*
+ * 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/res/drawable-hdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 00000000..b1efaf4b
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/tile.9.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/tile.9.png
new file mode 100644
index 00000000..13586288
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/tile.9.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-mdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 00000000..f5f9244f
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..5d07b3f0
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..6ef21e1f
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml
new file mode 100644
index 00000000..4d2afb01
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_delete_forever_black_24dp.xml
@@ -0,0 +1,20 @@
+<!--
+ * 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.
+-->
+<vector android:alpha="0.50" android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z"/>
+</vector>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_person_black_24dp.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_person_black_24dp.xml
new file mode 100644
index 00000000..6534d9f0
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable/ic_person_black_24dp.xml
@@ -0,0 +1,20 @@
+<!--
+ * 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.
+-->
+<vector android:alpha="0.50" android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
+</vector>
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
new file mode 100644
index 00000000..26d0657d
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/activity_main.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingStart="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin">
+
+ <Button
+ android:id="@+id/standardViewSignInButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/standard_view_sign_in" />
+
+ <Button
+ android:id="@+id/virtualViewSignInButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:text="@string/virtual_view_sign_in" />
+
+ <Button
+ android:id="@+id/creditCardCheckoutButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/activity_vertical_margin"
+ android:text="@string/credit_card_checkout" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/auth_activity.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/auth_activity.xml
new file mode 100644
index 00000000..4b5926a7
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/auth_activity.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/authLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ tools:context=".service.AuthActivity">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="Master password">
+ </TextView>
+
+ <EditText
+ android:id="@+id/master_password"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword">
+ </EditText>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Cancel" />
+
+ <Button
+ android:id="@+id/login"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Login" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/credit_card_activity.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/credit_card_activity.xml
new file mode 100644
index 00000000..7d8e099f
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/credit_card_activity.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/standardLoginLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+
+ <TextView
+ android:id="@+id/creditCardNumberLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="20dp"
+ android:importantForAutofill="no"
+ android:text="@string/credit_card_number_label" />
+
+ <EditText
+ android:id="@+id/creditCardNumberField"
+ android:layout_width="200sp"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/creditCardNumberLabel"
+ android:layout_toEndOf="@id/creditCardNumberLabel"
+ android:autofillHints="creditCardNumber" />
+
+ <TextView
+ android:id="@+id/creditCardExpirationLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@+id/creditCardNumberLabel"
+ android:layout_below="@+id/creditCardNumberLabel"
+ android:layout_marginTop="20dp"
+ android:importantForAutofill="no"
+ android:text="@string/credit_card_expiration_label" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/creditCardExpirationLabel"
+ android:layout_alignStart="@+id/creditCardNumberField"
+ android:orientation="horizontal">
+
+ <Spinner
+ android:id="@+id/expirationDay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints="creditCardExpirationDay" />
+
+ <Spinner
+ android:id="@+id/expirationMonth"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints="creditCardExpirationMonth" />
+
+ <Spinner
+ android:id="@+id/expirationYear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:autofillHints="creditCardExpirationYear" />
+ </LinearLayout>
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/clear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Clear" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Submit" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/list_item.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/list_item.xml
new file mode 100644
index 00000000..d01bc37d
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/list_item.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/text1"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#ffffffff"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall" />
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/login_activity.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/login_activity.xml
new file mode 100644
index 00000000..6382fe75
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/login_activity.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/standardLoginLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center">
+
+ <TextView
+ android:id="@+id/usernameLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="20dp"
+ android:importantForAutofill="no"
+ android:text="@string/username_label" />
+
+ <EditText
+ android:id="@+id/usernameField"
+ android:layout_width="200sp"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/usernameLabel"
+ android:layout_toEndOf="@id/usernameLabel"
+ android:autofillHints="username"
+ android:inputType="textPersonName" />
+
+ <TextView
+ android:id="@+id/passwordLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@+id/usernameLabel"
+ android:layout_below="@+id/usernameLabel"
+ android:layout_marginTop="20dp"
+ android:importantForAutofill="no"
+ android:text="@string/password_label" />
+
+ <EditText
+ android:id="@+id/passwordField"
+ android:layout_width="200sp"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/passwordLabel"
+ android:layout_alignStart="@+id/usernameField"
+ android:autofillHints="password"
+ android:inputType="textPassword" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/clear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Clear" />
+
+ <Button
+ android:id="@+id/login"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Login" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_activity.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_activity.xml
new file mode 100644
index 00000000..3704e40f
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<android.support.v4.widget.NestedScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbarStyle="insideOverlay"
+ android:scrollbars="vertical"
+ android:importantForAutofill="no">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/spacing_large"
+ android:paddingTop="@dimen/spacing_large">
+ <TextView
+ style="@style/Settings.Header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/settings_authentication_header"/>
+
+ <LinearLayout
+ android:id="@+id/settings_auth_responses_container"
+ style="@style/Settings.Container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/settings_auth_responses_label"
+ style="@style/Settings.Label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:importantForAccessibility="no"
+ android:text="@string/settings_authenticate_responses" />
+
+ <Switch
+ android:id="@+id/settings_auth_responses_switch"
+ style="@style/Settings.Switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/a11y_min_touch_target_dimen"
+ android:layout_marginStart="@dimen/padding_normal" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/settings_auth_datasets_container"
+ style="@style/Settings.Container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/settings_auth_datasets_label"
+ style="@style/Settings.Label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:importantForAccessibility="no"
+ android:text="@string/settings_authenticate_datasets" />
+
+ <Switch
+ android:id="@+id/settings_auth_datasets_switch"
+ style="@style/Settings.Switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/a11y_min_touch_target_dimen"
+ android:layout_marginStart="@dimen/padding_normal" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/settings_auth_credentials_container"
+ style="@style/Settings.Container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/settings_auth_credentials_label"
+ style="@style/Settings.Label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/settings_auth_credentials_label" />
+
+ <ImageView
+ android:id="@+id/settings_auth_credentials_icon"
+ style="@style/Settings.Switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/a11y_min_touch_target_dimen"
+ android:layout_marginStart="@dimen/padding_normal"
+ android:src="@drawable/ic_person_black_24dp"/>
+ </LinearLayout>
+
+ <TextView
+ style="@style/Settings.Header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/spacing_normal"
+ android:text="@string/settings_data_header"/>
+
+ <LinearLayout
+ android:id="@+id/settings_clear_data_container"
+ style="@style/Settings.Container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/settings_clear_data_label"
+ style="@style/Settings.Label"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/settings_clear_data_label" />
+
+ <ImageView
+ android:id="@+id/settings_clear_data_icon"
+ style="@style/Settings.Switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/a11y_min_touch_target_dimen"
+ android:layout_marginStart="@dimen/padding_normal"
+ android:src="@drawable/ic_delete_forever_black_24dp"/>
+ </LinearLayout>
+ </LinearLayout>
+
+</android.support.v4.widget.NestedScrollView>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_authentication_dialog.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_authentication_dialog.xml
new file mode 100644
index 00000000..93ac6dd1
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/settings_authentication_dialog.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<EditText xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/master_password_field"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="@dimen/spacing_normal"
+ android:importantForAutofill="no"
+ android:padding="@dimen/spacing_normal">
+
+ <requestFocus />
+</EditText>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/virtual_login_activity.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/virtual_login_activity.xml
new file mode 100644
index 00000000..59f56e1d
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/virtual_login_activity.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:weightSum="100">
+
+ <com.example.android.autofillframework.app.CustomVirtualView
+ android:id="@+id/custom_view"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="50" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="20dp"
+ android:gravity="center"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/clear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Clear" />
+
+ <Button
+ android:id="@+id/login"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Login" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/welcome_activity.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/welcome_activity.xml
new file mode 100644
index 00000000..4d746c53
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/layout/welcome_activity.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ android:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/welcome_text" />
+</FrameLayout> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 00000000..93465c02
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 00000000..6d5c2bb9
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 00000000..72ecd6c7
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 00000000..698fd4c5
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 00000000..b64a676b
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml
new file mode 100644
index 00000000..22074a2b
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-dimens.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_huge</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml
new file mode 100644
index 00000000..03d19741
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-sw600dp/template-styles.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceLarge</item>
+ <item name="android:lineSpacingMultiplier">1.2</item>
+ <item name="android:shadowDy">-6.5</item>
+ </style>
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml
new file mode 100644
index 00000000..8c1ea66f
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v11/template-styles.xml
@@ -0,0 +1,22 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Activity themes -->
+ <style name="Theme.Base" parent="android:Theme.Holo.Light" />
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml
new file mode 100644
index 00000000..8b6ec3f8
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-colors.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml
new file mode 100644
index 00000000..c778e4f9
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values-v21/base-template-styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+
+ <!-- Activity themes -->
+ <style name="Theme.Base" parent="android:Theme.Material.Light">
+ </style>
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml
new file mode 100644
index 00000000..ddf8b07d
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/base-strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="app_name">AutofillFramework</string>
+ <string name="intro_message">
+ <![CDATA[
+
+
+ This sample app demos the Autofill feature introduced in Android O.
+
+
+ ]]>
+ </string>
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/dimens.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..961725d5
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/dimens.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="spacing_normal">8dp</dimen>
+ <dimen name="spacing_micro">4dp</dimen>
+ <dimen name="padding_normal">16dp</dimen>
+ <dimen name="padding_normal_button">12dp</dimen>
+ <dimen name="spacing_large">32dp</dimen>
+ <dimen name="a11y_min_touch_target_dimen">48dp</dimen>
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
new file mode 100644
index 00000000..b5611ae1
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<resources>
+ <string name="settings_name">Autofill Settings</string>
+ <string name="username_label">Username</string>
+ <string name="password_label">Password</string>
+ <string name="welcome_text">You have successfully signed in!</string>
+ <string name="standard_view_sign_in">Sign in using standard views</string>
+ <string name="virtual_view_sign_in">Sign in using virtual views</string>
+ <string name="credit_card_checkout">Credit Card check out</string>
+ <string name="autofill_sign_in_prompt">Tap to sign in.</string>
+ <string name="credit_card_number_label">CC Number</string>
+ <string name="credit_card_expiration_label">CC Exp</string>
+ <string name="credit_card_security_code_label">CC Security Code</string>
+ <string name="settings_cancel">Cancel</string>
+ <string name="settings_save">Save</string>
+ <string name="settings_authenticate_responses">Authenticate responses</string>
+ <string name="settings_authenticate_datasets">Authenticate datasets</string>
+ <string name="settings_clear_data_label">Clear all data (including credentials)</string>
+ <string name="settings_clear_data_confirmation">Are you sure you want to delete all autofill
+ data from the sample service?
+ </string>
+ <string name="settings_clear_data_confirmation_title">Confirmation</string>
+ <string name="ok">OK</string>
+ <string name="cancel">Cancel</string>
+ <string name="settings_authentication_header">Authentication</string>
+ <string name="settings_data_header">Data</string>
+ <string name="settings_auth_credentials_label">Update credentials</string>
+ <string name="settings_auth_enter_current_password">Enter current password</string>
+ <string name="settings_auth_enter_new_password">Enter new password</string>
+ <string name="settings_auth_change_credentials_title">Change credentials</string>
+ <string-array name="month_array">
+ <item>Jan</item>
+ <item>Feb</item>
+ <item>Mar</item>
+ <item>Apr</item>
+ <item>May</item>
+ <item>Jun</item>
+ <item>Jul</item>
+ <item>Aug</item>
+ <item>Sep</item>
+ <item>Oct</item>
+ <item>Nov</item>
+ <item>Dec</item>
+ </string-array>
+
+ <string-array name="day_array">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ <item>4</item>
+ <item>5</item>
+ <item>6</item>
+ <item>7</item>
+ <item>8</item>
+ <item>9</item>
+ <item>10</item>
+ <item>11</item>
+ <item>12</item>
+ <item>13</item>
+ <item>14</item>
+ <item>15</item>
+ <item>16</item>
+ <item>17</item>
+ <item>18</item>
+ <item>19</item>
+ <item>20</item>
+ <item>21</item>
+ <item>22</item>
+ <item>23</item>
+ <item>24</item>
+ <item>25</item>
+ <item>26</item>
+ <item>27</item>
+ <item>28</item>
+ <item>30</item>
+ <item>31</item>
+ </string-array>
+
+ <string-array name="year_array">
+ <item>2017</item>
+ <item>2018</item>
+ <item>2019</item>
+ <item>2020</item>
+ <item>2021</item>
+ <item>2022</item>
+ <item>2023</item>
+ <item>2024</item>
+ </string-array>
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/styles.xml
new file mode 100644
index 00000000..92aabaa6
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/styles.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+<resources>
+ <style name="Settings.Label" parent="">
+ <item name="android:ellipsize">end</item>
+ <item name="android:lines">1</item>
+ <item name="android:paddingBottom">@dimen/spacing_normal</item>
+ <item name="android:paddingTop">@dimen/spacing_normal</item>
+ </style>
+ <style name="Settings.Container" parent="">
+ <item name="android:background">?android:selectableItemBackground</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:minHeight">?android:listPreferredItemHeightSmall</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:paddingEnd">?android:listPreferredItemPaddingEnd</item>
+ <item name="android:paddingStart">?android:listPreferredItemPaddingStart</item>
+ </style>
+ <style name="Settings.Switch" parent="">
+ <!-- We make the parent view clickable instead for better touch feedback -->
+ <item name="android:background">@null</item>
+ <item name="android:clickable">false</item>
+ </style>
+
+ <style name="Settings.Header" parent="@style/TextAppearance.AppCompat.Large">
+ <item name="android:paddingStart">?android:listPreferredItemPaddingStart</item>
+ <item name="android:paddingBottom">@dimen/spacing_normal</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml
new file mode 100644
index 00000000..39e710b5
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-dimens.xml
@@ -0,0 +1,32 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Define standard dimensions to comply with Holo-style grids and rhythm. -->
+
+ <dimen name="margin_tiny">4dp</dimen>
+ <dimen name="margin_small">8dp</dimen>
+ <dimen name="margin_medium">16dp</dimen>
+ <dimen name="margin_large">32dp</dimen>
+ <dimen name="margin_huge">64dp</dimen>
+
+ <!-- Semantic definitions -->
+
+ <dimen name="horizontal_page_margin">@dimen/margin_medium</dimen>
+ <dimen name="vertical_page_margin">@dimen/margin_medium</dimen>
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml
new file mode 100644
index 00000000..6e7d593d
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/template-styles.xml
@@ -0,0 +1,42 @@
+<!--
+ Copyright 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <!-- Activity themes -->
+
+ <style name="Theme.Base" parent="android:Theme.Light" />
+
+ <style name="Theme.Sample" parent="Theme.Base" />
+
+ <style name="AppTheme" parent="Theme.Sample" />
+ <!-- Widget styling -->
+
+ <style name="Widget" />
+
+ <style name="Widget.SampleMessage">
+ <item name="android:textAppearance">?android:textAppearanceMedium</item>
+ <item name="android:lineSpacingMultiplier">1.1</item>
+ </style>
+
+ <style name="Widget.SampleMessageTile">
+ <item name="android:background">@drawable/tile</item>
+ <item name="android:shadowColor">#7F000000</item>
+ <item name="android:shadowDy">-3.5</item>
+ <item name="android:shadowRadius">2</item>
+ </style>
+
+</resources>
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/xml/autofill_service.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/xml/autofill_service.xml
new file mode 100644
index 00000000..bc026e50
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/xml/autofill_service.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+-->
+
+<!--
+Attributes for the AutoFill service that tell the framework what will act as the Autofill service's
+Settings Activity. This is pointed to in the service's meta-data in the application's manifest.
+-->
+<autofill-service
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:settingsActivity="com.example.android.autofillframework.service.SettingsActivity"/> \ No newline at end of file