Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #123 +/- ##
============================================
- Coverage 57.90% 56.25% -1.66%
- Complexity 65 111 +46
============================================
Files 29 31 +2
Lines 5096 5475 +379
Branches 232 303 +71
============================================
+ Hits 2951 3080 +129
- Misses 2111 2355 +244
- Partials 34 40 +6
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds UnifiedPush as an optional backend for push notifications on Android, providing an alternative to FCM for devices without Google Play Services (e.g., Fairphone with de-Googled ROMs). It is gated behind a new unified-push Cargo feature flag that defaults to disabled.
Changes:
- Adds 5 new Tauri commands (
registerForUnifiedPush,unregisterFromUnifiedPush,getUnifiedPushDistributors,saveUnifiedPushDistributor,getUnifiedPushDistributor) implemented in Kotlin and exposed via the Rust/JS layer across all platforms (Android implementation, unsupported-platform stubs on iOS/macOS/desktop) - Adds Android receiver
TauriUnifiedPushMessagingServiceto handle UnifiedPush lifecycle events (new endpoint, messages, unregistration, errors), plus a publicUnifiedPushMessageHandlerinterface for custom message handling - Updates the build system, permissions, TypeScript API, and documentation to cover the new functionality
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
src/models.rs |
Adds UnifiedPushEndpointResponse, UnifiedPushDistributorsResponse, and UnifiedPushDistributorResponse Rust structs (feature-gated) |
src/mobile.rs |
Implements the 5 new UnifiedPush methods on mobile, gated by #[cfg(feature = "unified-push")] |
src/macos.rs |
Adds stub methods that return "only supported on Android" errors |
src/desktop.rs |
Adds stub methods that return "not supported on desktop" errors |
src/commands.rs |
Adds Tauri command handlers for the 5 UnifiedPush operations |
src/lib.rs |
Registers the 5 new commands with the plugin |
build.rs |
Writes enableUnifiedPush to android/build.properties based on the Cargo feature flag |
android/build.gradle.kts |
Reads enableUnifiedPush property, adds BuildConfig.ENABLE_UNIFIED_PUSH, adds JitPack dependency |
android/settings.gradle |
Adds JitPack Maven repository |
android/src/main/AndroidManifest.xml |
Registers TauriUnifiedPushMessagingService as an exported broadcast receiver |
android/src/main/java/.../NotificationPlugin.kt |
Implements the 5 UP commands, permission handling, and callbacks; adds getNotificationManager() helper |
android/src/main/java/.../TauriUnifiedPushMessagingService.kt |
New BroadcastReceiver that dispatches UnifiedPush events to NotificationPlugin and runs the fallback notification path |
android/src/main/java/.../UnifiedPushMessageHandler.kt |
New public interface for custom message handling |
android/src/main/java/.../TauriNotificationManager.kt |
Adds ic_notification drawable and app icon as additional small-icon fallbacks |
guest-js/index.ts |
Adds TS types, function implementations, event listeners, and exports for the full UnifiedPush API |
permissions/default.toml |
Adds all 5 new allow-* permission identifiers to the default set |
permissions/schemas/schema.json |
Adds allow/deny schema entries for the 5 new commands |
permissions/autogenerated/reference.md |
Auto-generated permission reference updated |
permissions/autogenerated/commands/*.toml |
New per-command permission files for the 5 new commands |
Cargo.toml |
Adds unified-push feature declaration and updates crate description |
README.md |
Adds UnifiedPush setup, usage, and API reference documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/NotificationPlugin.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/NotificationPlugin.kt
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…n.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…essagingService.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…essagingService.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriNotificationManager.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/test/java/app/tauri/notification/NotificationPluginUnifiedPushTest.kt
Show resolved
Hide resolved
android/src/test/java/app/tauri/notification/TauriUnifiedPushMessagingServiceTest.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/NotificationPlugin.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 28 out of 28 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Show resolved
Hide resolved
android/src/test/java/app/tauri/notification/NotificationPluginUnifiedPushTest.kt
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 33 out of 33 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/test/java/app/tauri/notification/TauriUnifiedPushMessagingServiceTest.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/NotificationPlugin.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/NotificationPlugin.kt
Outdated
Show resolved
Hide resolved
…otifications and adress reviews
…cation storage tests for icon handling
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 38 out of 38 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/app/tauri/notification/TauriNotificationManager.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriNotificationManager.kt
Outdated
Show resolved
Hide resolved
android/src/main/java/app/tauri/notification/TauriUnifiedPushMessagingService.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 35 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
android/src/main/java/app/tauri/notification/TauriNotificationManager.kt
Outdated
Show resolved
Hide resolved
… enhance security by stripping auth tokens from JSON
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 35 out of 35 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private val avatarExecutor: java.util.concurrent.ExecutorService = java.util.concurrent.Executors.newFixedThreadPool(4) { runnable -> | ||
| Thread(runnable).apply { isDaemon = true; name = "avatar-download" } |
There was a problem hiding this comment.
The TauriNotificationManager class creates a background thread-pool executor avatarExecutor on instantiation (line 53) for downloading avatar images, and exposes a destroy() method (line 233) to shut it down. However, destroy() is never called in NotificationPlugin — there is no override of any lifecycle method (e.g., onStop, onDestroy) that invokes manager.destroy(). This means the executor's threads will persist until the process terminates, and in long-running applications this could prevent proper resource cleanup.
| val raw = BitmapFactory.decodeStream(connection.inputStream) | ||
| connection.disconnect() | ||
| if (raw == null) return@Callable null | ||
| cropCircle(raw) |
There was a problem hiding this comment.
The submitAvatarDownload function downloads remote avatar images via HTTP connections and streams the response directly into BitmapFactory.decodeStream without any size limit check (line 188). A malicious or misconfigured server could return an extremely large image, leading to an OutOfMemoryError or significant memory pressure. Consider adding a Content-Length check before decoding, or use BitmapFactory.Options with inSampleSize / inJustDecodeBounds to limit the decoded image size.
| private fun prefetchAvatars(msgStyle: MessagingStyleConfig): Map<String, Bitmap?> { | ||
| val authToken = msgStyle.authToken | ||
| // Collect all unique URLs that need downloading | ||
| val urlsToDownload = mutableSetOf<String>() | ||
| msgStyle.user.iconUrl?.let { urlsToDownload.add(it) } | ||
| for (msg in msgStyle.messages) { | ||
| msg.sender?.iconUrl?.let { urlsToDownload.add(it) } | ||
| } | ||
| if (urlsToDownload.isEmpty()) return emptyMap() | ||
|
|
||
| // Submit all downloads in parallel | ||
| val futures = urlsToDownload.associateWith { url -> submitAvatarDownload(url, authToken) } | ||
|
|
||
| // Resolve all futures (total wait is ~10s max, not N×10s) | ||
| return futures.mapValues { (_, future) -> resolveAvatarFuture(future) } |
There was a problem hiding this comment.
The prefetchAvatars function calls resolveAvatarFuture (line 226) which blocks the calling thread for up to 10 seconds per batch. When this is called from buildNotification (which itself can be called on the main thread), this blocking network call can trigger an Android Application Not Responding (ANR) error. The notification-building path should either offload the entire operation to a background thread (e.g., using a coroutine or AsyncTask-style mechanism) before calling buildNotification, or the avatar download should happen entirely asynchronously and the notification should be updated once the download completes.
| #[command] | ||
| pub(crate) async fn unregister_from_unified_push<R: Runtime>( | ||
| _app: AppHandle<R>, | ||
| notification: State<'_, Notifications<R>>, | ||
| ) -> Result<()> { | ||
| notification.unregister_from_unified_push() | ||
| } | ||
|
|
||
| #[command] | ||
| pub(crate) async fn get_unified_push_distributors<R: Runtime>( | ||
| _app: AppHandle<R>, | ||
| notification: State<'_, Notifications<R>>, | ||
| ) -> Result<serde_json::Value> { | ||
| notification.get_unified_push_distributors() | ||
| } | ||
|
|
||
| #[command] | ||
| pub(crate) async fn save_unified_push_distributor<R: Runtime>( | ||
| _app: AppHandle<R>, | ||
| notification: State<'_, Notifications<R>>, | ||
| distributor: String, | ||
| ) -> Result<()> { | ||
| notification.save_unified_push_distributor(distributor) | ||
| } | ||
|
|
||
| #[command] | ||
| pub(crate) async fn get_unified_push_distributor<R: Runtime>( | ||
| _app: AppHandle<R>, | ||
| notification: State<'_, Notifications<R>>, | ||
| ) -> Result<serde_json::Value> { | ||
| notification.get_unified_push_distributor() | ||
| } |
There was a problem hiding this comment.
The unregister_from_unified_push Rust command handler (in src/commands.rs, line 64) is declared as async but calls a synchronous method notification.unregister_from_unified_push(). While this works correctly, for consistency with other synchronous commands in the file (like cancel, create_channel etc. which are non-async), the async keyword is unnecessary here and also for get_unified_push_distributors (line 72), save_unified_push_distributor (line 81), and get_unified_push_distributor (line 89). These commands don't need to be async since their implementations return synchronously.
This implements UnifiedPush as backend for notifications, as some manufacturers, such as Fairphone don't have FCM/the required Google services for FCM installed and some users may prefer using a FOSS backend