Android Sleep API の Codelab

作成するアプリの概要

この Codelab では、ユーザーの睡眠時間を検出する Android アプリを作成します。アプリは次のことを行います。

  • 権限をリクエストする
  • Android Sleep API に登録する
  • API イベントを取得して UI に表示する
  • 不要になったら登録を解除する

必要なもの

  • 最新バージョンの Android Studio と Android Build Tools v21 以上
  • Android 10(API レベル 29)以上を搭載しているデバイス

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

できるだけすぐに開始できるように、たたき台として利用できるスターター プロジェクトを用意しています。ターミナル / コマンドラインで git --version と入力し、git がインストールされているかどうかを確認します。インストールされていない場合は、こちらの手順に沿ってインストールします。その後、以下のコマンドを実行するだけで、プロジェクトのクローンを作成できます。

 git clone https://github.com/googlecodelabs/android-sleep/

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

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

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

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

1401a11c10711a60.png

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

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

4bc64eb3b99eb0ae.png

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

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

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

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

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

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

  • MainActivity.kt: ユーザーがアプリを起動したときに、アプリのメイン画面をレンダリングします。
  • SleepReceiver.kt: API から睡眠イベントを抽出し、データベースに保存します。
  • BootReceiver.kt: デバイスの起動が完了したら Sleep API に再登録し、引き続き Sleep API イベントをリッスンします。

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

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

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

177045a302bf57d8.png

デバイスにアプリが表示されます。

30efe28b9757e1e7.png

311c83ca64556d94.png

睡眠状態を記録するコードをまだ追加していませんが、これは以降のセクションで行います。

次のセクションでは、必要なライブラリと権限を確認します。

build.gradle と AndroidManifest.xml を確認する

アプリで Sleep API を使用するには、アプリ マニフェストで Google Location and Activity Recognition API への依存関係を宣言し、com.google.android.gms.permission.ACTIVITY_RECOGNITION 権限を指定する必要があります。

  1. start モジュールの build.gradle ファイルで「TODO: Review play services library required for activity authentication」を検索します。このステップでは特に作業はありません。必要な依存関係が次のように宣言されていることを確認します。
    // TODO: Review play services library required for activity recognition.
    implementation 'com.google.android.gms:play-services-location:18.0.0'
  1. start モジュールの AndroidManifest.xml で「TODO: Add activity recognition and boot complete permissions」を検索し、次のコードを <manifest> 要素に追加します。
<!-- TODO: Add activity recognition and receive boot complete permissions. -->
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

コードは次のようになります。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.example.sleepcodelab">
...
<!-- TODO: Add activity recognition and receive boot complete permissions. -->
<!-- Required for 29+. -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  ...
</manifest>

コメントからわかるように、API バージョン 29 の実行時の権限を追加する必要があります。

これで、アプリが睡眠の操作の認識をサポートできるようになりました。上記のコードを追加するだけで、これを実現できます。

操作の認識の権限を確認する

必要に応じて、実行時の権限を確認してリクエストする必要があります。

  • MainActivity.kt で操作の認識の権限を確認します。
  • 権限が付与されていない場合は、displayPermissionSettingsSnackBar() を呼び出します。このスナックバーにより、権限が必要であることを説明し、ユーザーがシステム設定で権限を承認できるようにします。

start モジュールの MainActivity.kt で「TODO: Review Activity Recognition permission check」を検索します。次のコード スニペットが表示されます。

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

// TODO: Review Activity Recognition permission checking.
private fun activityRecognitionPermissionApproved(): Boolean {
   return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(
           this,
           Manifest.permission.ACTIVITY_RECOGNITION
   );
}

睡眠管理を有効または無効にする

start モジュールの MainActivity.kt で「TODO: Enable/Disable Sleep tracking and ask for permissions if needed」を検索し、onClickRequestSleepData() メソッドを次のコードに置き換えます。

// TODO: Enable/Disable sleep tracking and ask for permissions if needed.
fun onClickRequestSleepData(view: View) {
   if (activityRecognitionPermissionApproved()) {
       if (subscribedToSleepData) {
           unsubscribeToSleepSegmentUpdates(applicationContext, sleepPendingIntent)
       } else {
           subscribeToSleepSegmentUpdates(applicationContext, sleepPendingIntent)
       }
   } else {
       requestPermissionLauncher.launch(permission.ACTIVITY_RECOGNITION)
   }
}

