Skip to content

Android 16のLive Update(ProgressStyle通知)に対応#5387

Merged
TinyKitten merged 6 commits intodevfrom
claude/android-16-live-update-mRQGu
Feb 21, 2026
Merged

Android 16のLive Update(ProgressStyle通知)に対応#5387
TinyKitten merged 6 commits intodevfrom
claude/android-16-live-update-mRQGu

Conversation

@TinyKitten
Copy link
Member

@TinyKitten TinyKitten commented Feb 20, 2026

https://claude.ai/code/session_016hTeobTKcEKjphKCN5p9nz

Summary by CodeRabbit

  • 新機能

    • Android API 36以上で列車のリアルタイム進行状況を通知で表示(駅名、次駅、種別、進行度、色分け、タップでアプリ復帰)。
    • 通知の開始・更新・停止をアプリから制御可能にし、表示がライフサイクルに連動。
    • アプリ側のフックとネイティブ連携用のヘルパーを追加し、通知制御を呼び出せるように。
    • 通知用のアイコンを追加。
  • Chores

    • ビルド設定に最低Android APIレベル36の下限を導入。

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Android向けの新規ネイティブKotlinモジュール LiveUpdateModule とそれを呼ぶ TypeScript ブリッジを追加し、APIレベル36以上で進捗通知の開始・更新・停止を行えるようにした。compileSdk の下限を 36 に設定し、通知アイコンを追加、フック側でネイティブ呼び出しを統合。

変更内容

コホート / ファイル(s) 概要
Android ネイティブモジュール
android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt, android/app/src/main/java/me/tinykitten/trainlcd/TrainLCDPackage.kt
新規 Kotlin モジュール LiveUpdateModule を追加し、startLiveUpdate/updateLiveUpdate/stopLiveUpdate@ReactMethod として公開。通知チャネル作成・進捗通知管理を実装し、TrainLCDPackage に登録。API <36 は短絡。
ビルド設定
android/app/build.gradle
compileSdkMath.max(rootProject.ext.compileSdkVersion as int, 36) に変更し、最低 compileSdk を 36 に設定。
リソース
android/app/src/main/res/drawable/ic_notification_live_update.xml
通知用ベクタードローアブル(24x24dp)を追加。
React/TS ブリッジと定数
src/utils/native/android/liveUpdateModule.ts, src/constants/native.ts
Android 専用 TS ラッパーと LiveUpdateState 型を追加。startLiveUpdate/updateLiveUpdate/stopLiveUpdate をエクスポート。IS_LIVE_UPDATE_ELIGIBLE_PLATFORM を追加(Android API >= 36 判定)。
フック連携
src/hooks/useUpdateLiveActivities.ts
フックのライフサイクルにネイティブ呼び出しを統合:開始時に startLiveUpdate、更新時に updateLiveUpdate、クリーンアップで stopLiveUpdate を呼び出すよう変更。

シーケンス図

sequenceDiagram
    participant React as React Component
    participant Hook as useUpdateLiveActivities
    participant Bridge as liveUpdateModule (TS)
    participant Native as LiveUpdateModule (Kotlin)
    participant System as Android Notification System

    React->>Hook: マウント/開始トリガー
    Hook->>Bridge: startLiveUpdate(state)
    Bridge->>Native: NativeModules.LiveUpdateModule.startLiveUpdate(state)
    Native->>Native: API >= 36 チェック
    Native->>Native: 通知チャネルを作成/確保
    Native->>System: 進捗通知を投稿
    System-->>Native: 表示完了

    React->>Hook: 進捗更新イベント
    Hook->>Bridge: updateLiveUpdate(state)
    Bridge->>Native: NativeModules.LiveUpdateModule.updateLiveUpdate(state)
    Native->>System: 通知内容・進捗を更新

    React->>Hook: アンマウント/停止
    Hook->>Bridge: stopLiveUpdate()
    Bridge->>Native: NativeModules.LiveUpdateModule.stopLiveUpdate()
    Native->>System: 通知をキャンセル
Loading

推定コードレビュー工数

🎯 3 (Moderate) | ⏱️ ~20 minutes

関連の可能性があるPR

ウサギの詩

🐰 ポケットでぴょん、通知が光るよ
進捗はちいさく、でも確かに伸びる
スタートで跳ねて、更新でぴょんぴょん
ネイティブとJSで手をつなぎながら
小さなアイコンが今日も知らせるよ ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PRタイトルは「Android 16のLive Update(ProgressStyle通知)に対応」で、変更内容と直接関連しており、主要な変更点を明確に表現しています。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/android-16-live-update-mRQGu

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/utils/native/android/liveUpdateModule.ts (1)

