Auto form fill, often shortened to just autofill, is a feature browsers have supported for years now. Most of us use it all the time. I, for one, find it indispensable during tasks such as filling out a registration form or completing a checkout process.
The latest release of Android, Android O, brings similar functionality to Android apps. In other words, Android can now help users fill out forms that belong to all the apps they have installed on their devices. This was a much-awaited feature because typing with a virtual keyboard on a small screen tends to be quite a hassle.
As an app developer, you can use the new Autofill Framework to create your own custom autofill service, a service that decides how to populate an app’s input fields. In this tutorial, I’ll show you how.
Fire up Android Studio and create a new project with an empty activity. You must, of course, remember to choose Android 7+ in the Target Android Devices dialog.
This project will need a few widgets that belong to the Design Support Library, so open the app module’s build.gradle file and add the following compile dependency to it:
compile 'com.android.support:design:26.+'
Lastly, press the Sync Now button to update the project.
2. Create a Settings Activity
In this tutorial, we’ll be creating an app containing a very simple autofill service that targets only those input fields where the user is expected to type in an email address. Because almost every other app on Google Play today asks for an email address, this service will be quite useful.
Our service obviously needs to know what the user’s email addresses are. Therefore, let us now build an activity where the user can type in and save two email addresses.
Step 1: Define the Layout
As you might expect, the layout of the activity will contain two EditText widgets where the user can type in his or her email addresses. If you want it to adhere to the guidelines of Material Design, placing the EditText widgets inside TextInputLayout containers is a good idea.
Additionally, the layout must have a Button widget the user can press to save the email addresses.
You are free to place the widgets anywhere you want. Nevertheless, for now, I suggest you place them all inside a LinearLayout whose orientation is vertical.
In the above code, you can see that the Button widget has an onClick attribute pointing to a method. Click on the yellow light bulb beside this attribute in Android Studio to generate a stub for it in the associated Activity class.
public void saveEmailAddresses(View view) {
// More code will be added here
}
Step 2: Save the Email Addresses
We’ll be using a shared preferences file called EMAIL_STORAGE to save our data. You can use the getSharedPreferences() method of your Activity class to access the file. Additionally, to be able to write to the file, you must call its edit() method, which generates a SharedPreferences.Editor object.
Accordingly, add the following code inside the saveEmailAddresses() method:
To fetch the email addresses the user has typed into the EditText widgets, you’ll have to first get references to them using the findViewById() method, and then call their getText() methods.
At this point, you can call the putString() method of the editor to add the email addresses to the preferences file as two key value pairs. After you do so, don’t forget to call the commit() method to make your changes permanent.
The settings activity we created in the previous step is currently just an ordinary activity. To let the Android platform know that it is a settings activity for an autofill service, we must create a meta-data XML file saying so.
Create a new XML file called email_address_filler.xml in the project’s res/xml folder. Inside it, add an <autofill-service> tag and set the value of its settingsActivity attribute to the name of your Activity class.
You can now run the app, type in two email addresses, and press the Save button to save them.
3. Create an Autofill Service
Any class that extends the abstract AutoFillService class can serve as an autofill service. So start by creating a new Java class with File > New > Java Class. In the dialog that pops up, name the class EmailAddressFiller and make sure that you set the value of the Superclass field to AutoFillService.
Android Studio will now prompt you to generate stubs for two abstract methods: onSaveRequest() and onFillRequest(). In this tutorial, we’ll be focusing only the onFillRequest() method, which is automatically called whenever the user opens an activity—of any app—containing input fields.
@Override
public void onFillRequest(AssistStructure assistStructure,
Bundle bundle,
CancellationSignal cancellationSignal,
FillCallback fillCallback) {
// More code goes here
}
Step 1: Analyze View Hierarchies
An autofill service needs to analyze an app’s user interface and identify input fields it can fill. That’s why the onFillRequest() method receives an AssistStructure object, which contains details about all the widgets that are currently visible on the screen. More precisely, it contains a tree of ViewNode objects.
If you’ve never seen such a tree, I suggest you use the uiautomatorviewer tool, which is part of the Android SDK, to analyze the layout hierarchies of a few apps. For example, here’s what the layout hierarchy of Android’s default mail app looks like:
Naturally, to analyze all nodes of a tree, you need a recursive method. Let’s create one now:
void identifyEmailFields(AssistStructure.ViewNode node,
List<AssistStructure.ViewNode> emailFields) {
// More code goes here
}
As you can see, this method has a ViewNode and a List as its parameters. We’ll be using the List to store all the input fields that expect email addresses.
You might now be wondering how you can programmatically tell if an input field expects an email address. Well, there’s really no foolproof approach you can follow. For now, we’re going to assume that all app developers always give meaningful resource IDs to their input fields. Based on that assumption, we can simply pick all input fields whose resource IDs contain strings such as “email” and “username”.
Accordingly, add the following code to the method:
Next, whenever we encounter a ViewNode object that contains more ViewNode objects, we must recursively call the identifyEmailFields() method to analyze all its children. The following code shows you how:
At this point, we can call the identifyEmailFields() method inside the onFillRequest() method and pass the root node of the view hierarchy to it.
// Create an empty list
List<AssistStructure.ViewNode> emailFields = new ArrayList<>();
// Populate the list
identifyEmailFields(assistStructure
.getWindowNodeAt(0)
.getRootViewNode(), emailFields);
If our service is unable to identify any input fields for emails, it should do nothing. Therefore, add the following code to it:
if(emailFields.size() == 0)
return;
Step 2: Create and Populate Remote Views
If our service does identify an input field it can fill, it must populate a drop-down list that will be shown below the input field. Doing so, however, is not straightforward because neither the input field nor the drop-down list belongs to our app.
To populate the drop-down list, we must use RemoteViews objects. As its name suggests, a RemoteViews object is a collection of views that can be displayed in another app.
To initialize a RemoteViews object, you’ll need a layout XML file. Let’s create one now called email_suggestion.xml. For now, it can contain just one TextView widget to display an email address.
Accordingly, add the following code to email_suggestion.xml:
You can now go back to the onFillRequest() method and create two RemoteViews objects: one for the primary email, and another for the secondary.
RemoteViews rvPrimaryEmail =
new RemoteViews(getPackageName(),
R.layout.email_suggestion);
RemoteViews rvSecondaryEmail =
new RemoteViews(getPackageName(),
R.layout.email_suggestion);
The TextView widgets inside the RemoteViews objects must display the two email addresses we stored in a shared preferences file earlier. To open the file, use the getSharedPreferences() method again. Once it’s opened, you can use its getString() method to fetch both the email addresses.
Finally, to set the contents of the remote TextView widgets, you must use the setTextViewText() method.
We can now use the remote views to create autofill data sets that can be sent to any app. To keep this tutorial from getting too long, we’ll be creating data sets only for the first email input field we encounter. The following code shows how to pick only the first email input field:
An autofill data set is nothing but an instance of the Dataset class, and can be built using the Dataset.Builder class.
When the user selects one of the email addresses our service shows in the drop-down list, it must set the contents of the associated input field using the setValue() method of the Dataset.Builder class. However, you cannot pass a ViewNode object to the setValue() method. It actually expects an autofill identifier, which must be obtained by calling the getAutoFillId() method of the ViewNode object.
Additionally, to specify the text that must be written into the input field, you must use the AutoFillValue.forText() method. The following code shows you how:
Dataset primaryEmailDataSet =
new Dataset.Builder(rvPrimaryEmail)
.setValue(
emailField.getAutoFillId(),
AutoFillValue.forText(primaryEmail)
).build();
Dataset secondaryEmailDataSet =
new Dataset.Builder(rvSecondaryEmail)
.setValue(
emailField.getAutoFillId(),
AutoFillValue.forText(secondaryEmail)
).build();
Before you send the data sets to an app, you must add them to a FillResponse object, which can be built using the FillResponse.Builder class. Call its addDataset() method twice to add both the data sets.
Once the FillResponse object is ready, pass it as an argument to the onSuccess() method of the FillCallback object, which is one of the parameters of the onFillRequest() method.
FillResponse response = new FillResponse.Builder()
.addDataset(primaryEmailDataSet)
.addDataset(secondaryEmailDataSet)
.build();
fillCallback.onSuccess(response);
Step 4: Update the Manifest
Like all services, the autofill service too must be declared in the project’s AndroidManifest.xml file. While doing so, you must make sure that it is protected by the android.permission.BIND_AUTO_FILL permission.
This service also needs an <intent-filter> tag that allows it to respond to the android.service.autofill.AutoFillService action, and a <meta-data> tag that points to the meta-data XML file we created in an earlier step.
Accordingly, add the following lines to your manifest file:
Our autofill service and app are now ready. Build the project and install the app on your device.
4. Activate and Use the Autofill Service
To activate the autofill service, open your device’s Settings app and navigate to Apps & Notifications > Advanced > Default apps > Autofill app. In the next screen, select your app from the list of available autofill apps.
You can now open any app that asks for an email address to see your autofill service in action. For example, here’s what you’d see on the login screens of Instagram and Pinterest:
Conclusion
You now know how to create and use a custom autofill service for Android. Feel free to extend it to support other common fields, such as first name or phone number. You can also try identifying input fields using other attributes, such as labels and hints.
To learn more about the Autofill Framework, do refer to its official documentation. And in the meantime, check out some of our other posts about Android O and Android app development!
Auto form fill, often shortened to just autofill, is a feature browsers have supported for years now. Most of us use it all the time. I, for one, find it indispensable during tasks such as filling out a registration form or completing a checkout process.
The latest release of Android, Android O, brings similar functionality to Android apps. In other words, Android can now help users fill out forms that belong to all the apps they have installed on their devices. This was a much-awaited feature because typing with a virtual keyboard on a small screen tends to be quite a hassle.
As an app developer, you can use the new Autofill Framework to create your own custom autofill service, a service that decides how to populate an app’s input fields. In this tutorial, I’ll show you how.
Prerequisites
To be able to follow this tutorial, you’ll need:
Android Studio 2.4 Preview 7 or higher
An emulator or device running Android O or higher
1. Create a New Project
Fire up Android Studio and create a new project with an empty activity. You must, of course, remember to choose Android 7+ in the Target Android Devices dialog.
This project will need a few widgets that belong to the Design Support Library, so open the app module’s build.gradle file and add the following compile dependency to it:
compile 'com.android.support:design:26.+'
Lastly, press the Sync Now button to update the project.
2. Create a Settings Activity
In this tutorial, we’ll be creating an app containing a very simple autofill service that targets only those input fields where the user is expected to type in an email address. Because almost every other app on Google Play today asks for an email address, this service will be quite useful.
Our service obviously needs to know what the user’s email addresses are. Therefore, let us now build an activity where the user can type in and save two email addresses.
Step 1: Define the Layout
As you might expect, the layout of the activity will contain two EditText widgets where the user can type in his or her email addresses. If you want it to adhere to the guidelines of Material Design, placing the EditText widgets inside TextInputLayout containers is a good idea.
Additionally, the layout must have a Button widget the user can press to save the email addresses.
You are free to place the widgets anywhere you want. Nevertheless, for now, I suggest you place them all inside a LinearLayout whose orientation is vertical.
In the above code, you can see that the Button widget has an onClick attribute pointing to a method. Click on the yellow light bulb beside this attribute in Android Studio to generate a stub for it in the associated Activity class.
public void saveEmailAddresses(View view) {
// More code will be added here
}
Step 2: Save the Email Addresses
We’ll be using a shared preferences file called EMAIL_STORAGE to save our data. You can use the getSharedPreferences() method of your Activity class to access the file. Additionally, to be able to write to the file, you must call its edit() method, which generates a SharedPreferences.Editor object.
Accordingly, add the following code inside the saveEmailAddresses() method:
To fetch the email addresses the user has typed into the EditText widgets, you’ll have to first get references to them using the findViewById() method, and then call their getText() methods.
At this point, you can call the putString() method of the editor to add the email addresses to the preferences file as two key value pairs. After you do so, don’t forget to call the commit() method to make your changes permanent.
The settings activity we created in the previous step is currently just an ordinary activity. To let the Android platform know that it is a settings activity for an autofill service, we must create a meta-data XML file saying so.
Create a new XML file called email_address_filler.xml in the project’s res/xml folder. Inside it, add an tag and set the value of its settingsActivity attribute to the name of your Activity class.
You can now run the app, type in two email addresses, and press the Save button to save them.
3. Create an Autofill Service
Any class that extends the abstract AutoFillService class can serve as an autofill service. So start by creating a new Java class with File > New > Java Class. In the dialog that pops up, name the class EmailAddressFiller and make sure that you set the value of the Superclass field to AutoFillService.
Android Studio will now prompt you to generate stubs for two abstract methods: onSaveRequest() and onFillRequest(). In this tutorial, we’ll be focusing only the onFillRequest() method, which is automatically called whenever the user opens an activity—of any app—containing input fields.
@Override
public void onFillRequest(AssistStructure assistStructure,
Bundle bundle,
CancellationSignal cancellationSignal,
FillCallback fillCallback) {
// More code goes here
}
Step 1: Analyze View Hierarchies
An autofill service needs to analyze an app’s user interface and identify input fields it can fill. That’s why the onFillRequest() method receives an AssistStructure object, which contains details about all the widgets that are currently visible on the screen. More precisely, it contains a tree of ViewNode objects.
If you’ve never seen such a tree, I suggest you use the uiautomatorviewer tool, which is part of the Android SDK, to analyze the layout hierarchies of a few apps. For example, here’s what the layout hierarchy of Android’s default mail app looks like:
Naturally, to analyze all nodes of a tree, you need a recursive method. Let’s create one now:
void identifyEmailFields(AssistStructure.ViewNode node,
List emailFields) {
// More code goes here
}
As you can see, this method has a ViewNode and a List as its parameters. We’ll be using the List to store all the input fields that expect email addresses.
You might now be wondering how you can programmatically tell if an input field expects an email address. Well, there’s really no foolproof approach you can follow. For now, we’re going to assume that all app developers always give meaningful resource IDs to their input fields. Based on that assumption, we can simply pick all input fields whose resource IDs contain strings such as “email” and “username”.
Accordingly, add the following code to the method:
Next, whenever we encounter a ViewNode object that contains more ViewNode objects, we must recursively call the identifyEmailFields() method to analyze all its children. The following code shows you how:
for(int i=0; i
At this point, we can call the identifyEmailFields() method inside the onFillRequest() method and pass the root node of the view hierarchy to it.
// Create an empty list
List emailFields = new ArrayList<>();
// Populate the list
identifyEmailFields(assistStructure
.getWindowNodeAt(0)
.getRootViewNode(), emailFields);
If our service is unable to identify any input fields for emails, it should do nothing. Therefore, add the following code to it:
if(emailFields.size() == 0)
return;
Step 2: Create and Populate Remote Views
If our service does identify an input field it can fill, it must populate a drop-down list that will be shown below the input field. Doing so, however, is not straightforward because neither the input field nor the drop-down list belongs to our app.
To populate the drop-down list, we must use RemoteViews objects. As its name suggests, a RemoteViews object is a collection of views that can be displayed in another app.
To initialize a RemoteViews object, you'll need a layout XML file. Let's create one now called email_suggestion.xml. For now, it can contain just one TextView widget to display an email address.
Accordingly, add the following code to email_suggestion.xml:
You can now go back to the onFillRequest() method and create two RemoteViews objects: one for the primary email, and another for the secondary.
RemoteViews rvPrimaryEmail =
new RemoteViews(getPackageName(),
R.layout.email_suggestion);
RemoteViews rvSecondaryEmail =
new RemoteViews(getPackageName(),
R.layout.email_suggestion);
The TextView widgets inside the RemoteViews objects must display the two email addresses we stored in a shared preferences file earlier. To open the file, use the getSharedPreferences() method again. Once it's opened, you can use its getString() method to fetch both the email addresses.
Finally, to set the contents of the remote TextView widgets, you must use the setTextViewText() method.
We can now use the remote views to create autofill data sets that can be sent to any app. To keep this tutorial from getting too long, we'll be creating data sets only for the first email input field we encounter. The following code shows how to pick only the first email input field:
An autofill data set is nothing but an instance of the Dataset class, and can be built using the Dataset.Builder class.
When the user selects one of the email addresses our service shows in the drop-down list, it must set the contents of the associated input field using the setValue() method of the Dataset.Builder class. However, you cannot pass a ViewNode object to the setValue() method. It actually expects an autofill identifier, which must be obtained by calling the getAutoFillId() method of the ViewNode object.
Additionally, to specify the text that must be written into the input field, you must use the AutoFillValue.forText() method. The following code shows you how:
Dataset primaryEmailDataSet =
new Dataset.Builder(rvPrimaryEmail)
.setValue(
emailField.getAutoFillId(),
AutoFillValue.forText(primaryEmail)
).build();
Dataset secondaryEmailDataSet =
new Dataset.Builder(rvSecondaryEmail)
.setValue(
emailField.getAutoFillId(),
AutoFillValue.forText(secondaryEmail)
).build();
Before you send the data sets to an app, you must add them to a FillResponse object, which can be built using the FillResponse.Builder class. Call its addDataset() method twice to add both the data sets.
Once the FillResponse object is ready, pass it as an argument to the onSuccess() method of the FillCallback object, which is one of the parameters of the onFillRequest() method.
FillResponse response = new FillResponse.Builder()
.addDataset(primaryEmailDataSet)
.addDataset(secondaryEmailDataSet)
.build();
fillCallback.onSuccess(response);
Step 4: Update the Manifest
Like all services, the autofill service too must be declared in the project's AndroidManifest.xml file. While doing so, you must make sure that it is protected by the android.permission.BIND_AUTO_FILL permission.
This service also needs an tag that allows it to respond to the android.service.autofill.AutoFillService action, and a tag that points to the meta-data XML file we created in an earlier step.
Accordingly, add the following lines to your manifest file:
Our autofill service and app are now ready. Build the project and install the app on your device.
4. Activate and Use the Autofill Service
To activate the autofill service, open your device's Settings app and navigate to Apps & Notifications > Advanced > Default apps > Autofill app. In the next screen, select your app from the list of available autofill apps.
You can now open any app that asks for an email address to see your autofill service in action. For example, here's what you'd see on the login screens of Instagram and Pinterest:
Conclusion
You now know how to create and use a custom autofill service for Android. Feel free to extend it to support other common fields, such as first name or phone number. You can also try identifying input fields using other attributes, such as labels and hints.
To learn more about the Autofill Framework, do refer to its official documentation. And in the meantime, check out some of our other posts about Android O and Android app development!
This website uses cookies to improve your experience. AcceptRead More
Privacy & Cookies Policy
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.