feat(notification): add Android chronometer support (#3282)

* feat(notification): add Android chronometer support

* e2e test

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
GaëtanandLucas Nogueira authored and GitHub committed 2026-10-16 16:07:08 -03:00
1 parent aedd883cf3
commit f36ba7ba48
7 files changed
+262 -1

No files matched your search

@@ -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
@@ -199,6 +199,16 @@ class TauriNotificationManager(
}
mBuilder.setVisibility(notification.visibility ?: NotificationCompat.VISIBILITY_PRIVATE)
mBuilder.setOnlyAlertOnce(true)
// Chronometer support
notification.`when`?.let { mBuilder.setWhen(it) }
if (notification.usesChronometer) {
mBuilder.setUsesChronometer(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 ?: "")
@@ -320,6 +330,7 @@ class TauriNotificationManager(
)
notificationIntent.putExtra(NOTIFICATION_INTENT_KEY, request.id)
notificationIntent.putExtra(TimedNotificationPublisher.NOTIFICATION_KEY, notification)
notificationIntent.putExtra(TimedNotificationPublisher.CUSTOM_WHEN_KEY, request.`when` != null)
var flags = PendingIntent.FLAG_CANCEL_CURRENT
if (SDK_INT >= Build.VERSION_CODES.S) {
flags = flags or PendingIntent.FLAG_MUTABLE
@@ -481,7 +492,10 @@ class TimedNotificationPublisher : BroadcastReceiver() {
} else {
getParcelableExtraLegacy(intent, NOTIFICATION_KEY)
}
notification?.`when` = System.currentTimeMillis()
// keep the user provided `when` (e.g. a chronometer target), otherwise show the time it fired
if (!intent.getBooleanExtra(CUSTOM_WHEN_KEY, false)) {
notification?.`when` = System.currentTimeMillis()
}
val id = intent.getIntExtra(NOTIFICATION_INTENT_KEY, Int.MIN_VALUE)
if (id == Int.MIN_VALUE) {
Logger.error(Logger.tags("Notification"), "No valid id supplied", null)
@@ -535,6 +549,7 @@ class TimedNotificationPublisher : BroadcastReceiver() {
companion object {
var NOTIFICATION_KEY = "NotificationPublisher.notification"
var CRON_KEY = "NotificationPublisher.cron"
var CUSTOM_WHEN_KEY = "NotificationPublisher.customWhen"
}
}
+23
View File
@@ -137,6 +137,29 @@ 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.**
*/
chronometerCountDown?: boolean
}
/**
+30
View File
@@ -243,6 +243,36 @@ impl<R: Runtime> NotificationBuilder<R> {
self.on_close = Some(OnClose(Box::new(handler)));
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.**
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.
+16
View File
@@ -243,6 +243,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<i64>,
/// 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.
#[serde(default)]
pub(crate) chronometer_count_down: bool,
}
fn default_id() -> i32 {
@@ -272,6 +285,9 @@ impl Default for NotificationData {
ongoing: false,
auto_cancel: false,
silent: false,
when: None,
uses_chronometer: false,
chronometer_count_down: false,
}
}
}