22-26: startLiveUpdate の引数が省略可能になっていますが、Kotlin 側は必須です。

state?: LiveUpdateState と省略可能にしていますが、Kotlin 側の startLiveUpdate(state: ReadableMap) は引数を必須としています。state なしで呼び出すと、Kotlin 側で null が渡され、state.getString() などで NullPointerException が発生する可能性があります。

実際の呼び出し元 (useUpdateLiveActivities.ts) では常に activityState を渡しているため、現時点では問題ありませんが、型の整合性のために修正を検討してください。

🔧 修正案
-export const startLiveUpdate = (state?: LiveUpdateState) => {
+export const startLiveUpdate = (state: LiveUpdateState) => {
   if (IS_LIVE_UPDATE_ELIGIBLE_PLATFORM) {
     LiveUpdateModule?.startLiveUpdate?.(state);
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/native/android/liveUpdateModule.ts` around lines 22 - 26, The
startLiveUpdate TypeScript wrapper declares an optional parameter (state?:
LiveUpdateState) but the native Kotlin signature startLiveUpdate(state:
ReadableMap) requires a non-null argument, so calling with undefined can lead to
a NullPointerException; make the TS parameter required (change startLiveUpdate
to accept state: LiveUpdateState) and/or add an explicit runtime guard that
throws a clear error if state is missing before calling
LiveUpdateModule.startLiveUpdate, referencing the startLiveUpdate function in
this file and the Kotlin startLiveUpdate(state: ReadableMap) to ensure
type/runtime consistency.
android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt (1)

74-76: @Suppress("NewApi") の使用は適切ですが、代替案も検討してください。

@Suppress("NewApi") で API 36+ のコードを許可していますが、呼び出し元 (startLiveUpdate, updateLiveUpdate) で既に API レベルチェックを行っているため、問題ありません。

ただし、より明示的なアプローチとして @RequiresApi(36) を使用し、呼び出し元でのチェックを強制する方法もあります。現状のままでも機能しますが、将来的なメンテナンス性を考慮すると良いかもしれません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt` around
lines 74 - 76, Replace the `@Suppress`("NewApi") on postProgressNotification with
a more explicit `@RequiresApi`(36) annotation (and remove the suppress) so callers
are required to perform the API-level check; update or confirm the callers
startLiveUpdate and updateLiveUpdate perform the API 36+ check before invoking
postProgressNotification to satisfy the new requirement and avoid adding runtime
suppressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt`:
- Around line 104-108: The Kotlin code appends "方面" to boundStationName causing
double suffixes or mixed-language output; change LiveUpdateModule.kt so it no
longer blindly appends "方面" — either (a) remove this append and trust the
TypeScript producer (useUpdateLiveActivities.ts) to supply the correctly
localized string, or (b) if you must append here, only do so for Japanese locale
and only when boundStationName does not already end with "方面" (check
boundStationName.endsWith("方面")); update the logic around the boundStationName
variable accordingly to avoid producing "渋谷方面方面" or adding "方面" to English names
like "Shibuya".

---

Nitpick comments:
In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt`:
- Around line 74-76: Replace the `@Suppress`("NewApi") on postProgressNotification
with a more explicit `@RequiresApi`(36) annotation (and remove the suppress) so
callers are required to perform the API-level check; update or confirm the
callers startLiveUpdate and updateLiveUpdate perform the API 36+ check before
invoking postProgressNotification to satisfy the new requirement and avoid
adding runtime suppressions.

In `@src/utils/native/android/liveUpdateModule.ts`:
- Around line 22-26: The startLiveUpdate TypeScript wrapper declares an optional
parameter (state?: LiveUpdateState) but the native Kotlin signature
startLiveUpdate(state: ReadableMap) requires a non-null argument, so calling
with undefined can lead to a NullPointerException; make the TS parameter
required (change startLiveUpdate to accept state: LiveUpdateState) and/or add an
explicit runtime guard that throws a clear error if state is missing before
calling LiveUpdateModule.startLiveUpdate, referencing the startLiveUpdate
function in this file and the Kotlin startLiveUpdate(state: ReadableMap) to
ensure type/runtime consistency.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt`:
- Around line 57-67: React Native may pass undefined as null to the Kotlin
bridge, so the non-nullable parameter in startLiveUpdate(state: ReadableMap) and
updateLiveUpdate(state: ReadableMap) can cause NPEs when
postProgressNotification reads values; change both method signatures to accept a
nullable ReadableMap? and guard against null (either return early or handle it
inside postProgressNotification), ensuring any calls like state.getString(...)
are only performed after a null-check; reference startLiveUpdate,
updateLiveUpdate, and postProgressNotification when applying the nullability
checks.
- Around line 46-54: createContentIntent() must guard against
getLaunchIntentForPackage(...) returning null: change createContentIntent to
return a nullable PendingIntent (PendingIntent?) and check the result of
reactApplicationContext.packageManager.getLaunchIntentForPackage(...); if intent
is null return null, otherwise call PendingIntent.getActivity(...) with the
non-null intent; update any callers that call createContentIntent() (e.g.,
notification builder code that calls setContentIntent) to handle a null return
by not calling setContentIntent when contentIntent is null so you don't pass a
null Intent into PendingIntent.getActivity.

@TinyKitten TinyKitten self-assigned this Feb 20, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt`:
- Around line 78-87: Replace direct ReadableMap getters in LiveUpdateModule
(where stationName, nextStationName, approaching, stopped, lineName, lineColor,
progress, trainTypeName, boundStationName, passingStationName are read) with
defensive extension helpers: add optString, optBoolean, optDouble extension
functions on ReadableMap that check hasKey and isNull and return a default, then
use state.optString("..."), state.optBoolean("...") and state.optDouble("...")
instead of getString/getBoolean/getDouble so missing or null keys won’t crash.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt (1)

43-49: 通知チャンネルの表示文言はリソース化推奨。
ユーザー表示文字列なので、strings.xmlへ移してローカライズ可能にしておくと保守性が上がります。

♻️ 例
-        val channel = NotificationChannel(
-            CHANNEL_ID,
-            "運行情報",
-            NotificationManager.IMPORTANCE_DEFAULT
-        ).apply {
-            description = "現在の運行状況をリアルタイムで表示します"
+        val channel = NotificationChannel(
+            CHANNEL_ID,
+            reactApplicationContext.getString(R.string.live_update_channel_name),
+            NotificationManager.IMPORTANCE_DEFAULT
+        ).apply {
+            description = reactApplicationContext.getString(R.string.live_update_channel_desc)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt` around
lines 43 - 49, The hardcoded Japanese title "運行情報" and description
"現在の運行状況をリアルタイムで表示します" in the NotificationChannel creation should be moved to
resources: add entries to strings.xml (e.g., notification_channel_title and
notification_channel_description), then update the NotificationChannel
instantiation in LiveUpdateModule (where NotificationChannel and CHANNEL_ID are
used) to use context.getString(R.string.notification_channel_title) and
context.getString(R.string.notification_channel_description) (or
requireContext().getString(...) / reactContext.getString(...) depending on
available context variable) instead of string literals so the texts are
localizable and maintainable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt`:
- Around line 43-49: The hardcoded Japanese title "運行情報" and description
"現在の運行状況をリアルタイムで表示します" in the NotificationChannel creation should be moved to
resources: add entries to strings.xml (e.g., notification_channel_title and
notification_channel_description), then update the NotificationChannel
instantiation in LiveUpdateModule (where NotificationChannel and CHANNEL_ID are
used) to use context.getString(R.string.notification_channel_title) and
context.getString(R.string.notification_channel_description) (or
requireContext().getString(...) / reactContext.getString(...) depending on
available context variable) instead of string literals so the texts are
localizable and maintainable.

@TinyKitten
Copy link
Member Author

@coderabbitai nitpickの件をi18n対応として起票してください

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 21, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@TinyKitten: かしこまりました。まず、このPRのレビューコメントを確認してnitpickの内容を特定し、i18n対応のissueを起票いたします。


🏁 Script executed:

#!/bin/bash
# Fetch PR review comments to find the nitpick
gh pr view 5387 --json reviews,comments --jq '.reviews[] | select(.body != "") | {author: .author.login, body: .body, url: .url}'
gh pr view 5387 --json comments --jq '.comments[] | {author: .author.login, body: .body, url: .url}'

Length of output: 25941


@TinyKitten: かしこまりました。nitpickのi18n対応issueを起票いたします。


🏁 Script executed:

#!/bin/bash
# Create an issue for i18n support of Live Update notification channel strings
gh issue create \
  --repo TrainLCD/MobileApp \
  --title "Live Update通知チャンネルの文言をi18n対応する" \
  --body "## 概要
Live Update機能の通知チャンネル作成時に使用されている文言がハードコードされているため、リソースファイルへ移してローカライズ可能にする必要があります。

## 対象ファイル
\`android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt\` (43-49行目)

## 現在の実装
\`\`\`kotlin
val channel = NotificationChannel(
    CHANNEL_ID,
    \"運行情報\",
    NotificationManager.IMPORTANCE_DEFAULT
).apply {
    description = \"現在の運行状況をリアルタイムで表示します\"
}
\`\`\`

## 改善内容
- 「運行情報」と「現在の運行状況をリアルタイムで表示します」を\`strings.xml\`へ移動
- \`reactApplicationContext.getString(R.string.xxx)\`を使用してリソースから取得
- 英語版のリソースも追加してローカライズ対応

## 参考
- PR `#5387`: https://github.com/TrainLCD/MobileApp/pull/5387
- レビューコメント: https://github.com/TrainLCD/MobileApp/pull/5387#issuecomment-3937648978

Requested by: `@TinyKitten`" \
  --assignee TinyKitten

Length of output: 49


@TinyKitten: issueを作成しました!

Issue #5388: Live Update通知チャンネルの文言をi18n対応する
🔗 #5388

Live Update通知チャンネルのハードコードされた文言(「運行情報」「現在の運行状況をリアルタイムで表示します」)をstrings.xmlへ移動し、ローカライズ対応する内容でissueを起票いたしました。


🧠 Learnings used
Learnt from: CR
Repo: TrainLCD/MobileApp PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-20T10:31:13.520Z
Learning: Write commit messages in Japanese (e.g., `テレメトリー送信機をリファクタリングしてnull状態を回避`)

Learnt from: CR
Repo: TrainLCD/MobileApp PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-20T10:31:13.520Z
Learning: Code reviews and feedback should be provided in Japanese

Learnt from: TinyKitten
Repo: TrainLCD/MobileApp PR: 5102
File: .env:0-0
Timestamp: 2026-01-18T01:25:10.303Z
Learning: TrainLCD/MobileAppのレビューコメントでは、CodeRabbitの組み込みツール(dotenv-linter等)による指摘と、プロジェクト固有のコーディング規約による指摘を明確に区別して記載すること。組み込みツールの指摘であることを明示的に伝えること。

Learnt from: CR
Repo: TrainLCD/MobileApp PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-02-20T10:31:13.520Z
Learning: Interaction with GitHub Copilot should be conducted in Japanese

Learnt from: CR
Repo: TrainLCD/MobileApp PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-20T10:31:33.813Z
Learning: Commit messages must be single-sentence statements in Japanese; prefix production hot fixes with `Hotfix:`

Learnt from: TinyKitten
Repo: TrainLCD/MobileApp PR: 0
File: :0-0
Timestamp: 2024-12-18T00:29:05.817Z
Learning: 翻訳ファイル(`assets/translations/en.json` と `assets/translations/ja.json`)の変更を要約する際、`backgroundPermissionDenied` のようなキーが追加されているのか削除されているのかを正確に確認する。

Learnt from: TinyKitten
Repo: TrainLCD/MobileApp PR: 0
File: :0-0
Timestamp: 2025-08-11T21:13:39.083Z
Learning: TinyKitten prefers concise code reviews for the TrainLCD/MobileApp repository. Keep review comments brief and to the point.

Learnt from: TinyKitten
Repo: TrainLCD/MobileApp PR: 4448
File: functions/src/funcs/tts.ts:214-232
Timestamp: 2025-08-11T20:23:23.071Z
Learning: PR `#4448` in TrainLCD/MobileApp is specifically about AI feedback summarization and labeling functionality, not about TTS (Text-to-Speech) improvements. The TTS code changes appear to be part of a modularization refactoring but are not the focus of this PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt (1)

106-111: 通知コンテンツの日本語文字列もi18n対応の検討を

"通過中""まもなく"がハードコードされています。Issue #5388でチャンネル文言のi18n対応が追跡されていますが、これらの通知コンテンツ文字列も同様にstrings.xmlへ移動することを検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt` around
lines 106 - 111, The notification content currently hardcodes Japanese fragments
("通過中" and "まもなく") when building contentTitle in LiveUpdateModule.kt (using
passingStationName, stopped, approaching, stationName, nextStationName); extract
these texts into strings.xml (e.g. notification_passing="%1$s 通過中" and
notification_approaching="まもなく %1$s" or separate short labels) and replace the
hardcoded literals with resource lookups via the module's Context
(getString(R.string.notification_passing, passingStationName) /
getString(R.string.notification_approaching, nextStationName)) or equivalent
formatting so the strings become localizable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@android/app/src/main/java/me/tinykitten/trainlcd/LiveUpdateModule.kt`:
- Around line 106-111: The notification content currently hardcodes Japanese
fragments ("通過中" and "まもなく") when building contentTitle in LiveUpdateModule.kt
(using passingStationName, stopped, approaching, stationName, nextStationName);
extract these texts into strings.xml (e.g. notification_passing="%1$s 通過中" and
notification_approaching="まもなく %1$s" or separate short labels) and replace the
hardcoded literals with resource lookups via the module's Context
(getString(R.string.notification_passing, passingStationName) /
getString(R.string.notification_approaching, nextStationName)) or equivalent
formatting so the strings become localizable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants