Статьи

Как использовать платформу автозаполнения Android O

Автоматическое заполнение форм, часто сокращаемое до автоматического заполнения, является функцией, которую браузеры поддерживают уже много лет. Большинство из нас используют это все время. Я, например, считаю это необходимым во время таких задач, как заполнение регистрационной формы или завершение процесса оформления заказа.

Последний выпуск Android, Android O , предоставляет аналогичную функциональность приложениям Android. Другими словами, Android теперь может помочь пользователям заполнять формы, которые принадлежат всем приложениям, установленным на их устройствах. Это была очень долгожданная функция, потому что печатать с помощью виртуальной клавиатуры на маленьком экране, как правило, довольно сложно.

Как разработчик приложения вы можете использовать новую среду автозаполнения для создания собственной настраиваемой службы автозаполнения, которая решает, как заполнять поля ввода приложения. В этом уроке я покажу как.

Чтобы следовать этому руководству, вам понадобится:

  • Android Studio 2.4 Preview 7 или выше
  • Эмулятор или устройство под управлением Android O или выше

Запустите Android Studio и создайте новый проект с пустым действием. Вы, конечно, не забудьте выбрать Android 7+ в диалоговом окне Target Android Devices .

Target android devices dialog

Для этого проекта потребуются несколько виджетов, принадлежащих библиотеке поддержки проектирования, поэтому откройте файл build.gradle модуля app и добавьте в него следующую зависимость compile :

1
compile ‘com.android.support:design:26.+’

Наконец, нажмите кнопку « Синхронизировать сейчас» , чтобы обновить проект.

В этом руководстве мы создадим приложение, содержащее очень простую службу автозаполнения, предназначенную только для тех полей ввода, в которые пользователь должен ввести адрес электронной почты. Поскольку почти все другие приложения в Google Play сегодня запрашивают адрес электронной почты, эта услуга будет весьма полезна.

Наш сервис, очевидно, должен знать, какие адреса электронной почты пользователя. Поэтому давайте теперь создадим действие, в котором пользователь сможет ввести и сохранить два адреса электронной почты.

Как и следовало ожидать, макет действия будет содержать два виджета EditText где пользователь может вводить свои адреса электронной почты. Если вы хотите, чтобы он придерживался принципов дизайна материалов, размещение виджетов EditText внутри контейнеров TextInputLayout является хорошей идеей.

Кроме того, в макете должен быть виджет « Button пользователь может нажать для сохранения адресов электронной почты.

Вы можете разместить виджеты где угодно. Тем не менее, на данный момент я предлагаю вам поместить их все в LinearLayout , ориентация которого вертикальная.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?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/primary»
            android:hint=»Your primary email address»
            android:inputType=»textEmailAddress»/>
    </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/secondary»
            android:hint=»Your other email address»
            android:inputType=»textEmailAddress»/>
    </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=»Save»
        android:onClick=»saveEmailAddresses»/>
 
</LinearLayout>

В приведенном выше коде вы можете видеть, что виджет Button имеет атрибут onClick указывающий на метод. Нажмите на желтую лампочку рядом с этим атрибутом в Android Studio, чтобы сгенерировать для него заглушку в связанном классе Activity .

1
2
3
public void saveEmailAddresses(View view) {
    // More code will be added here
}

Мы будем использовать файл общих настроек EMAIL_STORAGE для сохранения наших данных. Вы можете использовать метод getSharedPreferences() вашего класса Activity для доступа к файлу. Кроме того, чтобы иметь возможность записи в файл, необходимо вызвать его метод edit() , который генерирует объект SharedPreferences.Editor .

Соответственно, добавьте следующий код в метод saveEmailAddresses() :

1
2
SharedPreferences.Editor editor =
       getSharedPreferences(«EMAIL_STORAGE», MODE_PRIVATE).edit();

Чтобы получить адреса электронной почты, которые пользователь ввел в виджеты EditText , сначала нужно получить ссылки на них с findViewById() метода findViewById() , а затем вызвать их getText() .

