Skip to content

Commit fee50ec

Browse files
committed
chore(notification): expand Android diagnostics and document iOS parity path
1 parent bb74ff5 commit fee50ec

3 files changed

Lines changed: 59 additions & 3 deletions

File tree

plugins/notification/android/src/main/java/NotificationPlugin.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import android.content.Context
1212
import android.content.Intent
1313
import android.os.Build
1414
import android.webkit.WebView
15+
import app.tauri.Logger
1516
import app.tauri.PermissionState
1617
import app.tauri.annotation.Command
1718
import app.tauri.annotation.InvokeArg
@@ -220,6 +221,7 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
220221

221222
super.load(webView)
222223
this.webView = webView
224+
Logger.debug(Logger.tags("Notification"), "Plugin load started")
223225
synchronized(this) {
224226
pendingActionEvents.clear()
225227
pendingActionEventKeys.clear()
@@ -239,6 +241,7 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
239241
this.manager = manager
240242

241243
notificationManager = activity.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
244+
Logger.debug(Logger.tags("Notification"), "Plugin load complete; awaiting notification intents")
242245

243246
val intent = activity.intent
244247
intent?.let {
@@ -248,16 +251,21 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
248251

249252
override fun onNewIntent(intent: Intent) {
250253
super.onNewIntent(intent)
254+
Logger.debug(Logger.tags("Notification"), "onNewIntent received action=${intent.action}")
251255
onIntent(intent)
252256
}
253257

254258
fun onIntent(intent: Intent) {
255259
if (Intent.ACTION_MAIN != intent.action) {
260+
Logger.debug(Logger.tags("Notification"), "Ignoring intent action=${intent.action}")
256261
return
257262
}
263+
Logger.debug(Logger.tags("Notification"), "Processing ACTION_MAIN intent for notification action")
258264
val dataJson = manager.handleNotificationActionPerformed(intent, notificationStorage)
259265
if (dataJson != null) {
260266
dispatchActionPerformed(dataJson)
267+
} else {
268+
Logger.debug(Logger.tags("Notification"), "No action payload extracted from intent")
261269
}
262270
}
263271

@@ -284,12 +292,17 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
284292
return
285293
}
286294
}
295+
Logger.debug(Logger.tags("Notification"), "Dispatching actionPerformed event immediately")
287296
trigger("actionPerformed", payload)
288297
}
289298

