diff --git a/android/app/src/main/java/com/shonar/recording/RecordingNotificationHelper.kt b/android/app/src/main/java/com/shonar/recording/RecordingNotificationHelper.kt index 9e0c010..4c00d65 100644 --- a/android/app/src/main/java/com/shonar/recording/RecordingNotificationHelper.kt +++ b/android/app/src/main/java/com/shonar/recording/RecordingNotificationHelper.kt @@ -34,9 +34,10 @@ import com.shonar.R object RecordingNotificationHelper { // v2: fresh id — sidesteps any stuck/user-locked importance lingering on // the old id. Created HIGH on every service start (see ensureChannel). - const val CHANNEL_ID = "shonar_recording_ongoing_v2" + const val CHANNEL_ID = "shonar_recording_ongoing_v3" private const val LEGACY_CHANNEL_ID = "shonar_recording" private const val LEGACY_CHANNEL_ID_V1 = "shonar_recording_ongoing" + private const val LEGACY_CHANNEL_ID_V2 = "shonar_recording_ongoing_v2" const val NOTIFICATION_ID = 1001 @Volatile @@ -51,20 +52,29 @@ object RecordingNotificationHelper { val mgr = ctx.getSystemService(NotificationManager::class.java) runCatching { mgr.deleteNotificationChannel(LEGACY_CHANNEL_ID) } runCatching { mgr.deleteNotificationChannel(LEGACY_CHANNEL_ID_V1) } - val existing = runCatching { mgr.getNotificationChannel(CHANNEL_ID) }.getOrNull() - if (existing == null) { - mgr.createNotificationChannel( - NotificationChannel( - CHANNEL_ID, - ctx.getString(R.string.notif_channel_recording), - NotificationManager.IMPORTANCE_HIGH, - ).apply { - description = ctx.getString(R.string.notif_channel_recording_desc) - setSound(null, null) - enableVibration(false) - }, - ) - } + runCatching { mgr.deleteNotificationChannel(LEGACY_CHANNEL_ID_V2) } + // A fully SILENT high-importance channel is classified "minimally + // impactful" by Android 14+ SystemUI, whose heads-up popup then waits + // on an interaction-decay timer — the popup pill showed ~10s after the + // tap while recording itself started in 0.2s (verified 2026-09-17 via + // StackScroll timestamps). One short 60ms tick makes the notification + // unambiguously alerting: the pill pops with the tap. Sound stays off; + // a beep at record start would be worse than the tick. + // Note: createNotificationChannel over an existing id refreshes + // vibration/sound (app-controlled), only importance stays user-locked. + mgr.createNotificationChannel( + NotificationChannel( + CHANNEL_ID, + ctx.getString(R.string.notif_channel_recording), + NotificationManager.IMPORTANCE_HIGH, + ).apply { + description = ctx.getString(R.string.notif_channel_recording_desc) + setSound(null, null) + vibrationPattern = longArrayOf(0, 60) + enableVibration(true) + lockscreenVisibility = Notification.VISIBILITY_PUBLIC + }, + ) channelReady = true }