| /* | |
| * Copyright (C) 2020 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.inlinefillservice; | |
| import static com.example.android.inlinefillservice.InlineFillService.TAG; | |
| import android.app.assist.AssistStructure; | |
| import android.content.Context; | |
| import android.service.autofill.FillContext; | |
| import android.service.autofill.FillRequest; | |
| import android.util.ArrayMap; | |
| import android.util.Log; | |
| import android.view.View; | |
| import android.view.autofill.AutofillId; | |
| import android.widget.Toast; | |
| import androidx.annotation.NonNull; | |
| import java.util.List; | |
| import java.util.Map; | |
| final class Helper { | |
| /** | |
| * Displays a toast with the given message. | |
| */ | |
| static void showMessage(@NonNull Context context, @NonNull CharSequence message) { | |
| Log.i(TAG, message.toString()); | |
| Toast.makeText(context, message, Toast.LENGTH_LONG).show(); | |
| } | |
| /** | |
| * Extracts the autofillable fields from the request through assist structure. | |
| */ | |
| static ArrayMap<String, AutofillId> getAutofillableFields(@NonNull FillRequest request) { | |
| AssistStructure structure = getLatestAssistStructure(request); | |
| return getAutofillableFields(structure); | |
| } | |
| /** | |
| * Helper method to get the {@link AssistStructure} associated with the latest request | |
| * in an autofill context. | |
| */ | |
| @NonNull | |
| private static AssistStructure getLatestAssistStructure(@NonNull FillRequest request) { | |
| List<FillContext> fillContexts = request.getFillContexts(); | |
| return fillContexts.get(fillContexts.size() - 1).getStructure(); | |
| } | |
| /** | |
| * Parses the {@link AssistStructure} representing the activity being autofilled, and returns a | |
| * map of autofillable fields (represented by their autofill ids) mapped by the hint associate | |
| * with them. | |
| * | |
| * <p>An autofillable field is a {@link AssistStructure.ViewNode} whose getHint(ViewNode) | |
| * method. | |
| */ | |
| @NonNull | |
| private static ArrayMap<String, AutofillId> getAutofillableFields( | |
| @NonNull AssistStructure structure) { | |
| ArrayMap<String, AutofillId> fields = new ArrayMap<>(); | |
| int nodes = structure.getWindowNodeCount(); | |
| for (int i = 0; i < nodes; i++) { | |
| AssistStructure.ViewNode node = structure.getWindowNodeAt(i).getRootViewNode(); | |
| addAutofillableFields(fields, node); | |
| } | |
| ArrayMap<String, AutofillId> result = new ArrayMap<>(); | |
| int filedCount = fields.size(); | |
| for (int i = 0; i < filedCount; i++) { | |
| String key = fields.keyAt(i); | |
| AutofillId value = fields.valueAt(i); | |
| // For fields with no hint we just use Field | |
| if (key.equals(value.toString())) { | |
| result.put("Field:" + i + "-", fields.valueAt(i)); | |
| } else { | |
| result.put(key, fields.valueAt(i)); | |
| } | |
| } | |
| return result; | |
| } | |
| /** | |
| * Adds any autofillable view from the {@link AssistStructure.ViewNode} and its descendants to | |
| * the map. | |
| */ | |
| private static void addAutofillableFields(@NonNull Map<String, AutofillId> fields, | |
| @NonNull AssistStructure.ViewNode node) { | |
| if (node.getAutofillType() == View.AUTOFILL_TYPE_TEXT) { | |
| if (!fields.containsValue(node.getAutofillId())) { | |
| final String key; | |
| if (node.getHint() != null) { | |
| key = node.getHint().toLowerCase(); | |
| } else if (node.getAutofillHints() != null) { | |
| key = node.getAutofillHints()[0].toLowerCase(); | |
| } else { | |
| key = node.getAutofillId().toString(); | |
| } | |
| fields.put(key, node.getAutofillId()); | |
| } | |
| } | |
| int childrenSize = node.getChildCount(); | |
| for (int i = 0; i < childrenSize; i++) { | |
| addAutofillableFields(fields, node.getChildAt(i)); | |
| } | |
| } | |
| } |