Activity Recognition Transition API を使ってみた

こんにちは、 id:numanuma08 です。個人的に興味を持ったのでAndroidのActivity Recognition Transition API を使ってみました。

Activity Recognition Transition API とは

Google Play Serviceで利用可能なAPIでTransition APIとも呼ばれます。ユーザーのアクティビティを監視し、変更をトリガーにアプリケーションを呼び出します。

ユーザーがアクティビティを開始したときや終了したときに検出する  |  Android デベロッパー  |  Android Developers

Google Developers Japan: Activity Recognition Transition API による状況認識機能をすべてのデベロッパーに開放

ActivityRecognitionClient  |  Google Play services  |  Google Developers

スマートフォンの電池使用量を最小限にしつつ、複数のセンサーを組み合わせてユーザーの行動を検知できます。日本語で書かれたAPIガイドやCodelabもあるのでとりあえず使って見る環境はありますが、アプリに組み込むとなると色々気にしたほうが良い部分もあったので紹介します。

とりあえず使ってみる

とりあえず使ってみるにはAPIガイドを読みつつCodelabを実践するといいです。

ユーザーがアクティビティを開始したときや終了したときに検出する  |  Android デベロッパー  |  Android Developers

Activity Recognition Transition API Codelab  |  Android Developers

気をつけたほうがいいこと

Permissionについて

APIガイドではAndroidManifestに定義するPermissionがcom.google.android.gms.permission.ACTIVITY_RECOGNITIONのみですが実際にはSDK 29以上ではandroid.permission.ACTIVITY_RECOGNITIONの定義も必要です。android.permission.ACTIVITY_RECOGNITIONはRuntime PermissionなのでAPI呼び出し前にcheckSelfPermissionrequestPermissionが必須です。この辺りの詳しい情報はGoogle Fitに関するドキュメント内にありました。

Android の権限  |  Google Fit  |  Google Developers

ActivityTransitionResult内のデータについて

Transition APIの結果はIntentで通知されるため、Intentをハンドルできるコンポーネントでデータ取得を行います。一般に、BroadcastReceiverやServiceが利用されると思います。Intent内のデータに含まれるActivityTransitionResultは検知されたActivityを配列transitionEventsに格納しています。

私は最初、アクティビティの検知結果がなぜ配列になるのか理解できませんでした。APIガイドには次のように説明があります。

イベントは時系列順に並べられます。たとえば、アプリが IN_VEHICLE アクティビティ タイプを対象に ACTIVITY_TRANSITION_ENTER 遷移と ACTIVITY_TRANSITION_EXIT 遷移に関するリクエストを行っていた場合、ユーザーが自動車の運転を開始したときに ActivityTransitionEvent オブジェクトを受け取り、ユーザーが他のアクティビティに遷移したときにもう 1 つオブジェクトを受け取ります。

この文章を読んでも理解できなかったのですが、実際にAPIを使ってみるとわかりました。例えば、IN_VEHICLEACTIVITY_TRANSITION_ENTERACTIVITY_TRANSITION_EXITそしてWALINGACTIVITY_TRANSITION_ENTERACTIVITY_TRANSITION_EXITをリクエストしていると次のように呼び出しが発生します。

  1. ユーザーが歩き始める → transitionType: WALKING, activityType: ACTIVITY_TRANSITION_ENTER
  2. ユーザーが歩くのをやめる → transitionType: WALKING, activityType: ACTIVITY_TRANSITION_EXIT
  3. ユーザーが運転を開始する → transitionType: IN_VEHICLE, activityType: ACTIVITY_TRANSITION_ENTER
  4. ユーザーが運転をやめて歩きだす → [transitionType: IN_VEHICLE, activityType: ACTIVITY_TRANSITION_EXIT, transitionType: WALKING, activityType: ACTIVITY_TRANSITION_ENTER]

「運転をやめて歩く」のように短い時間で発生するイベントに対しては1つのActivityTransitionResult内に複数のActivityTransitionEventが含まれるようです。

イベントが検知されるまでのタイムラグについて

ユーザーがアクティビティを開始・終了してもすぐに検知イベントは発生しません。むしろ、ラグがあることで運転中の信号待ちなどをトリガーに運転終了の誤検知を防いでいるようです。体感ですが、歩きや走りの場合は開始終了がそれぞれ1分くらい遅れます。運転の場合は運転開始から1分くらい、運転終了から3分くらいしてから通知が得られました。

具体的にどれくらい遅延が発生したのかは、ActivityTransitionEvent.getElapsedRealTimeNanosを読み取ると実際にイベントが発生したエポックタイムが取得できるようなので、この数値と通知が来た時の時間を比較したらわかるかもしれません。

デバッグ方法

Transition APIはデバッグ・テスト方法がありません。内部的には加速度センサーやジャイロセンサーを利用しているのでエミュレーターや実機を振るとカジュアルに試すことが可能です。2018年のDroidkaigiで@kakka_blog さんがセンサーの値を保存しておいてデバッグで利用する方法を紹介されていました。

20180209 DroidKaigi2018 ActivityRecognition simulation - Speaker Deck

具体的な実装方法を紹介されたブログ記事もありました。

ActivityRecognition と加速度センサーのエミュレート – ギャップロ

この辺りの手法を利用すると良いでしょう。

バックグラウンドからの起動

Codelabでは動的BroadcastReceiverでアクティビティ遷移イベントをキャッチしていましたが実際にはアプリのプロセスが終了している状態でも静的BroadcastReceiverやServiceを使ってアクティビティ遷移イベントをキャッチ可能です。イベントはシステムが発行しているので、AndroidManifestでexported=falseを宣言して問題ありません。

また、アクティビティ遷移イベントの検知を使ってフォアグラウンドサービスの起動も可能です。Sdk 32から制約が厳しくなりましたがアクティビティ遷移イベントの検知はフォアグラウンドサービスを起動可能な例外的な状況として定義されています。

フォアグラウンド サービスの起動に関する制限  |  Android 12  |  Android Developers

終わりに

実際に製品に組み込んで運用した経験はないのですが、私が今の段階で調査したActivity Transition APIの情報は以上です。APIが提供してくれる機能は面白く、Google Mapsのタイムライン機能はたぶんこのAPIを内部的に利用していると思います。一方で、実際に使ってみないとわからない細かい不思議なポイントもいくつかありました。もし、Activity Transition APIを使ってアプリ開発で調査している人がいましたら参考にしてください。