Кодовая лаборатория API перехода для распознавания действий

1. Введение

Мы носим телефоны с собой повсюду, но до сих пор приложениям было сложно адаптироваться к постоянно меняющейся обстановке и активности пользователя.

Раньше для этого разработчики тратили ценное инженерное время на объединение различных сигналов (местоположение, данные с датчиков и т. д.), чтобы определить, когда началась или закончилась такая активность, как ходьба или вождение. Хуже того, когда приложения независимо и непрерывно проверяют изменения в активности пользователя, это негативно сказывается на времени автономной работы батареи.

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

Например, приложение для обмена сообщениями может запросить: «Сообщите мне, когда пользователь вошел в транспортное средство или вышел из него» , чтобы установить статус пользователя как «занят». Аналогично, приложение для определения места парковки может запросить: «Сообщите мне, когда пользователь вышел из транспортного средства и начал идти пешком», чтобы сохранить место парковки пользователя.

В этом практическом занятии вы узнаете, как использовать API распознавания активности для определения момента начала/остановки пользователем такой активности, как ходьба или бег.

Предварительные требования

Знание основ разработки под Android и некоторое знакомство с функциями обратного вызова.

Что вы узнаете

  • Регистрация на переходы между мероприятиями
  • Обработка этих событий
  • Отмена регистрации на переходы между действиями, когда они больше не требуются.

Что вам понадобится

  • Android Studio Bumblebee
  • Устройство или эмулятор Android

2. Начало работы

Клонируйте репозиторий стартового проекта.

Чтобы вы могли как можно быстрее начать работу, мы подготовили для вас стартовый проект. Если у вас установлен Git, вы можете просто выполнить команду ниже. (Вы можете проверить это, набрав git --version в терминале/командной строке и убедившись, что команда выполняется правильно.)

 git clone https://github.com/android/codelab-activity_transitionapi

Если у вас нет Git, вы можете получить проект в виде ZIP-архива:

Импортируйте проект

Запустите Android Studio, выберите на экране приветствия «Открыть существующий проект Android Studio» и откройте каталог проекта.

После загрузки проекта вы также можете увидеть предупреждение о том, что Git не отслеживает все ваши локальные изменения. В этом случае вы можете нажать « Игнорировать» или крестик в правом верхнем углу. (Вы не будете отправлять изменения обратно в репозиторий Git.)

В верхнем левом углу окна проекта, если вы находитесь в режиме просмотра Android , вы должны увидеть что-то похожее на изображение ниже. (Если вы находитесь в режиме просмотра проекта , вам нужно будет развернуть проект, чтобы увидеть то же самое.)

d2363db913d8e5ad.png

Есть два значка папок ( base и complete ). Каждый из них называется «модулем».

Обратите внимание, что для первой компиляции проекта в фоновом режиме в Android Studio может потребоваться несколько секунд. В это время в строке состояния внизу окна Android Studio будет отображаться индикатор загрузки:

c9f23d5336be3cfe.png

Мы рекомендуем дождаться завершения этого процесса, прежде чем вносить изменения в код. Это позволит Android Studio подключить все необходимые компоненты.

Кроме того, если появится сообщение типа «Перезагрузите страницу, чтобы изменения языка вступили в силу?» или что-то подобное, выберите «Да».

Разберитесь в начальном проекте.

Итак, вы всё настроили и готовы добавить распознавание активности. Мы будем использовать base модуль, который является отправной точкой для этого практического занятия. Другими словами, вы будете добавлять код из каждого шага в base .

complete модуль можно использовать для проверки вашей работы или в качестве справочного материала в случае возникновения каких-либо проблем.

Обзор ключевых компонентов:

  • MainActivity : Содержит весь код, необходимый для распознавания активности.

Настройка эмулятора

Если вам нужна помощь в настройке эмулятора Android, обратитесь к статье «Запуск приложения» .

Запустите стартовый проект

Давайте запустим наше приложение.

  • Подключите ваше Android-устройство к компьютеру или запустите эмулятор.
  • На панели инструментов выберите base конфигурацию из выпадающего списка и нажмите зеленую кнопку-треугольник (Запуск) рядом с ней:

a640a291ffaf62ad.png

  • Ниже вы увидите приложение:

f58d4bb92ee77f41.png

  • Сейчас приложение ничего не делает, кроме как выводит сообщение. Теперь мы добавим функцию распознавания активности.