1
2
3
4
5
6
7
String primaryEmailAddress =
                ((EditText)findViewById(R.id.primary))
                            .getText().toString();
                             
String secondaryEmailAddress =
                ((EditText)findViewById(R.id.secondary))
                            .getText().toString();

На этом этапе вы можете вызвать метод редактора putString() чтобы добавить адреса электронной почты в файл настроек в виде двух пар ключ-значение. После этого не забудьте вызвать метод commit() чтобы сделать ваши изменения постоянными.

1
2
3
editor.putString(«PRIMARY_EMAIL», primaryEmailAddress);
editor.putString(«SECONDARY_EMAIL», secondaryEmailAddress);
editor.commit();

Задание настроек, которое мы создали на предыдущем шаге, в настоящее время является обычным действием Чтобы платформа Android знала, что это действие по настройке для службы автозаполнения, мы должны создать XML-файл метаданных, сообщив об этом.

Создайте новый XML-файл с именем email_address_filler.xml в папке проекта res / xml . Внутри него добавьте <autofill-service> и установите в качестве значения его атрибута settingsActivity имя вашего класса Activity .

1
2
3
4
<?xml version=»1.0″ encoding=»utf-8″?>
<autofill-service
    xmlns:android=»http://schemas.android.com/apk/res/android»
    android:settingsActivity=»com.tutsplus.simplefill.MainActivity»/>

Теперь вы можете запустить приложение, ввести два адреса электронной почты и нажать кнопку Сохранить , чтобы сохранить их.

Settings activity running

Любой класс, который расширяет абстрактный класс AutoFillService может служить в качестве службы автозаполнения. Итак, начните с создания нового Java-класса с помощью File> New> Java Class . В появившемся диалоговом окне назовите класс EmailAddressFiller и убедитесь, что в поле Superclass установлено значение AutoFillService .

Create new class dialog

Android Studio теперь предложит вам создать заглушки для двух абстрактных методов: onSaveRequest() и onFillRequest() . В этом руководстве мы сосредоточимся только на onFillRequest() , который автоматически вызывается всякий раз, когда пользователь открывает действие любого приложения, содержащее поля ввода.

1
2
3
4
5
6
7
8
9
@Override
public void onFillRequest(AssistStructure assistStructure,
                          Bundle bundle,
                          CancellationSignal cancellationSignal,
                          FillCallback fillCallback) {
 
    // More code goes here
 
}

Служба автозаполнения должна проанализировать пользовательский интерфейс приложения и определить поля ввода, которые оно может заполнить. Вот почему метод onFillRequest() получает объект AssistStructure , который содержит сведения обо всех виджетах, которые в данный момент видны на экране. Точнее, он содержит дерево объектов ViewNode .

Если вы никогда не видели такое дерево, я предлагаю вам использовать инструмент uiautomatorviewer , который является частью Android SDK, для анализа иерархии макетов некоторых приложений. Например, вот как выглядит иерархия макетов почтового приложения Android по умолчанию:

View hierarchy of default mail app

Естественно, чтобы проанализировать все узлы дерева, вам нужен рекурсивный метод. Давайте создадим один сейчас:

1
2
3
4
void identifyEmailFields(AssistStructure.ViewNode node,
                     List<AssistStructure.ViewNode> emailFields) {
    // More code goes here
}

Как видите, этот метод имеет ViewNode и List качестве параметров. Мы будем использовать List для хранения всех полей ввода, которые ожидают адреса электронной почты.

Теперь вам может быть интересно, как программно определить, ожидает ли поле ввода адрес электронной почты. Ну, на самом деле нет надежного подхода, которому вы можете следовать. Сейчас мы предполагаем, что все разработчики приложений всегда предоставляют значимые идентификаторы ресурсов своим полям ввода. Исходя из этого предположения, мы можем просто выбрать все поля ввода, идентификаторы ресурсов которых содержат строки, такие как «электронная почта» и «имя пользователя».

Соответственно, добавьте следующий код в метод:

