186 lines
9.8 KiB
Markdown
186 lines
9.8 KiB
Markdown
|
Warning: This sample is currently outdated and you should reference the Java version instead.
|
||
|
============================================================================================
|
||
|
|
||
|
Android AutofillFramework Sample (Kotlin)
|
||
|
=========================================
|
||
|
|
||
|
This sample demonstrates the use of the Autofill Framework. It includes implementations of client
|
||
|
Activities with views that should be autofilled, and a Service that can provide autofill data to
|
||
|
client Activities.
|
||
|
|
||
|
Maintainer's Note
|
||
|
------------------
|
||
|
**IMPORTANT:** The Kotlin version of this sample is temporarily out of date. Until this is corrected, you should reference the Java version instead.
|
||
|
|
||
|
Introduction
|
||
|
------------
|
||
|
|
||
|
This sample demonstrates the use of the Autofill framework from the service side and the client
|
||
|
side. In practice, only a small handful of apps will develop Autofill services because a device
|
||
|
will only have one service as default at a time, and there is just a small number of 3rd-party apps
|
||
|
providing these services (typically password managers). However, all apps targeting O with any
|
||
|
autofillable fields should follow the necessary steps to 1) ensure their views can be autofilled
|
||
|
and 2) optimize their autofill performance. Most of the time, there is little to no extra code
|
||
|
involved, but the use of custom views and views with virtual child views requires more work.
|
||
|
|
||
|
The sample's Autofill service is implemented to parse the client's view hierarchy in search of
|
||
|
autofillable fields that it has data for. If such fields exist in the hierarchy, the service sends
|
||
|
data suggestions to the client to autofill those fields. The client uses the following attributes
|
||
|
to specify autofill properties: `importantForAutofill`, `autofillHints`, and `autofillType`.
|
||
|
`importantForAutofill` specifies whether the view is autofillable. `autofillHints` is a list of
|
||
|
strings that hint to the service **what** data to fill the view with. This sample service only
|
||
|
supports the hints listed [here](https://developer.android.com/reference/android/view/View.html#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE)
|
||
|
with the prefix AUTOFILL_HINT_*. `autofillType` tells the service the type of data it expects to
|
||
|
receive (i.e. a list index, a date, or a string). Specifying `autofillType` is only necessary
|
||
|
when implementing a custom view since all of the provided widgets in the UI toolkit do this for you.
|
||
|
|
||
|
To set the device's default Autofill service to the one in the sample, edit **Settings** >
|
||
|
**System** > **Languages & Input** > **Advanced** > **Auto-fill service** and select the sample
|
||
|
app. To edit the service's settings, tap the settings icon next to the **Auto-fill service** list
|
||
|
item or open the **Autofill Settings** launcher icon.. Here, you can set whether you want to enable
|
||
|
authentication on the entire autofill Response or just on individual autofill datasets. You should
|
||
|
also set the master password to “unlock” authenticated autofill data with.
|
||
|
|
||
|
**Note:** This sample service stores all autofill data in SharedPreferences and thus is not secure.
|
||
|
Be careful about what you store when experimenting with the sample because anyone with root access
|
||
|
to your device will be able to view your autofill data.
|
||
|
|
||
|
The client side of the app has three Activities that each have autofillable fields. The first
|
||
|
Activity uses standard views to comprise a login form. Very little needs to be done by the client
|
||
|
app to ensure the views get autofilled properly. The second Activity uses a custom view with
|
||
|
virtual children, meaning some autofillable child views are not known to the View hierarchy to be
|
||
|
child views. Supporting autofill on these child views is a little more involved.
|
||
|
|
||
|
The following code snippet shows how to signal to the autofill service that a specific
|
||
|
autofillable virtual view has come into focus:
|
||
|
|
||
|
```kotlin
|
||
|
class CustomVirtualView(context: Context, attrs: AttributeSet) : View(context, attrs) {
|
||
|
...
|
||
|
// Cache AutofillManager system service
|
||
|
private val autofillManager: AutofillManager = context.getSystemService(AutofillManager::class.java)
|
||
|
...
|
||
|
// Notify service which virtual view has come into focus.
|
||
|
autofillManager.notifyViewEntered(this@CustomVirtualView, id, absBounds)
|
||
|
...
|
||
|
// Notify service that a virtual view has left focus.
|
||
|
autofillManager.notifyViewExited(this@CustomVirtualView, id)
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Now that the autofillable view has signaled to the service that it has been autofilled, it needs
|
||
|
to provide the virtual view hierarchy to the Autofill service. This is done out of the box for
|
||
|
views part of the UI toolkit, but you need to implement this yourself if you have the view has
|
||
|
virtual child views. The following code example shows the `View` method you have to override in
|
||
|
order to provide this view hierarchy data to the Autofill service.
|
||
|
|
||
|
```kotlin
|
||
|
override fun onProvideAutofillVirtualStructure(structure: ViewStructure, flags: Int) {
|
||
|
// Build a ViewStructure to pack in AutoFillService requests.
|
||
|
structure.setClassName(javaClass.name)
|
||
|
val childrenSize = items.size()
|
||
|
Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags + ", items = "
|
||
|
+ childrenSize + ", extras: " + bundleToString(structure.extras))
|
||
|
var index = structure.addChildCount(childrenSize)
|
||
|
// Traverse through the view hierarchy, including virtual child views. For each view, we
|
||
|
// need to set the relevant autofill metadata and add it to the ViewStructure.
|
||
|
for (i in 0..childrenSize - 1) {
|
||
|
val item = items.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++
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
After the service processes the Autofill request and sends back a series of Autofill `Datasets`
|
||
|
(wrapped in a `Response` object), the user can pick which `Dataset` they want to autofill their
|
||
|
views with. When a `Dataset` is selected, this method is invoked for all of the views that were
|
||
|
associated with that `Dataset` by the service. For example, the `Dataset` might contain Autofill
|
||
|
values for username, password, birthday, and address. This method would then be invoked on all
|
||
|
four of those fields. The following code example shows how the sample app implements the method
|
||
|
to deliver a UI update to the appropriate child view after the user makes their selection.
|
||
|
|
||
|
```kotlin
|
||
|
override fun autofill(values: SparseArray<AutofillValue>) {
|
||
|
// User has just selected a Dataset from the list of autofill suggestions.
|
||
|
// The Dataset is comprised of a list of AutofillValues, with each AutofillValue meant
|
||
|
// to fill a specific autofillable view. Now we have to update the UI based on the
|
||
|
// AutofillValues in the list.
|
||
|
for (i in 0..values.size() - 1) {
|
||
|
val id = values.keyAt(i)
|
||
|
val value = values.valueAt(i)
|
||
|
items[id]?.let { item ->
|
||
|
if (item.editable) {
|
||
|
// Set the item's text to the text wrapped in the AutofillValue.
|
||
|
item.text = value.textValue
|
||
|
} else {
|
||
|
// Component not editable, so no-op.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
postInvalidate()
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Pre-requisites
|
||
|
--------------
|
||
|
|
||
|
- Android SDK Preview O
|
||
|
- Android Studio 3.0+
|
||
|
- Android Build Tools v26+
|
||
|
- Android Support Repository v26+
|
||
|
- Gradle v3.0+
|
||
|
|
||
|
Screenshots
|
||
|
-------------
|
||
|
|
||
|
<img src="screenshots/1_MainPage.png" height="400" alt="Screenshot"/> <img src="screenshots/2_SampleLoginEditTexts.png" height="400" alt="Screenshot"/> <img src="screenshots/3_SampleLoginEditTextsAutofilled.png" height="400" alt="Screenshot"/> <img src="screenshots/4_WelcomeActivity.png" height="400" alt="Screenshot"/> <img src="screenshots/5_SampleLoginCustomVirtualView.png" height="400" alt="Screenshot"/> <img src="screenshots/6_SampleLoginCustomVirtualViewAutofilled.png" height="400" alt="Screenshot"/> <img src="screenshots/7_SampleCheckOutSpinnersAutofillable.png" height="400" alt="Screenshot"/> <img src="screenshots/8_SampleCheckOutSpinnersAutofilled.png" height="400" alt="Screenshot"/> <img src="screenshots/9_SettingsActivity.png" height="400" alt="Screenshot"/> <img src="screenshots/10_AuthNeeded.png" height="400" alt="Screenshot"/> <img src="screenshots/11_AuthActivity.png" height="400" alt="Screenshot"/>
|
||
|
|
||
|
Getting Started
|
||
|
---------------
|
||
|
|
||
|
This sample uses the Gradle build system. To build this project, use the
|
||
|
"gradlew build" command or use "Import Project" in Android Studio.
|
||
|
|
||
|
Support
|
||
|
-------
|
||
|
|
||
|
- Google+ Community: https://plus.google.com/communities/105153134372062985968
|
||
|
- Stack Overflow: http://stackoverflow.com/questions/tagged/android
|
||
|
|
||
|
If you've found an error in this sample, please file an issue:
|
||
|
https://github.com/googlesamples/android-AutofillFramework
|
||
|
|
||
|
Patches are encouraged, and may be submitted by forking this project and
|
||
|
submitting a pull request through GitHub. Please see CONTRIBUTING.md for more details.
|
||
|
|
||
|
License
|
||
|
-------
|
||
|
|
||
|
Copyright 2017 The Android Open Source Project, Inc.
|
||
|
|
||
|
Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||
|
license agreements. See the NOTICE file distributed with this work for
|
||
|
additional information regarding copyright ownership. The ASF licenses this
|
||
|
file to you 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.
|