操作の認識の権限が承認され、ユーザーが睡眠データに登録した場合、睡眠データの更新を受信できるように登録します。それ以外の場合は、登録を解除します。

権限が承認されていない場合、スプラッシュ画面を表示して権限が必要な理由を説明し、ユーザーがそこから権限を許可できるようにします。

PendingIntent を作成する

start モジュールの MainActivity.kt で「TODO: Create a PendingIntent for Sleep API events」を検索し、次のスニペットを貼り付けます。

// TODO: Create a PendingIntent for Sleep API events
sleepPendingIntent =
   SleepReceiver.createSleepReceiverPendingIntent(context = applicationContext)

これで、Sleep API データが利用可能になったときに通知を受け取れるようになりました。

Sleep API の更新をリクエストする

start モジュールの MainActivity.kt で「TODO: Request Sleep API updates」を検索し、次のスニペットを貼り付けます。

// TODO: Request Sleep API updates
val task = ActivityRecognition.getClient(context).requestSleepSegmentUpdates(
   pendingIntent,
   // Registers for both SleepSegmentEvent and SleepClassifyEvent data.
   SleepSegmentRequest.getDefaultSleepSegmentRequest()
)

task.addOnSuccessListener {
   mainViewModel.updateSubscribedToSleepData(true)
   Log.d(TAG, "Successfully subscribed to sleep data.")
}
task.addOnFailureListener { exception ->
   Log.d(TAG, "Exception when subscribing to sleep data: $exception")
}

Sleep API の更新への登録が成功すると、アプリは、登録した PendingIntent 内で睡眠検出通知を受け取るようになります。

起動完了時に再登録する

start モジュールの receiver/BootReceiver.kt で「TODO: Request Sleep API upon boot complete」を検索し、次のスニペットを貼り付けます。

// TODO: Request Sleep API upon boot complete
val subscribedToSleepData = repository.subscribedToSleepDataFlow.first()
if (subscribedToSleepData) {
   subscribeToSleepSegmentUpdates(
       context = context,
       pendingIntent = SleepReceiver.createSleepReceiverPendingIntent(context)
   )
}

このコードにより、デバイスの再起動後もアプリが Sleep API の更新を受け取れるようになります。

睡眠の分類または睡眠時間検出のイベントが発生すると、アプリは Intent コールバックを受け取ります。このインテントから、SleepSegmentEvent オブジェクトまたは SleepClassifyEvent オブジェクトのリストを抽出できます。

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

start モジュールの receiver/SleepReceiver.kt で「TODO: Extract sleep information from PendingIntent」を検索し、コメントの後に次のコードを追加します。

// TODO: Extract sleep information from PendingIntent.
if (SleepSegmentEvent.hasEvents(intent)) {
   val sleepSegmentEvents: List<SleepSegmentEvent> =
       SleepSegmentEvent.extractEvents(intent)
   addSleepSegmentEventsToDatabase(repository, sleepSegmentEvents)
} else if (SleepClassifyEvent.hasEvents(intent)) {
   val sleepClassifyEvents: List<SleepClassifyEvent> =
       SleepClassifyEvent.extractEvents(intent)
   addSleepClassifyEventsToDatabase(repository, sleepClassifyEvents)
}

これにより、SleepSegmentEvent または SleepClassifyEvent のデータが Room データベースに保存されます。MainActivity は、LiveData を使用して ViewModel 経由でそのデータベース情報にアクセスします。これらのクラスがどのように連携するかについて詳しくは、Room と View の Codelab をご覧ください。

これで完成です。アプリを実行してみましょう。

ケーブルを使用してデバイスをワークステーションに接続します。Android Studio で [Run] をクリックして、デバイスにアプリをインストールして実行します。操作の認識の権限を付与して、[SUBSCRIBE TO SLEEP DATA] ボタンをタップすると、最初の SleepClassifyEvent が約 10 分後に表示されます。SleepSegmentEvent は 1 日以内に表示されます。

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

コードレビュー(省略可)

次のコードレビューは省略可能です。これらのコードレビューにより、Sleep API とデータベース エンティティ間のデータ マッピングに関する詳細を把握できます。

  • data/db/SleepClassifyEventEntity.kt
  • data/db/SleepSegmentEventEntity.kt

リファレンス ドキュメント