Краткое содержание

На этом этапе вы узнали о следующем:

  • Общая схема работы в рамках практического занятия.
  • Основные принципы работы нашего приложения.
  • Как развернуть ваше приложение.

3. Проверьте библиотеку и добавьте разрешение на создание манифеста.

Для использования API переходов в вашем приложении необходимо объявить зависимость от API определения местоположения и активности Google и указать разрешение com.google.android.gms.permission.ACTIVITY_RECOGNITION в манифесте приложения.

  1. Найдите в файле build.gradle запись TODO: Review play services library required for activity recognition . Для этого шага (шаг 1) никаких действий не требуется, просто проверьте объявленную зависимость, которая нам нужна. Она должна выглядеть примерно так:
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:19.0.1'
  1. В base модуле найдите TODO: Добавьте оба разрешения на распознавание активности в манифест в файле AndroidManifest.xml и добавьте приведенный ниже код в элемент <manifest> .
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

Теперь ваш код должен выглядеть примерно так:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.example.myapp">
<!-- TODO: Add both activity recognition permissions to the manifest. -->
<!-- Required for 28 and below. -->
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

  ...
</manifest>

Как видно из комментариев, для Android 10 необходимо добавить второе разрешение. Это требуется для разрешения времени выполнения, которое было добавлено в версии API 29.

Вот и всё! Теперь ваше приложение поддерживает распознавание активности, нам просто нужно добавить код для его активации.

Запустить приложение

Запустите приложение из Android Studio. Оно должно выглядеть точно так же. Мы пока не добавили код для отслеживания переходов, это будет в следующем разделе.

4. Проверка/запрос разрешений во время выполнения в Android

Хотя поддержка разрешений предусмотрена для версий API 28 и ниже, нам необходимо поддерживать разрешения во время выполнения в версиях API 29 и выше:

  • В файле MainActivity.java мы проверим, использует ли пользователь Android 10 (29) или более позднюю версию, и если да, то проверим разрешения на распознавание активности.
  • Если разрешения не будут предоставлены, мы перенаправим пользователя на заставку ( PermissionRationalActivity.java ), объясняющую, почему приложению требуется это разрешение, и предоставим ему возможность его подтвердить.

Проверка кода и версии Android.

В base модуле найдите в файле MainActivity.java фрагмент кода, содержащий команду TODO: Review check for devices with Android 10 (29+). Вы должны увидеть следующий фрагмент кода.

Обратите внимание: для этого раздела никаких действий не требуется.

// TODO: Review check for devices with Android 10 (29+).
private boolean runningQOrLater =
    android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q;

Как уже говорилось ранее, в Android 10 и выше требуется подтверждение разрешения времени выполнения android.permission.ACTIVITY_RECOGNITION . Мы используем эту простую проверку, чтобы решить, нужно ли проверять разрешения времени выполнения.

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

В base модуле найдите TODO: Review permission check for 29+ in MainActivity.java . Вы должны увидеть следующий фрагмент кода.

Обратите внимание: для этого раздела никаких действий не требуется.

// TODO: Review permission check for 29+.
if (runningQOrLater) {

   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
} else {
   return true;
}

Мы используем переменную, созданную на предыдущем шаге, чтобы определить, нужно ли проверять права доступа во время выполнения.

Для версий Q и выше мы проверяем и возвращаем результат проверки разрешений во время выполнения. Это часть более крупного метода activityRecognitionPermissionApproved() , который позволяет разработчику одним простым вызовом узнать, нужно ли запрашивать разрешение или нет.

Запросить разрешения во время выполнения и включить/отключить переходы распознавания активности.

В base модуле найдите TODO: Enable/Disable activity tracking and request for permissions if needed in MainActivity.java . Добавьте приведенный ниже код после комментария.

// TODO: Enable/Disable activity tracking and ask for permissions if needed.
if (activityRecognitionPermissionApproved()) {

   if (activityTrackingEnabled) {
      disableActivityTransitions();

   } else {
      enableActivityTransitions();
   }

} else {  
   // Request permission and start activity for result. If the permission is approved, we
   // want to make sure we start activity recognition tracking.
   Intent startIntent = new Intent(this, PermissionRationalActivity.class);
   startActivityForResult(startIntent, 0);

}

