0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

NotionとHealth Connectを連携して、自分専用の健康管理Androidアプリを作った

0
Posted at

はじめに

ダッシュボート.png

少し前から高血圧の薬を飲み始めました。

そのため、毎日朝晩の2回、血圧と脈拍を測定しています。

私は普段からNotionをよく使っているので、測定した血圧と脈拍もNotionのデータベースに記録していました。

一方で、運動不足も気になっていたため、少し意識して歩くようにもしていました。

歩数はAndroidスマホに自動的に蓄積されています。

ただし、データの保存先は分かれていました。

データ 保存先
血圧・脈拍 Notion
歩数 Androidスマホ

血圧、脈拍、歩数を一つの場所で確認できれば、日々の健康管理がもう少し分かりやすくなるはずです。

そこで、AndroidスマホとNotionの間で健康データを同期するアプリを作りました。

作ったもの

Screenshot_20260603-125120.png

今回作成したアプリは、Health ConnectとNotionを連携するAndroidアプリです。

リポジトリはこちらです。

アプリ名は Health Notion Sync です。

主な役割は次の3つです。

  1. スマホに蓄積された歩数をNotionへ同期する
  2. Notionに保存した血圧・脈拍をHealth Connectへ同期する
  3. 血圧・脈拍をアプリから簡単にNotionへ登録する

データの流れは次のようになります。

歩数は スマホからNotionへ 同期します。

血圧と脈拍は Notionからスマホへ 同期します。

血圧・脈拍については、アプリからNotionへ新規登録することもできます。

使用技術

今回のアプリでは、以下の技術を使用しています。

用途 技術
Androidアプリ Kotlin
健康データ連携 AndroidX Health Connect Client
バックグラウンド処理 AndroidX WorkManager
非同期処理 Kotlin Coroutines
外部データ保存 Notion API
音声入力 Android RecognizerIntent
トークン保護 Android Keystore
APK配布 GitHub Actions / GitHub Releases

大規模なバックエンドはありません。

AndroidアプリからHealth ConnectとNotion APIへ直接アクセスする構成です。

Health Connectとは

Health Connectは、Android端末上で健康データを共有するための仕組みです。

歩数、心拍、血圧などのデータを、対応するアプリ間で連携できます。

今回のアプリでは、次のレコードを扱っています。

StepsRecord
BloodPressureRecord
HeartRateRecord

歩数はHealth Connectから読み取り、血圧と心拍はHealth Connectへ書き込みます。

必要な権限もアプリ側で明示的に要求します。

private val requiredPermissions = setOf(
    HealthPermission.getReadPermission(StepsRecord::class),
    HealthPermission.getReadPermission(BloodPressureRecord::class),
    HealthPermission.getWritePermission(BloodPressureRecord::class),
    HealthPermission.getReadPermission(HeartRateRecord::class),
    HealthPermission.getWritePermission(HeartRateRecord::class),
    HealthPermission.PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND
)

ユーザーが許可した健康データだけを読み書きします。

歩数をNotionへ同期する

Health Connectには、細かい時間単位で歩数レコードが保存されています。

そのままNotionへ送ると行数が増えすぎるため、アプリ側で1日単位に集計しています。

同期の流れは次の通りです。

実装では、Health Connectの集計APIを使用しています。

val aggregatedByDay = client.aggregateGroupByPeriod(
    AggregateGroupByPeriodRequest(
        metrics = setOf(StepsRecord.COUNT_TOTAL),
        timeRangeFilter = aggregateTimeRange,
        timeRangeSlicer = Period.ofDays(1),
        dataOriginFilter = GOOGLE_FIT_DATA_ORIGIN_FILTER
    )
)

Notion側では、1日につき1行だけ保持します。

同じ日のデータがすでに存在する場合は、新しいレコードが追加されるたびに行を更新します。

これにより、当日分の歩数も途中経過を含めて同期できます。

Notionの歩数データベースには、最低限以下のプロパティが必要です。

プロパティ 既定名
日時 Date 日付
歩数 Number 歩数

歩数レコード.png

歩数.png

血圧と脈拍をHealth Connectへ同期する

血圧と脈拍は、逆方向に同期します。

Notionに保存しているデータを取得し、Health Connectへ書き込みます。
バイタルレコード.png

血圧と脈拍は、それぞれHealth Connectのレコードに変換します。

BloodPressureRecord(
    time = measuredAt,
    zoneOffset = zoneOffset,
    metadata = Metadata.manualEntry("notion-blood-pressure-$measuredAt"),
    systolic = Pressure.millimetersOfMercury(systolic),
    diastolic = Pressure.millimetersOfMercury(diastolic)
)
HeartRateRecord(
    startTime = measuredAt,
    startZoneOffset = zoneOffset,
    endTime = measuredAt.plusSeconds(1),
    endZoneOffset = zoneOffset,
    samples = listOf(
        HeartRateRecord.Sample(measuredAt, heartRate)
    ),
    metadata = Metadata.manualEntry("notion-heart-rate-$measuredAt")
)

Notionのバイタルデータベースには、以下のプロパティが必要です。

プロパティ 既定名
測定日時 Date Date
最高血圧 Number 収縮期
最低血圧 Number 拡張期
脈拍 Number 脈拍

プロパティ名はアプリの設定画面から変更できます。

Health Connectへ同期した血圧と脈拍は、Google FitまたはGoogle Healthなどの対応アプリから確認できます。

