diff --git a/.changes/notification-chronometer.md b/.changes/notification-chronometer.md new file mode 100644 index 0000000000..47536f8b9e --- /dev/null +++ b/.changes/notification-chronometer.md @@ -0,0 +1,6 @@ +--- +"tauri-plugin-notification": minor:feat +"@tauri-apps/plugin-notification": minor:feat +--- + +Added chronometer support for Android notifications with `when`, `usesChronometer`, and `chronometerCountDown` options. This allows displaying an automatically updating elapsed time counter directly in the notification, useful for time-tracking apps, ongoing calls, or timers. diff --git a/plugins/notification/android/src/main/java/Notification.kt b/plugins/notification/android/src/main/java/Notification.kt index bf10f3dc4e..b8dcde5e74 100644 --- a/plugins/notification/android/src/main/java/Notification.kt +++ b/plugins/notification/android/src/main/java/Notification.kt @@ -38,6 +38,9 @@ class Notification { var sourceJson: String? = null var visibility: Int? = null var number: Int? = null + var `when`: Long? = null + var usesChronometer: Boolean = false + var chronometerCountDown: Boolean = false fun getSound(context: Context, defaultSound: Int): String? { var soundPath: String? = null diff --git a/plugins/notification/android/src/main/java/TauriNotificationManager.kt b/plugins/notification/android/src/main/java/TauriNotificationManager.kt index a8912739bb..c69cb77b78 100644 --- a/plugins/notification/android/src/main/java/TauriNotificationManager.kt +++ b/plugins/notification/android/src/main/java/TauriNotificationManager.kt @@ -199,6 +199,23 @@ class TauriNotificationManager( } mBuilder.setVisibility(notification.visibility ?: NotificationCompat.VISIBILITY_PRIVATE) mBuilder.setOnlyAlertOnce(true) + + // Chronometer support + if (notification.`when` != null) { + mBuilder.setWhen(notification.`when`!!) + mBuilder.setShowWhen(true) + } + if (notification.usesChronometer) { + mBuilder.setUsesChronometer(true) + if (notification.`when` == null) { + mBuilder.setWhen(System.currentTimeMillis()) + mBuilder.setShowWhen(true) + } + } + if (notification.chronometerCountDown) { + mBuilder.setChronometerCountDown(true) + } + mBuilder.setSmallIcon(notification.getSmallIcon(context, getDefaultSmallIcon(context))) mBuilder.setLargeIcon(notification.getLargeIcon(context)) val iconColor = notification.getIconColor(config?.iconColor ?: "") diff --git a/plugins/notification/guest-js/index.ts b/plugins/notification/guest-js/index.ts index 685c60c203..fa7f0684e6 100644 --- a/plugins/notification/guest-js/index.ts +++ b/plugins/notification/guest-js/index.ts @@ -136,6 +136,31 @@ interface Options { * Sets the number of items this notification represents on Android. */ number?: number + /** + * Set the time that the event occurred. Notifications in the panel are sorted by this time. + * Represented as milliseconds since the epoch (e.g. `Date.now()`). + * + * **Android only.** + */ + when?: number + /** + * Show the {@link when} field as a stopwatch. Instead of presenting `when` as a timestamp, + * the notification will show an automatically updating display of the minutes and seconds + * since `when`. Useful when showing an elapsed time (like an ongoing phone call or timer). + * + * **Android only.** + */ + usesChronometer?: boolean + /** + * Sets the Chronometer to count down instead of counting up. + * This is only relevant if {@link usesChronometer} is `true`. + * If `when` is set to a time in the future, the chronometer will count down to zero then stop. + * + * **Android only.** + * + * @since API 24 (Android 7.0+) + */ + chronometerCountDown?: boolean } interface ScheduleInterval { diff --git a/plugins/notification/src/lib.rs b/plugins/notification/src/lib.rs index 8b79c87306..ea79e00a26 100644 --- a/plugins/notification/src/lib.rs +++ b/plugins/notification/src/lib.rs @@ -205,6 +205,36 @@ impl NotificationBuilder { self.data.silent = true; self } + + /// Set the time that the event occurred (milliseconds since epoch). + /// Notifications in the panel are sorted by this time. + /// When used with `uses_chronometer`, the notification shows elapsed time since this value. + /// + /// **Android only.** + pub fn when(mut self, when: i64) -> Self { + self.data.when = Some(when); + self + } + + /// Show the `when` field as a stopwatch. + /// Instead of presenting `when` as a timestamp, the notification will show an automatically + /// updating display of the minutes and seconds since `when`. + /// + /// **Android only.** + pub fn uses_chronometer(mut self) -> Self { + self.data.uses_chronometer = true; + self + } + + /// Sets the Chronometer to count down instead of counting up. + /// Only relevant if `uses_chronometer` is true. + /// If `when` is set to a time in the future, the chronometer will count down to zero then stop. + /// + /// **Android only (API 24+).** + pub fn chronometer_count_down(mut self) -> Self { + self.data.chronometer_count_down = true; + self + } } /// Extensions to [`tauri::App`], [`tauri::AppHandle`], [`tauri::WebviewWindow`], [`tauri::Webview`] and [`tauri::Window`] to access the notification APIs. diff --git a/plugins/notification/src/models.rs b/plugins/notification/src/models.rs index 02134e4dbc..f38eebbd91 100644 --- a/plugins/notification/src/models.rs +++ b/plugins/notification/src/models.rs @@ -176,6 +176,19 @@ pub struct NotificationData { pub(crate) auto_cancel: bool, #[serde(default)] pub(crate) silent: bool, + /// Set the time that the event occurred (milliseconds since epoch). + /// Notifications in the panel are sorted by this time. + /// Android only. + pub(crate) when: Option, + /// Show the `when` field as a stopwatch (elapsed time). + /// Android only. + #[serde(default)] + pub(crate) uses_chronometer: bool, + /// Sets the Chronometer to count down instead of counting up. + /// Only relevant if `uses_chronometer` is true. + /// Android only (API 24+). + #[serde(default)] + pub(crate) chronometer_count_down: bool, } fn default_id() -> i32 { @@ -205,6 +218,9 @@ impl Default for NotificationData { ongoing: false, auto_cancel: false, silent: false, + when: None, + uses_chronometer: false, + chronometer_count_down: false, } } }