Activity Recognition Transition API の Codelab

1. はじめに

スマートフォンはどこにでも持ち歩くものですが、これまでは、ユーザーの環境やアクティビティが絶えず変化する状況にアプリが対応することは困難でした。

これまで、開発者はさまざまなシグナル(位置情報、センサーなど)を組み合わせて、歩行や運転などのアクティビティの開始と終了を判断するために、貴重なエンジニアリング時間を費やしていました。さらに、アプリがユーザー アクティビティの変化を個別に継続的にチェックすると、バッテリーの消耗が早まります。

Activity Recognition Transition API は、すべての処理を自動的に実行し、ユーザーのアクティビティが変化したタイミングという、実際に重要な情報のみを通知するシンプルな API を提供することで、これらの問題を解決します。アプリは、関心のあるアクティビティの遷移を登録するだけで、API が変更を通知します。

たとえば、メッセージ アプリで「車に乗り降りしたときに通知して」と尋ねて、ユーザーのステータスを「ビジー」に設定できます。同様に、駐車場検出アプリは「車から降りて歩き始めたら教えてください」と尋ねて、ユーザーの駐車場所を保存できます。

この Codelab では、Activity Recognition Transition API を使用して、ユーザーが歩行やランニングなどのアクティビティを開始または停止したタイミングを判断する方法を学びます。

前提条件

Android 開発とコールバックに精通している。

学習内容

  • アクティビティ遷移の登録
  • イベントの処理
  • 不要になったアクティビティ遷移の登録を解除する

必要なもの

  • Android Studio Bumblebee
  • Android デバイスまたはエミュレータ

2. スタートガイド

スターター プロジェクト リポジトリのクローンを作成する

できるだけすぐに開始できるように、たたき台として利用できるスターター プロジェクトを用意しています。git がインストールされている場合は、以下のコマンドをそのまま実行できます。(ターミナル / コマンドラインで「git --version」と入力して、コマンドが正しく実行されているかどうかを確認できます)。

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

git がない場合は、プロジェクトを ZIP ファイルとして取得できます。

プロジェクトをインポートする

Android Studio を起動し、ウェルカム画面で [Open an existing Android Studio project] を選択してプロジェクト ディレクトリを開きます。

プロジェクトが読み込まれた後、ローカルで行った変更の一部が Git によりトラッキングされていないことを示すアラートが表示される場合もあります。その場合は、[無視] または右上の [X] をクリックします。(変更を Git リポジトリにプッシュバックすることはありません)。

[Android] ビューの場合は、プロジェクト ウィンドウの左上に次のような画像が表示されます。([Project] ビューの場合は、プロジェクトを展開すると同じ画像が表示されます)。

d2363db913d8e5ad.png

2 つのフォルダ アイコン(basecomplete)があり、それぞれを「モジュール」と呼びます。

初回は、バックグラウンドでプロジェクトをコンパイルするのに数秒かかることがあります。その間、Android Studio の下部にあるステータスバーにスピナーが表示されます。

c9f23d5336be3cfe.png

コンパイルが完了するまで、コードを変更しないことをおすすめします。このコンパイルで、必要なすべてのコンポーネントを Android Studio に取り込むことができます。

また、「Reload for language changes to take effect?」のようなメッセージが表示された場合は、[Yes] を選択します。

スターター プロジェクトを理解する

これで、操作の認識を追加する準備が整いました。この Codelab のベースとして、ここでは base モジュールを使用します。つまり、各ステップで base にコードを追加していきます。

complete モジュールは、作業のチェックや、問題が発生した場合の参照用として使用できます。

主要コンポーネントの概要:

  • MainActivity: アクティビティの認識に必要なすべてのコードが含まれます。

エミュレータのセットアップ

Android Emulator のセットアップについてサポートが必要な場合は、アプリを実行するをご覧ください。

スターター プロジェクトを実行する

アプリを実行しましょう。

  • Android デバイスをパソコンに接続するか、エミュレータを起動します。
  • ツールバーで、プルダウン メニューから base の構成を選択し、その横にある緑色の三角形(Run)ボタンをクリックします。

a640a291ffaf62ad.png

  • 次のようなアプリが表示されます。

f58d4bb92ee77f41.png

  • アプリは、メッセージを出力する以外何もしません。次に、操作の認識を追加します。

まとめ

このステップでは、次のことを学習しました。

  • Codelab の一般的な設定。
  • アプリの基本
  • アプリをデプロイする方法。

3. ライブラリを確認してマニフェストに権限を追加する

アプリで Transition API を使用するには、アプリ マニフェストで Google Location and Activity Recognition API への依存関係を宣言し、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 モジュールの AndroidManifest.xml で「TODO: Add both activity recognition permissions to the manifest」を検索し、次のコードを <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 用の 2 つ目の権限を追加する必要があります。これは、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 モジュールの MainActivity.java で「TODO: Review permission check for 29+」を検索します。次のコード スニペットが表示されます。

このセクションでの作業は特にありません。

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

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

前の手順で作成した変数を使用して、実行時の権限を確認する必要があるかどうかを確認します。

Q 以降では、実行時の権限の結果を確認して返します。これは、権限をリクエストする必要があるかどうかを 1 回の簡単な呼び出しでデベロッパーに知らせることができる、activityRecognitionPermissionApproved() という大きなメソッドの一部です。

