diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2017-05-25 18:28:10 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-05-25 18:28:11 +0000 |
commit | fcf34d86a8df8c432b600800544cf81ea35e7b6b (patch) | |
tree | 464d560ac2a2250bf180a069226da610b462d73c /input/autofill | |
parent | 393d1311916ab9655f6422bebc82a1dfaf835ef3 (diff) | |
parent | 01218aae71dd295c2ff604af14aecf73e7bd7c08 (diff) | |
download | android-fcf34d86a8df8c432b600800544cf81ea35e7b6b.tar.gz |
Merge "Updated Autofill sample description in readme." into oc-dev
Diffstat (limited to 'input/autofill')
2 files changed, 112 insertions, 74 deletions
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java index 085f827c..dc09de53 100644 --- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java +++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/app/CustomVirtualView.java @@ -84,17 +84,19 @@ public class CustomVirtualView extends View { mLineLength = mTextHeight + mVerticalGap; mTextPaint.setTextSize(mTextHeight); mUsernameLine = addLine("usernameField", context.getString(R.string.username_label), - new String[] {View.AUTOFILL_HINT_USERNAME}, " ", true); + new String[]{View.AUTOFILL_HINT_USERNAME}, " ", true); mPasswordLine = addLine("passwordField", context.getString(R.string.password_label), - new String[] {View.AUTOFILL_HINT_PASSWORD}, " ", false); + new String[]{View.AUTOFILL_HINT_PASSWORD}, " ", false); Log.d(TAG, "Text height: " + mTextHeight); } @Override public void autofill(SparseArray<AutofillValue> values) { - // User has just selected a Dataset from the list of Autofill suggestions and the Dataset's - // AutofillValue gets passed into this method. + // 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. Log.d(TAG, "autoFill(): " + values); for (int i = 0; i < values.size(); i++) { final int id = values.keyAt(i); @@ -114,14 +116,18 @@ public class CustomVirtualView extends View { postInvalidate(); } + @Override public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { - // Build a ViewStructure to pack in AutoFillService requests. + // Build a ViewStructure that will get passed to the AutofillService by the framework + // when it is time to find autofill suggestions. structure.setClassName(getClass().getName()); int childrenSize = mItems.size(); Log.d(TAG, "onProvideAutofillVirtualStructure(): flags = " + flags + ", items = " + childrenSize + ", extras: " + bundleToString(structure.getExtras())); int 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 (int i = 0; i < childrenSize; i++) { Item item = mItems.valueAt(i); Log.d(TAG, "Adding new child at index " + index + ": " + item); diff --git a/input/autofill/AutofillFramework/template-params.xml b/input/autofill/AutofillFramework/template-params.xml index c9d8b07e..44d36fd0 100644 --- a/input/autofill/AutofillFramework/template-params.xml +++ b/input/autofill/AutofillFramework/template-params.xml @@ -97,9 +97,8 @@ <description> <![CDATA[ This sample demonstrates the use of the Autofill Framework. It includes implementations of client -Activities that want to be autofilled, and a Service that can provide autofill data to client -Activities. For simplicity, this sample's service uses mock data to autofill what it thinks are -username and password text fields. +Activities with views that should be autofilled, and a Service that can provide autofill data to +client Activities. ]]> </description> @@ -109,54 +108,88 @@ username and password text fields. <intro> <![CDATA[ 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. However, all apps targeting O with any autofillable -fields should follow the necessary steps to ensure their Views can be autofilled. 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 text -fields that it has data for. If such text fields exist in the hierarchy, the service sends data -suggestions to the client to fill in those text fields. In this basic sample, it will only look for -views whose resource IDs are "usernameField" and "passwordField" and will send mock credential data -accordingly. A real Autofill service would attempt to autofill more than just login credentials and -would be able to fill in other view types in addition to text fields (e.g. spinners, checkboxes, -etc.). It would also use more advanced heuristics to determine what data to send to which views. - -To set the device's default Autofill service to the one in the sample, edit -**Settings** > **Apps & Notifications** > **Default Apps** > **Auto-fill app** and select the -sample app. To edit the service's settings, 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 datasets. You can also set the number of mock datasets that are sent to the client app. - -The client side of the app has two Activities that each have a username field and a password field. -One of the Activities uses standard views and the other Activity uses a custom view with virtual -children. The standard views do not require any extra code to allow autofill. The following code -example shows the `View` method you have to override in order to provide view hierarchy data to the -Autofill service. This is triggered when the `View` goes into focus and Android invokes an Autofill -request. +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: + +```java +class CustomVirtualView { +... + // Cache AutofillManager system service + mAutofillManager = context.getSystemService(AutofillManager.class); +... + // Notify service which virtual view has come into focus. + mAutofillManager.notifyViewEntered(CustomVirtualView.this, id, absBounds); +... + // Notify service that a virtual view has left focus. + mAutofillManager.notifyViewExited(CustomVirtualView.this, 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. ```java -/* -This method is responsible for building the ViewStructure that gets passed to the AutoFillService -by the framework when it is time to find Autofill suggestions. To do this, it should traverse -through its view hierarchy and add views to the ViewStructure on the way. -*/ @Override -public void onProvideAutoFillVirtualStructure(ViewStructure structure, int flags) { - structure.setClassName(getClass().getName()); - int childrenSize = mItems.size(); - int index = structure.addChildCount(childrenSize); - for (int i = 0; i < childrenSize; i++) { - Item item = mItems.valueAt(i); - ViewStructure child = structure.newChild(index, item.id, flags); - child.setSanitized(item.sanitized); - child.setText(item.text); - child.setAutoFillValue(AutoFillValue.forText(item.text)); - child.setFocused(item.line.focused); - child.setId(item.id, getContext().getPackageName(), null, item.line.idEntry); - index++; - } +public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) { + // Build a ViewStructure that will get passed to the AutofillService by the framework + // when it is time to find autofill suggestions. + structure.setClassName(getClass().getName()); + int childrenSize = mItems.size(); + int 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 (int i = 0; i < childrenSize; i++) { + Item item = mItems.valueAt(i); + ViewStructure child = structure.newChild(index); + child.setAutofillId(structure, item.id); + child.setAutofillHints(item.hints); + child.setAutofillType(item.type); + child.setDataIsSensitive(!item.sanitized); + child.setText(item.text); + child.setAutofillValue(AutofillValue.forText(item.text)); + child.setFocused(item.focused); + child.setId(item.id, getContext().getPackageName(), null, item.line.idEntry); + child.setClassName(item.getClassName()); + index++; + } } ``` @@ -164,30 +197,29 @@ After the service processes the Autofill request and sends back a series of Auto (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. +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. ```java -/* -User has just selected a Dataset from the list of Autofill suggestions and the Dataset's -AutoFillValue gets passed into this method. This method updates the UI based on the data -in the AutoFillValue. -*/ @Override -public void autoFillVirtual(int id, AutoFillValue value) { - Item item = mItems.get(id); - if (item == null) { - // ID not recognized so no-op. - return; - } - if (!item.editable) { - // Component is not editable so no-op. - return; - } - // Set the virtual child view's text to the text wrapped in the AutoFillValue. - item.text = value.getTextValue(); - postInvalidate(); +public void autofill(SparseArray<AutofillValue> values) { + // 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 (int i = 0; i < values.size(); i++) { + final int id = values.keyAt(i); + final AutofillValue value = values.valueAt(i); + final Item item = mItems.get(id); + if (item == null || !item.editable) { + // Component either not found or is not editable, so no-op. + return; + } + // Set the item's text to the text wrapped in the AutofillValue. + item.text = value.getTextValue(); + } + postInvalidate(); } ``` ]]> |