Android 8 has added an auto-fill framework that makes it easier for us to make fewer mistakes when filling out forms. It also reduces the time it takes to fill out forms, which is a time-consuming and error-prone task. Users can easily get frustrated with apps that require these types of tasks. The auto-fill framework improves the user experience by providing the following advantages:

  • Less time spent filling fields Auto-fill features can help users avoid reentering information
  • Minimizing user input errors Typing can be error-prone, especially on mobile devices where the necessity of deleting input information can also eliminate errors that follow

Therefore, automatic filling frame can make my operation easier and improve the user’s convenience in use. So let’s take a look at how to implement the auto-fill framework, so let’s create a loginDemo, loginDemo, and let’s look at the layout of the login page, okay

<? xml version="1.0" encoding="utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/username"
            android:hint="Your primary email address"
            android:inputType="textEmailAddress"
            android:importantForAutofill="yes"/>
    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/password"
            android:hint="numberPassword"
            android:importantForAutofill="yes"
            android:inputType="numberPassword"/>
    </android.support.design.widget.TextInputLayout>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/save_button"
        style="@style/Widget.AppCompat.Button.Colored"
        android:text="Login"
        android:onClick="userLogin"/>
</LinearLayout>
Copy the code

Layout is very simple, there is only one android: importantForAutofill properties, it is the key. In order for the Android system to recognize whether a control supports the auto-fill service, we first need to add an identifier to the control that supports the service, which will inspire the auto-fill framework.

1. Add an automatic fill label to the control

android:importantForAutofill
Copy the code

There are several values for this property

  • Auto Indicates the system default value. The system determines whether to trigger the automatic fill service
  • No This control does not support automatic fill service
  • NoExcludeDescendants Neither this control nor its children support the auto-fill service
  • The yes view supports automatic population service
  • YesExcludeDescendants this control supports, but its child controls do not. Then we’ll look at the logic of MainActivity
public class MainActivity extends AppCompatActivity {
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAutofillManager = this.getSystemService(AutofillManager.class);
        mAutofillCallback = new MyAutofillCallback();
    }

    public void userLogin(View view) {
        String uName = ((EditText) findViewById(R.id.username)).getText().toString();
        String pw = ((EditText) findViewById(R.id.password)).getText().toString();
        SharedPreferences.Editor editor = getSharedPreferences("EMAIL_STORAGE", MODE_PRIVATE)
                .edit();
        editor.putString("PRIMARY_EMAIL", uName);
        editor.putString("USER_PW", pw); editor.commit(); }}Copy the code

SharedPreferences = SharedPreferences = SharedPreferences = SharedPreferences = SharedPreferences = SharedPreferences

2 AutofillService service

Before we write the code, AutofillService is a service class that helps us to automatically fill the screen. It has two methods that we need to rewrite.

@Override
    public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal,
    @NonNull FillCallback fillCallback) {
    }
    
     public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
         
     }
Copy the code
  • OnFillRequest The system calls this method when the page content is available for auto-filling

    The FillRequest parameter automatically populates the request object for the service and can be used to get the on-screen view

    The FillCallback parameter processes the service being populated and displays the auto-populated data

  • OnSaveRequest is called when the user requests the service to save a screen field

    It has basically the same parameters as the first one

So just to give you an overview of what AutofullService does, we’re creating a class MyAutofillService that inherits AutofillService

public class MyAutofillService extends AutofillService {

    public void traverseStructure(AssistStructure structure, List<AssistStructure.ViewNode> emailFields) {
        int nodes = structure.getWindowNodeCount();

        for (int i = 0; i < nodes; i++) {
            AssistStructure.WindowNode windowNode = structure.getWindowNodeAt(i);
            AssistStructure.ViewNode viewNode = windowNode.getRootViewNode();
            traverseNode(viewNode, emailFields);
        }
    }