290299
@Command
291300
fun show(invoke: Invoke) {
292301
val notification = invoke.parseArgs(Notification::class.java)
302+
Logger.debug(
303+
Logger.tags("Notification"),
304+
"show called id=${notification.id} title=${notification.title} actionTypeId=${notification.actionTypeId} hasSchedule=${notification.schedule != null}"
305+
)
293306
val id = manager.schedule(notification)
294307

295308
invoke.resolveObject(id)
@@ -298,23 +311,30 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
298311
@Command
299312
fun batch(invoke: Invoke) {
300313
val args = invoke.parseArgs(BatchArgs::class.java)
314+
Logger.debug(
315+
Logger.tags("Notification"),
316+
"batch called notifications=${args.notifications.size}"
317+
)
301318

302319
val ids = manager.schedule(args.notifications)
303320
notificationStorage.appendNotifications(args.notifications)
321+
Logger.debug(Logger.tags("Notification"), "batch scheduled ids=$ids")
304322

305323
invoke.resolveObject(ids)
306324
}
307325

308326
@Command
309327
fun cancel(invoke: Invoke) {
310328
val args = invoke.parseArgs(CancelArgs::class.java)
329+
Logger.debug(Logger.tags("Notification"), "cancel called notifications=${args.notifications}")
311330
manager.cancel(args.notifications)
312331
invoke.resolve()
313332
}
314333

315334
@Command
316335
fun removeActive(invoke: Invoke) {
317336
val args = invoke.parseArgs(RemoveActiveArgs::class.java)
337+
Logger.debug(Logger.tags("Notification"), "removeActive called notifications=${args.notifications.size}")
318338

319339
if (args.notifications.isEmpty()) {
320340
notificationManager.cancelAll()
@@ -334,29 +354,40 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
334354
@Command
335355
fun getPending(invoke: Invoke) {
336356
val notifications= notificationStorage.getSavedNotifications()
357+
Logger.debug(Logger.tags("Notification"), "getPending returning count=${notifications.size}")
337358
val result = Notification.buildNotificationPendingList(notifications)
338359
invoke.resolveObject(result)
339360
}
340361

341362
@Command
342363
fun registerActionTypes(invoke: Invoke) {
343364
val args = invoke.parseArgs(RegisterActionTypesArgs::class.java)
365+
Logger.debug(
366+
Logger.tags("Notification"),
367+
"registerActionTypes called types=${args.types.size}"
368+
)
344369
notificationStorage.writeActionGroup(args.types)
345370
invoke.resolve()
346371
}
347372

348373
@Command
349374
fun registerActionListenerReady(invoke: Invoke) {
350375
val pending = JSArray()
376+
var drainedCount = 0
351377
synchronized(this) {
352378
isActionListenerReady = true
353379
for (event in pendingActionEvents) {
354380
pending.put(event.payload)
355381
}
382+
drainedCount = pendingActionEvents.size
356383
pendingActionEvents.clear()
357384
pendingActionEventKeys.clear()
358385
persistPendingActionEventsLocked()
359386
}
387+
Logger.debug(
388+
Logger.tags("Notification"),
389+
"Action listener marked ready; drained pending actionPerformed events=$drainedCount"
390+
)
360391
invoke.resolveObject(pending)
361392
}
362393

plugins/notification/android/src/main/java/TauriNotificationManager.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ class TauriNotificationManager(
7070
val input = results?.getCharSequence(REMOTE_INPUT_KEY)
7171
dataJson.put("inputValue", input?.toString())
7272
val menuAction = data.getStringExtra(ACTION_INTENT_KEY)
73+
Logger.debug(
74+
Logger.tags("Notification"),
75+
"Action performed id=$notificationId actionId=$menuAction removable=$isRemovable"
76+
)
7377
dismissVisibleNotification(notificationId)
7478
dataJson.put("actionId", menuAction)
7579
var request: JSONObject? = null
@@ -87,6 +91,10 @@ class TauriNotificationManager(
8791
if (savedNotification?.actionTypeId != null) {
8892
request.put("actionTypeId", savedNotification.actionTypeId)
8993
}
94+
Logger.debug(
95+
Logger.tags("Notification"),
96+
"Recovered missing notification metadata from storage id=$notificationId actionTypeId=${savedNotification?.actionTypeId}"
97+
)
9098
} else if (!request.has("id")) {
9199
request.put("id", notificationId)
92100
}
@@ -136,17 +144,23 @@ class TauriNotificationManager(
136144

137145
fun schedule(notification: Notification): Int {
138146
val notificationManager = NotificationManagerCompat.from(context)
147+
Logger.debug(
148+
Logger.tags("Notification"),
149+
"Scheduling notification id=${notification.id} actionTypeId=${notification.actionTypeId} hasSchedule=${notification.schedule != null}"
150+
)
139151
return trigger(notificationManager, notification)
140152
}
141153

142154
fun schedule(notifications: List<Notification>): List<Int> {
143155
val ids = mutableListOf<Int>()
144156
val notificationManager = NotificationManagerCompat.from(context)
157+
Logger.debug(Logger.tags("Notification"), "Scheduling batch notifications count=${notifications.size}")
145158

146159
for (notification in notifications) {
147160
val id = trigger(notificationManager, notification)
148161
ids.add(id)
149162
}
163+
Logger.debug(Logger.tags("Notification"), "Scheduled batch notification ids=$ids")
150164

151165
return ids
152166
}
@@ -324,6 +338,10 @@ class TauriNotificationManager(
324338
intent.putExtra(NOTIFICATION_OBJ_INTENT_KEY, notificationJson.toString())
325339
val schedule = notification.schedule
326340
intent.putExtra(NOTIFICATION_IS_REMOVABLE_KEY, schedule == null || schedule.isRemovable())
341+
Logger.debug(
342+
Logger.tags("Notification"),
343+
"Built action intent notificationId=${notification.id} action=$action removable=${schedule == null || schedule.isRemovable()}"
344+
)
327345
return intent
328346
}
329347

plugins/notification/ios/Sources/NotificationHandler.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,17 @@ public class NotificationHandler: NSObject, NotificationHandlerProtocol {
7575
// Delivery note:
7676
// This event is emitted as soon as the OS callback arrives. If the JS side has
7777
// not attached `onAction` yet (for example during cold start), the event may be
78-
// missed. Android currently mitigates this with a pending-action queue drained
79-
// by JS after listener registration.
78+
// missed. Android mitigates this with a pending-action queue and an explicit
79+
// "listener ready" handshake from JS.
8080
//
81-
// TODO: Consider applying the same queue/drain handshake on iOS for parity.
81+
// iOS parity guidance:
82+
// 1. Queue `ReceivedNotification` payloads at this boundary when listeners are not ready.
83+
// 2. Expose a small command that JS calls after `onAction` registration to drain the queue.
84+
// 3. Consider persistence (for example `UserDefaults`) so queued actions survive process restarts.
85+
// 4. Keep the payload contract aligned with Android (`actionId`, `inputValue`, `notification`).
86+
//
87+
// Important: queue payloads should be built from `UNNotificationResponse` fields directly
88+
// where possible, since `notificationsMap` is in-memory and may be unavailable after restart.
8289
try? self.plugin?.trigger(
8390
"actionPerformed",
8491
data: ReceivedNotification(

0 commit comments

Comments
 (0)