diff options
author | Douglas Sigelbaum <sigelbaum@google.com> | 2017-05-16 22:31:36 -0700 |
---|---|---|
committer | Douglas Sigelbaum <sigelbaum@google.com> | 2017-05-17 00:35:21 -0700 |
commit | 08a3592720a23e56c9546cf89abf707372f5abfb (patch) | |
tree | 603afca306120848ce00f4cbbc5be26859cfa358 | |
parent | 8fa30fc0cbc6a7e2da9749e66373d70a593fd1ce (diff) | |
download | android-08a3592720a23e56c9546cf89abf707372f5abfb.tar.gz |
Initial kotlin commit for client app in the autofill sample.
Bug: 38182790
Test: manual
Change-Id: Ib76173768461528f52297a7acd2f40d532b14d0a
70 files changed, 3555 insertions, 0 deletions
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/build.gradle b/input/autofill/AutofillFramework/kotlinApp/Application/build.gradle new file mode 100644 index 00000000..43771dd8 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/build.gradle @@ -0,0 +1,62 @@ + +buildscript { + repositories { + jcenter() + } + ext.kotlin_version = '1.1.2-3' + dependencies { + classpath 'com.android.tools.build:gradle:2.2.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +repositories { + jcenter() +} + +dependencies { + compile "com.android.support:support-v4:25.0.1" + compile "com.android.support:support-v13:25.0.1" + compile "com.android.support:cardview-v7:25.0.1" + compile "com.android.support:appcompat-v7:25.0.1" + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" +} + +// The sample build uses multiple directories to +// keep boilerplate and common code separate from +// the main sample code. +List<String> dirs = [ + 'main', // main sample code; look here for the interesting stuff. + 'common', // components that are reused by multiple samples + 'template'] // boilerplate code that is generated by the sample template process + +android { + compileSdkVersion "android-O" + buildToolsVersion "25.0.2" + + defaultConfig { + minSdkVersion "O" + targetSdkVersion "O" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + sourceSets { + main { + dirs.each { dir -> + java.srcDirs "src/${dir}/java" + res.srcDirs "src/${dir}/res" + } + } + androidTest.setRoot('tests') + androidTest.java.srcDirs = ['tests/src'] + + } + +} 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 Binary files differnew file mode 100644 index 00000000..b1efaf4b --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000..13586288 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-hdpi/tile.9.png 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 Binary files differnew file mode 100644 index 00000000..f5f9244f --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-mdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000..5d07b3f0 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xhdpi/ic_launcher.png 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 Binary files differnew file mode 100644 index 00000000..6ef21e1f --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/drawable-xxhdpi/ic_launcher.png 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 Binary files differnew file mode 100755 index 00000000..93465c02 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-hdpi/ic_launcher.png 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 Binary files differnew file mode 100755 index 00000000..6d5c2bb9 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-mdpi/ic_launcher.png 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 Binary files differnew file mode 100755 index 00000000..72ecd6c7 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xhdpi/ic_launcher.png 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 Binary files differnew file mode 100755 index 00000000..698fd4c5 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxhdpi/ic_launcher.png 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 Binary files differnew file mode 100755 index 00000000..b64a676b --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/mipmap-xxxhdpi/ic_launcher.png 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 diff --git a/input/autofill/AutofillFramework/kotlinApp/build.gradle b/input/autofill/AutofillFramework/kotlinApp/build.gradle new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/build.gradle diff --git a/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.jar b/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..8c0fb64a --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.jar diff --git a/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties b/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..eaba3011 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Apr 10 15:27:10 PDT 2013 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip diff --git a/input/autofill/AutofillFramework/kotlinApp/gradlew b/input/autofill/AutofillFramework/kotlinApp/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/input/autofill/AutofillFramework/kotlinApp/gradlew.bat b/input/autofill/AutofillFramework/kotlinApp/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/1_HomePage.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/1_HomePage.png Binary files differnew file mode 100644 index 00000000..edb54ee5 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/1_HomePage.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/2_StandardViewAutofillable.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/2_StandardViewAutofillable.png Binary files differnew file mode 100644 index 00000000..cce86428 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/2_StandardViewAutofillable.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/3_StandardViewAutofilled.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/3_StandardViewAutofilled.png Binary files differnew file mode 100644 index 00000000..b53efa07 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/3_StandardViewAutofilled.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/4_WelcomeActivity.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/4_WelcomeActivity.png Binary files differnew file mode 100644 index 00000000..1d61a554 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/4_WelcomeActivity.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/5_CustomViewAutofillable.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/5_CustomViewAutofillable.png Binary files differnew file mode 100644 index 00000000..7e0895b4 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/5_CustomViewAutofillable.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/6_CustomViewAutofilled.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/6_CustomViewAutofilled.png Binary files differnew file mode 100644 index 00000000..793816ec --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/6_CustomViewAutofilled.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/7_SettingsActivity.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/7_SettingsActivity.png Binary files differnew file mode 100644 index 00000000..187c0b33 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/7_SettingsActivity.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/8_AuthNeeded.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/8_AuthNeeded.png Binary files differnew file mode 100644 index 00000000..e3b4f922 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/8_AuthNeeded.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/9_AuthActivity.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/9_AuthActivity.png Binary files differnew file mode 100644 index 00000000..905193e4 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/9_AuthActivity.png diff --git a/input/autofill/AutofillFramework/kotlinApp/screenshots/icon-web.png b/input/autofill/AutofillFramework/kotlinApp/screenshots/icon-web.png Binary files differnew file mode 100644 index 00000000..29fb3ff9 --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/screenshots/icon-web.png diff --git a/input/autofill/AutofillFramework/kotlinApp/settings.gradle b/input/autofill/AutofillFramework/kotlinApp/settings.gradle new file mode 100644 index 00000000..0a5c310b --- /dev/null +++ b/input/autofill/AutofillFramework/kotlinApp/settings.gradle @@ -0,0 +1,2 @@ + +include 'Application' |