スマホ側では、血圧の推移がグラフ化されるため、Notionを開かなくても変化をすぐに確認できます。
Screenshot_20260603-125638.png

音声入力で血圧・脈拍を登録する

毎日朝晩の2回、血圧と脈拍を入力するのは意外と面倒です。

入力の手間を減らすため、アプリからNotionへ直接登録できるようにしました。

さらに、Androidの音声認識機能を使って、3項目をまとめて入力できます。

血圧計を見ながら、3つの数字を連続して話します。
Screenshot_20260603-125655.png

例えば、次のように話します。

128、82、71

すると、以下の入力欄へ自動的に反映されます。

入力欄
最高血圧 128
最低血圧 82
脈拍 71

項目名を話す必要はありません。

血圧計を見ながら3つの数字だけを順番に読み上げればよいので、毎日の入力がかなり楽になりました。

音声認識の起動にはAndroidの RecognizerIntent を利用しています。

val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply {
    putExtra(
        RecognizerIntent.EXTRA_LANGUAGE_MODEL,
        RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
    )
    putExtra(
        RecognizerIntent.EXTRA_LANGUAGE,
        Locale.JAPAN.toLanguageTag()
    )
    putExtra(
        RecognizerIntent.EXTRA_PROMPT,
        "最高血圧、最低血圧、脈拍の順に数字を話してください。"
    )
    putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3)
}

音声認識結果から数字を抽出し、3項目へ順番に割り当てます。

単純な数字抽出だけでなく、認識結果が連結された場合もある程度補正します。

例えば、3つの数字が一続きになってしまった場合は、最高血圧、最低血圧、脈拍として妥当そうな組み合わせを選びます。

判定には、おおよそ以下の範囲を利用しています。

項目 想定範囲
最高血圧 80〜250
最低血圧 40〜150
脈拍 40〜220

加えて、最高血圧が最低血圧以下になった場合は、大きなペナルティを与えます。

if (systolic <= diastolic) {
    penalty += 1_000.0 + (diastolic - systolic)
}

音声認識が完全でなくても、実用上使いやすくするための補正です。

手動同期と自動同期

TOP画面には、次の3つの同期ボタンがあります。

ボタン 処理
歩数 スマホの歩数をNotionへ同期
血圧・脈拍 Notionの血圧・脈拍をHealth Connectへ同期
すべて 両方をまとめて同期

同期対象は直近30日間です。

TOP画面では、スマホ側とNotion側の最新データ日時も確認できます。

また、WorkManagerを利用して、1日1回の自動同期も実装しています。

val request = PeriodicWorkRequestBuilder<AutoSyncWorker>(
    1,
    TimeUnit.DAYS
)
    .setInitialDelay(
        initialAutoSyncDelayMillis(autoSyncTime),
        TimeUnit.MILLISECONDS
    )
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
    )
    .build()

ネットワーク接続がある場合に実行されます。

自動同期の最終成功時刻、最終失敗時刻、失敗理由、次回予定時刻も画面から確認できます。

Screenshot_20260603-125737.png

設定画面

設定画面では、以下を入力します。

  • Notion Integration Token
  • 歩数データベースのData Source ID
  • 歩数データベースのプロパティ名
  • 血圧・脈拍データベースのData Source ID
  • 血圧・脈拍データベースのプロパティ名
  • 自動同期時刻
  • Health Connectの権限

注意点として、設定するのはNotionの Database IDではなくData Source ID です。

また、Notion側では対象のデータベースをIntegrationへ共有しておく必要があります。

トークンは端末内で暗号化して保存する

Notion Integration Tokenを平文で保存するのは避けたいところです。

このアプリでは、Android Keystoreを利用して端末内で暗号化して保存しています。

また、Notion Integrationには必要最小限のデータベースだけを共有します。

個人用アプリでも、健康データを扱う以上、最低限の権限管理は必要です。

GitHub ActionsでAPKを自動リリースする

main ブランチへ変更を反映すると、GitHub Actionsで署名済みAPKをビルドし、GitHub Releaseへ添付します。

APKファイル名にはバージョン番号を含めています。

sync-health-notion-v0.0.8-release.apk

Androidの versionCode は、SemVerから生成しています。

val versionCode = major * 10_000 + minor * 100 + patch

例えば 0.0.8 の場合、versionCode8 です。

この仕組みにより、新しいAPKを端末へ上書きインストールできます。

作って良かったこと

このアプリを作って一番良かったのは、健康管理のデータがつながったことです。

Notionでは、今まで記録していた血圧と脈拍に加えて、歩数もグラフ化できるようになりました。

歩数グラフを見ると、

「今日は少し歩数が少ない」

「最近は良い感じに歩けている」

という変化がすぐに分かります。

良い状態を維持したいと思うので、以前より歩くことに前向きになりました。

血圧と脈拍もHealth Connectへ同期できるため、Google FitまたはGoogle Healthからすぐにグラフを確認できます。

血圧も順調に下がってきました。
バイタルデータ.png

数値の変化が見えると、健康管理を続けるモチベーションになります。

おわりに

今回作ったものは、健康管理を劇的に変える大規模なサービスではありません。

毎日少し面倒だった入力作業を減らし、別々の場所に保存されていたデータをつないだ、小さなAndroidアプリです。

ただ、自分自身が毎日使うものなので、効果は大きく感じています。

エンジニアとして便利なものを作るだけでなく、自分の生活を少し改善するためにコードを書くのも良いものです。

今後も実際に使いながら、必要な機能を追加していく予定です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?