Здесь мы спрашиваем, одобрено ли распознавание активности. Если да, и распознавание активности уже включено, мы его отключаем. В противном случае мы его включаем.

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

Код запроса на проверку разрешения

В base модуле найдите TODO: Review permission request for activity recognition in PermissionRationalActivity.java . Вы должны увидеть следующий фрагмент кода.

Обратите внимание: для этого раздела никаких действий не требуется.

// TODO: Review permission request for activity recognition.
ActivityCompat.requestPermissions(
             this,
             new String[]{Manifest.permission.ACTIVITY_RECOGNITION},
             PERMISSION_REQUEST_ACTIVITY_RECOGNITION)

Это самая важная часть Activity, и именно её следует проверить. Код инициирует запрос на получение разрешения, когда пользователь его запрашивает.

Помимо этого, класс PermissionRationalActivity.java отображает обоснование того, почему пользователь должен одобрить разрешение на распознавание действий (рекомендуемая практика). Пользователь может либо нажать кнопку « Нет, спасибо» , либо кнопку «Продолжить» (которая запускает приведенный выше код).

Если хотите узнать больше, можете ознакомиться с файлом.

5. Регистрация/отмена регистрации получателя для переходов между действиями.

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

Создайте объект BroadcastReceiver для осуществления перехода.

В base модуле найдите TODO: Create a BroadcastReceiver to listen for activity transitions in MainActivity.java . Вставьте приведенный ниже фрагмент кода.

// TODO: Create a BroadcastReceiver to listen for activity transitions.
// The receiver listens for the PendingIntent above that is triggered by the system when an
// activity transition occurs.
mTransitionsReceiver = new TransitionsReceiver();

Зарегистрируйте BroadcastReceiver для перехода.

В base модуле найдите TODO: Register a BroadcastReceiver to listen for activity transitions in MainActivity.java (Это находится в onStart() ). Вставьте приведенный ниже фрагмент кода.

// TODO: Register a BroadcastReceiver to listen for activity transitions.
registerReceiver(mTransitionsReceiver, new IntentFilter(TRANSITIONS_RECEIVER_ACTION));

Теперь у нас есть способ получать обновления при возникновении переходов между активностями через PendingIntent.

Отменить регистрацию BroadcastReceiver

В base модуле найдите обработчик перехода активности «Unregister activity transition receiver when user leaves the app» в файле MainActivity.java (он находится в onStop() ). Вставьте приведенный ниже фрагмент кода.

// TODO: Unregister activity transition receiver when user leaves the app.
unregisterReceiver(mTransitionsReceiver);

Рекомендуется отменять регистрацию приемника при завершении Activity .

6. Настройка переходов между действиями и запрос обновлений.

Для начала получения обновлений о переходах между действиями необходимо выполнить следующие действия:

Создайте список переходов между действиями (ActivityTransitions), которые необходимо отслеживать.

Для создания объекта ActivityTransitionRequest необходимо создать список объектов ActivityTransition , представляющих собой переходы, которые вы хотите отслеживать. Объект ActivityTransition включает следующие данные:

  1. Тип активности, представленный классом DetectedActivity . API переходов поддерживает следующие виды активности:
  1. Тип перехода, представленный классом ActivityTransition . Типы переходов:

В base модуле найдите TODO: Add activity transitions to track in MainActivity.java . Добавьте приведенный ниже код после комментария.

// TODO: Add activity transitions to track.
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.WALKING)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
        .build());
activityTransitionList.add(new ActivityTransition.Builder()
        .setActivityType(DetectedActivity.STILL)
        .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
        .build());

Этот код добавляет отслеживаемые переходы в ранее пустой список.

Создать PendingIntent

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

В base модуле найдите TODO: Initialize PendingIntent, который будет срабатывать при переходе между активностями в MainActivity.java . Добавьте приведенный ниже код после комментария.

// TODO: Initialize PendingIntent that will be triggered when a activity transition occurs.
Intent intent = new Intent(TRANSITIONS_RECEIVER_ACTION);
mActivityTransitionsPendingIntent =
        PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);

Теперь у нас есть PendingIntent, который мы можем активировать при возникновении одного из ActivityTransition .

Создайте запрос на переход к действию (ActivityTransitionRequest) и запросите обновления.

Вы можете создать объект ActivityTransitionRequest , передав список объектов ActivityTransition в класс ActivityTransitionRequest.