実行時の権限をリクエストし、アクティビティ認識の遷移を有効または無効にする

base モジュールの MainActivity.java で「TODO: Enable/Disable activity tracking and ask for permissions if needed」を検索し、コメントの後に次のコードを追加します。

// 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 モジュールの PermissionRationalActivity.java で「TODO: Review permission request for activity recognition」を検索します。次のコード スニペットが表示されます。

このセクションでの作業は特にありません。

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

これはアクティビティの最も重要な部分であり、確認すべき部分です。ユーザーがリクエストすると、コードが権限のリクエストをトリガーします。

それ以外の場合、PermissionRationalActivity.java クラスは、ユーザーがアクティビティ認識権限を承認する必要がある理由(ベスト プラクティス)を表示します。ユーザーは [No Thanks] ボタンまたは [Continue] ボタン(上記のコードをトリガー)をクリックできます。

詳しくは、ファイルをご確認ください。

5. アクティビティ遷移のレシーバの登録/登録解除

アクティビティ認識コードを設定する前に、システムによって発生した遷移アクションをアクティビティが処理できることを確認します。

遷移用の BroadcastReceiver を作成する

base モジュールの MainActivity.java で「TODO: Create a BroadcastReceiver to listen for activity transitions」を検索します。以下のスニペットを貼り付けます。

// 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 モジュールの MainActivity.java で「TODO: Register a BroadcastReceiver to listen for activity transitions」を検索します。(onStart() にあります)。以下のスニペットを貼り付けます。

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

これで、PendingIntent を介してアクティビティの遷移が発生したときに更新を取得できるようになりました。

BroadcastReceiver の登録を解除する

base モジュールの MainActivity.java で「Unregister activity transition receiver when user leaves the app」を検索します。(onStop() にあります)。以下のスニペットを貼り付けます。

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

Activity がシャットダウンするときにレシーバの登録を解除することをおすすめします。

6. アクティビティの遷移を設定して更新をリクエストする

アクティビティ遷移の更新を受け取るには、以下を実装する必要があります。

続く ActivitiyTransitions のリストを作成します

ActivityTransitionRequest オブジェクトを作成するには、トラッキングする遷移を表す ActivityTransition オブジェクトのリストを作成する必要があります。ActivityTransition オブジェクトには、次のデータが含まれます。

  1. アクティビティ タイプ。DetectedActivity クラスによって示されます。Transition API は、以下のアクティビティをサポートしています。
  1. 遷移タイプ。ActivityTransition クラスによって示されます。切り替え効果の種類は次のとおりです。

base モジュールの MainActivity.java で「TODO: Add activity transitions to track」を検索します。コメントの後に次のコードを追加します。

// 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 を作成する

前述のように、ActivityTransitionRequest の変更に関するアラートを受け取るには PendingIntent が必要です。そのため、ActivityTransitionRequest を設定する前に、PendingIntent を作成する必要があります。

base モジュールの MainActivity.java で「TODO: Initialize PendingIntent that will be triggered when a activity transition occurs」を検索し、コメントの後に次のコードを追加します。

// 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);

これで、ActivityTransition のいずれかが発生したときにトリガーできる PendingIntent ができました。

ActivityTransitionRequest を作成し、更新をリクエストする

ActivityTransitionRequest オブジェクトを作成するには、ActivityTransition のリストを ActivityTransitionRequest クラスに渡します。

base モジュールの MainActivity.java で「Create request and listen for activity changes」を検索します。コメントの後に次のコードを追加します。

// 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 モジュールの MainActivity.java で「Stop listening for activity changes」を検索します。コメントの後に次のコードを追加します。

// 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 モジュールの onPause()MainActivity.java で「TODO: Disable activity transitions when user leaves the app」を検索します。コメントの後に次のコードを追加します。

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

これで、アクティビティ遷移の変更をトラッキングする手順は完了です。これで、更新を処理するだけです。

7. イベントを処理する

リクエストされたアクティビティ遷移が発生すると、アプリは Intent コールバックを受け取ります。インテントから ActivityTransitionResult オブジェクトを抽出できます。このオブジェクトには、ActivityTransitionEvent オブジェクトのリストが格納されています。イベントは時系列順に並べられます。たとえば、アプリが ACTIVITY_TRANSITION_ENTER 遷移と ACTIVITY_TRANSITION_EXIT 遷移で IN_VEHICLE アクティビティ タイプをリクエストしている場合、ユーザーが自動車の運転を開始したときに ActivityTransitionEvent オブジェクトを受け取り、ユーザーが他のアクティビティに遷移したときにもう 1 つオブジェクトを受け取ります。

これらのイベントを処理するコードを追加しましょう。

base モジュールで、先ほど作成した BroadcastReceiver の onReceive()MainActivity.java で「TODO: Extract activity transition information from listener」を検索します。コメントの後に次のコードを追加します。

// 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. コードを確認する

アクティビティの遷移を追跡し、画面にリスト表示するシンプルなアプリを作成しました。

コードの全体を読み、この Codelab で加えた変更を振り返ることで、コードがどのように動作するかを理解しやすくなります。