    public void traverseNode(AssistStructure.ViewNode viewNode, List<AssistStructure.ViewNode> emailFields) {
        if (viewNode == null || viewNode.getClassName() == null)
            return;
        if (viewNode.getClassName().contains("EditText")) {
            String viewId = viewNode.getIdEntry();
            if(viewId ! = null && (viewId.contains("email") || viewId.contains("username") || viewId.contains("password"))) {
                emailFields.add(viewNode);
                return; }}for(int i = 0; i < viewNode.getChildCount(); i++) { AssistStructure.ViewNode childNode = viewNode.getChildAt(i); traverseNode(childNode, emailFields); } } @Override public void onFillRequest(@NonNull FillRequest request, @NonNull CancellationSignal cancellationSignal, @nonnull FillCallback FillCallback) {// Get the structure from the request //1 Get the auto-filled context List<FillContext> context = request.getFillContexts(); AssistStructure Structure = context.get(context.size() -1).getStructure(); List< assistStructure. ViewNode> emailFields = new ArrayList<>(); // Get the target space traverseStructure(structure, emailFields);if (emailFields.size() < 2) {
            fillCallback.onSuccess(null);
            return;
        }
        SharedPreferences sharedPreferences = getSharedPreferences("EMAIL_STORAGE", MODE_PRIVATE);
        String primaryEmail = sharedPreferences.getString("PRIMARY_EMAIL"."");
        String secondaryEmail = sharedPreferences.getString("SECONDARY_EMAIL".""); RemoteViews rvPrimaryEmail = new RemoteViews(getPackageName(), r.layout.user_suggestion); rvPrimaryEmail.setTextViewText(R.id.email_suggestion_item, primaryEmail); rvPrimaryEmail.setImageViewResource(R.id.icon, R.mipmap.user); RemoteViews rvSecondaryEmail = new RemoteViews(getPackageName(), R.layout.user_suggestion); rvSecondaryEmail.setTextViewText(R.id.user_suggestion_item, primaryEmail); rvSecondaryEmail.setImageViewResource(R.id.icon, R.mipmap.pw); AssistStructure.ViewNode userField = emailFields.get(0); AssistStructure.ViewNode passField = emailFields.get(1); // Construct Dataset primaryEmailDataSet = new Dataset.Builder(rvPrimaryEmail).setValue(userfield.getautoFillid (), AutofillValue.forText(primaryEmail) ).build(); Dataset secondaryEmailDataSet = new Dataset.Builder(rvSecondaryEmail) .setValue(passField.getAutofillId(), AutofillValue.forText(secondaryEmail) ).build(); FillResponse Response = new fillResponse.builder ().adddataset (primaryEmailDataSet) .addDataset(secondaryEmailDataSet) // .addDataset(msecondaryEmailDataSet) // .setSaveInfo(new SaveInfo. Builder (/ / only add SaveInfo object callback onSaveRequest / / SaveInfo SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_EMAIL_ADDRESS, // new AutofillId[]{emailField.getAutofillId(), emailField.getAutofillId()}) // .build()) .build(); // The notification auto-fill framework fillcallback.onsuccess (response); } // Save the record that needs to be auto-filled. @Override public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) { List<FillContext> context = request.getFillContexts(); AssistStructure structure = context.get(context.size() - 1).getStructure(); List<AssistStructure.ViewNode> emailFields = new ArrayList<>(); traverseStructure(structure, emailFields); callback.onSuccess(); }}Copy the code

We get the space information for the current interface in the onFillRequest method, then iterate to see if there is any space left for our target, save it in a list, which is commented in detail in the code, and here is the user_Suggestion layout file

<? xml version="1.0" encoding="utf-8"? > <LinearLayout android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/user_suggestion_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:textSize="18sp"
        android:textStyle="bold"></TextView>

    <ImageView
        android:id="@+id/icon"
        android:layout_gravity="center"
        android:layout_height="wrap_content"
        android:layout_marginEnd="? android:attr/listPreferredItemPaddingEnd"
        android:layout_width="wrap_content"
        android:src="@mipmap/ic_launcher" />
</LinearLayout>

Copy the code

Once the service class is finished, we need to configure it

<service android:name=".MyAutofillService"
            android:label="Mtx Autofill Service"
            android:permission="android.permission.BIND_AUTOFILL_SERVICE">

            <meta-data android:name="android.autofill"
                android:resource="@xml/login_filler"/>
            <intent-filter>
                <action android:name="android.service.autofill.AutofillService"/>
            </intent-filter>
        </service>
Copy the code

Android :label= “XXX” specifies the name of the service. Optionally, declare an Android.permission.BIND_AUTOFILL_SERVICE permission, followed by login_filler

<? xml version="1.0" encoding="utf-8"? > <autofill-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsActivity="com.mtx.login.MainActivity"/>
Copy the code

Finally, set up the phone: Settings > System > Language and Input > Advanced > Input Help > Automatic Fill Service >Mtx Autofill Service