1
2
3
4
5
6
7
8
if(node.getClassName().contains(«EditText»)) {
    String viewId = node.getIdEntry();
    if(viewId!=null && (viewId.contains(«email»)
                        ||
        emailFields.add(node);
        return;
    }
}

Затем, когда мы сталкиваемся с объектом ViewNode который содержит больше объектов ViewNode , мы должны рекурсивно вызывать метод ViewNode identifyEmailFields() для анализа всех его дочерних ViewNode . Следующий код показывает вам, как:

1
2
3
for(int i=0; i<node.getChildCount();i++) {
    identifyEmailFields(node.getChildAt(i), emailFields);
}

На этом этапе мы можем вызвать метод onFillRequest() внутри метода onFillRequest() и передать ему корневой узел иерархии представления.

1
2
3
4
5
6
7
// Create an empty list
List<AssistStructure.ViewNode> emailFields = new ArrayList<>();
 
// Populate the list
identifyEmailFields(assistStructure
                    .getWindowNodeAt(0)
                    .getRootViewNode(), emailFields);

Если наш сервис не может определить какие-либо поля ввода для электронных писем, он ничего не должен делать. Поэтому добавьте в него следующий код:

1
2
if(emailFields.size() == 0)
   return;

Если наш сервис идентифицирует поле ввода, которое он может заполнить, он должен заполнить раскрывающийся список, который будет отображаться под полем ввода. Однако сделать это непросто, поскольку ни поле ввода, ни раскрывающийся список не принадлежат нашему приложению.

Чтобы заполнить раскрывающийся список, мы должны использовать объекты RemoteViews . Как следует из названия, объект RemoteViews представляет собой набор представлений, которые могут отображаться в другом приложении.

Для инициализации объекта RemoteViews вам понадобится XML-файл макета. Давайте создадим один, теперь называемый email_suggestion.xml . На данный момент он может содержать только один виджет TextView для отображения адреса электронной почты.

Соответственно, добавьте следующий код в email_suggestion.xml :

1
2
3
4
5
6
7
8
9
<?xml version=»1.0″ encoding=»utf-8″?>
<TextView xmlns:android=»http://schemas.android.com/apk/res/android»
    android:layout_width=»wrap_content»
    android:layout_height=»wrap_content»
    android:id=»@+id/email_suggestion_item»
    android:textSize=»18sp»
    android:textStyle=»bold»
    android:padding=»5dp»>
</TextView>

Теперь вы можете вернуться к onFillRequest() и создать два объекта RemoteViews : один для основной электронной почты, а другой для дополнительной.

1
2
3
4
5
6
7
RemoteViews rvPrimaryEmail =
            new RemoteViews(getPackageName(),
                            R.layout.email_suggestion);
                             
RemoteViews rvSecondaryEmail =
            new RemoteViews(getPackageName(),
                            R.layout.email_suggestion);

Виджеты TextView внутри объектов RemoteViews должны отображать два адреса электронной почты, которые мы сохранили в файле общих настроек ранее. Чтобы открыть файл, снова используйте метод getSharedPreferences() . Открыв его, вы можете использовать его метод getString() для получения обоих адресов электронной почты.

Наконец, чтобы установить содержимое удаленных виджетов TextView , вы должны использовать метод setTextViewText() .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
// Load the email addresses from preferences
SharedPreferences sharedPreferences =
                getSharedPreferences(«EMAIL_STORAGE», MODE_PRIVATE);
 
String primaryEmail =
                sharedPreferences.getString(«PRIMARY_EMAIL», «»);
String secondaryEmail =
                sharedPreferences.getString(«SECONDARY_EMAIL», «»);
 
// Update remote TextViews
rvPrimaryEmail.setTextViewText(R.id.email_suggestion_item,
                               primaryEmail);
rvSecondaryEmail.setTextViewText(R.id.email_suggestion_item,
                                 secondaryEmail);

Теперь мы можем использовать удаленные представления для создания наборов данных автозаполнения, которые можно отправлять в любое приложение. Чтобы этот учебник не стал слишком длинным, мы будем создавать наборы данных только для первого поля ввода электронной почты, с которым мы столкнемся. Следующий код показывает, как выбрать только первое поле ввода электронной почты:

1
AssistStructure.ViewNode emailField = emailFields.get(0);

Набор данных автозаполнения — это не что иное, как экземпляр класса Dataset , и его можно построить с Dataset.Builder класса Dataset.Builder .

Когда пользователь выбирает один из адресов электронной почты, которые наша служба показывает в раскрывающемся списке, он должен установить содержимое соответствующего поля ввода с помощью setValue() класса Dataset.Builder . Однако вы не можете передать объект setValue() метод setValue() . Фактически он ожидает идентификатор автозаполнения, который должен быть получен путем вызова getAutoFillId() объекта ViewNode .

Кроме того, чтобы указать текст, который должен быть записан в поле ввода, необходимо использовать метод AutoFillValue.forText() . Следующий код показывает вам, как:

01
02
03
04
05
06
07
08
09
10
11
12
13
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();

Перед отправкой наборов данных в приложение вы должны добавить их в объект FillResponse , который можно создать с FillResponse.Builder класса FillResponse.Builder . Вызовите его addDataset() дважды, чтобы добавить оба набора данных.

Как FillResponse объект FillResponse будет готов, передайте его в качестве аргумента onSuccess() объекта FillCallback , который является одним из параметров метода onFillRequest() .

1
2
3
4
5
6
FillResponse response = new FillResponse.Builder()
                            .addDataset(primaryEmailDataSet)
                            .addDataset(secondaryEmailDataSet)
                            .build();
 
fillCallback.onSuccess(response);

Как и все сервисы, сервис автозаполнения также должен быть объявлен в файле AndroidManifest.xml проекта. При этом вы должны убедиться, что он защищен разрешением android.permission.BIND_AUTO_FILL .

Этому сервису также нужен <intent-filter> который позволяет ему реагировать на действие android.service.autofill.AutoFillService , и <meta-data> который указывает на файл XML метаданных, который мы создали на предыдущем шаге. ,

Соответственно, добавьте следующие строки в ваш файл манифеста:

1
2
3
4
5
6
7
8
<service android:name=».EmailAddressFiller»
    android:permission=»android.permission.BIND_AUTO_FILL»>
    <meta-data android:name=»android.autofill»
        android:resource=»@xml/email_address_filler»/>
    <intent-filter>
        <action android:name=»android.service.autofill.AutoFillService»/>
    </intent-filter>
</service>

Наш сервис автозаполнения и приложение теперь готовы. Создайте проект и установите приложение на свое устройство.

Чтобы активировать службу автозаполнения, откройте приложение « Настройки» своего устройства и выберите « Приложения и уведомления»> «Дополнительно»> «Приложения по умолчанию»> «Автозаполнение» . На следующем экране выберите свое приложение из списка доступных приложений автозаполнения.

Autofill app selection screen

Теперь вы можете открыть любое приложение, которое запрашивает адрес электронной почты, чтобы увидеть вашу службу автозаполнения в действии. Например, вот что вы увидите на экранах входа в Instagram и Pinterest:

Autofill suggestions displayed for two popular apps

Теперь вы знаете, как создать и использовать собственный сервис автозаполнения для Android. Не стесняйтесь расширять его для поддержки других общих полей, таких как имя или номер телефона. Вы также можете попробовать определить поля ввода, используя другие атрибуты, такие как метки и подсказки.

Чтобы узнать больше об Autofill Framework, обратитесь к его официальной документации . А пока, посмотрите другие наши посты о разработке приложений для Android и Android!

  • Android SDK
    Начните с RxJava 2 для Android
    Джессика Торнсби
  • Android SDK
    Совет: Работа с пользовательскими шрифтами в Android O
    Джессика Торнсби
  • Android вещи
    Android вещи и машинное обучение
    Пол Требилкокс-Руис