В base модуле найдите строку Create request и listen for activity changes в MainActivity.java . Добавьте приведенный ниже код после комментария.

// TODO: Create request and listen for activity changes.
ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

Давайте рассмотрим код. Сначала мы создаём ActivityTransitionRequest из нашего списка переходов между действиями.

ActivityTransitionRequest request = new ActivityTransitionRequest(activityTransitionList);

Далее мы регистрируемся для получения обновлений о переходах между действиями, передавая созданный на предыдущем шаге экземпляр ActivityTransitionRequest и объект PendingIntent методу requestActivityTransitionUpdates() . Метод requestActivityTransitionUpdates() возвращает объект Task , который можно проверить на успешность или неудачу, как показано в следующем блоке кода:

// Register for Transitions Updates.
Task<Void> task =
        ActivityRecognition.getClient(this)
                .requestActivityTransitionUpdates(request, mActivityTransitionsPendingIntent);


task.addOnSuccessListener(
        new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void result) {
                activityTrackingEnabled = true;
                printToScreen("Transitions Api was successfully registered.");

            }
        });
task.addOnFailureListener(
        new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions Api could NOT be registered: " + e);
                Log.e(TAG, "Transitions Api could NOT be registered: " + e);

            }
        });

После успешной регистрации для получения обновлений о переходах активности ваше приложение получает уведомления в зарегистрированном объекте PendingIntent. Мы также устанавливаем переменную, указывающую на включение отслеживания активности, чтобы знать, следует ли его отключать/включать, если пользователь снова нажмет на кнопку.

Отключать обновления при закрытии приложения

Важно отключить обновления при закрытии приложения.

В base модуле найдите строку "Stop listening for activity changes" в файле MainActivity.java . Добавьте приведенный ниже код после комментария.

// TODO: Stop listening for activity changes.
ActivityRecognition.getClient(this).removeActivityTransitionUpdates(mActivityTransitionsPendingIntent)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                activityTrackingEnabled = false;
                printToScreen("Transitions successfully unregistered.");
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                printToScreen("Transitions could not be unregistered: " + e);
                Log.e(TAG,"Transitions could not be unregistered: " + e);
            }
        });

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

В base модуле найдите TODO: Disable activity transitions when user leaves the app in MainActivity.java in onPause() . Добавьте код ниже после комментария.

// TODO: Disable activity transitions when user leaves the app.
if (activityTrackingEnabled) {
    disableActivityTransitions();
}

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

7. Обработка событий

Когда происходит запрошенный переход между активностями, ваше приложение получает обратный вызов Intent . Из Intent можно извлечь объект ActivityTransitionResult , который содержит список объектов ActivityTransitionEvent . События упорядочены в хронологическом порядке, например, если приложение запрашивает тип активности IN_VEHICLE при переходах ACTIVITY_TRANSITION_ENTER и ACTIVITY_TRANSITION_EXIT , то оно получает объект ActivityTransitionEvent, когда пользователь начинает движение, и еще один, когда пользователь переходит к любой другой активности.

Давайте добавим код для обработки этих событий.

В base модуле найдите TODO: Extract activity transition information from listener in MainActivity.java in onReceive() of the BroadcastReceiver we created earlier. Добавьте код ниже после комментария.

// TODO: Extract activity transition information from listener.
if (ActivityTransitionResult.hasResult(intent)) {

    ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);

    for (ActivityTransitionEvent event : result.getTransitionEvents()) {

        String info = "Transition: " + toActivityString(event.getActivityType()) +
                " (" + toTransitionType(event.getTransitionType()) + ")" + "   " +
                new SimpleDateFormat("HH:mm:ss", Locale.US).format(new Date());

        printToScreen(info);
    }
}

Эта функция преобразует информацию в String и выведет её на экран.

Вот и всё, вы закончили! Попробуйте запустить приложение.

ВАЖНОЕ ЗАМЕЧАНИЕ: Воспроизвести изменения активности на эмуляторе сложно, поэтому мы рекомендуем использовать физическое устройство.

Вы должны иметь возможность отслеживать изменения активности.

Для достижения наилучших результатов установите приложение на реальное устройство и походите в нём. :)

8. Ознакомьтесь с кодом.

Вы создали простое приложение, которое отслеживает переходы между активностями и отображает их на экране.

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