From 012f4dcc26162d581987f18ac771f7e83bef21cb Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Sat, 30 May 2026 17:54:59 +0530 Subject: [PATCH] Restructure notification channels by intent and importance Notifications were too intrusive: install-success and export sat on IMPORTANT_HIGH channels and produced heads-up alerts for every app, while genuine download failures were buried in the IMPORTANCE_MIN downloads channel where they were easily missed. Restructure the channels around user intent: - Download progress -> MIN, silent, no badge - Installed apps -> LOW, silent (was HIGH) - Updates available -> DEFAULT, silent - App export -> LOW, silent (was HIGH) - Errors & alerts -> HIGH (new, the only heads-up channel) Failed downloads, installer-status errors and the unarchive auth prompt now route to the high-importance alerts channel so they aren't lost, while successful installs/exports stay quiet. Android ignores importance edits on an already-created channel, so the install/export channels get new IDs and the retired IDs (including the folded-in account channel) are deleted on next launch. --- app/src/main/java/com/aurora/Constants.kt | 17 +++- .../com/aurora/store/util/NotificationUtil.kt | 92 +++++++++++++------ app/src/main/res/values/strings.xml | 2 +- 3 files changed, 78 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/aurora/Constants.kt b/app/src/main/java/com/aurora/Constants.kt index 29e9d0ce6..d42a65c56 100644 --- a/app/src/main/java/com/aurora/Constants.kt +++ b/app/src/main/java/com/aurora/Constants.kt @@ -42,11 +42,22 @@ object Constants { const val UPDATE_URL_NIGHTLY = "https://auroraoss.com/downloads/AuroraStore/Feeds/nightly_feed.json" - const val NOTIFICATION_CHANNEL_EXPORT = "NOTIFICATION_CHANNEL_EXPORT" - const val NOTIFICATION_CHANNEL_INSTALL = "NOTIFICATION_CHANNEL_INSTALL" + // Channel IDs carry a version suffix where the importance changed from a previous + // release: Android ignores importance edits on an already-created channel, so a new ID + // is the only way to roll out a lower importance. Retired IDs are listed in + // [LEGACY_NOTIFICATION_CHANNELS] so they can be deleted on next launch. + const val NOTIFICATION_CHANNEL_EXPORT = "NOTIFICATION_CHANNEL_EXPORT_V2" + const val NOTIFICATION_CHANNEL_INSTALL = "NOTIFICATION_CHANNEL_INSTALLED" const val NOTIFICATION_CHANNEL_DOWNLOADS = "NOTIFICATION_CHANNEL_DOWNLOADS" const val NOTIFICATION_CHANNEL_UPDATES = "NOTIFICATION_CHANNEL_UPDATES" - const val NOTIFICATION_CHANNEL_ACCOUNT = "NOTIFICATION_CHANNEL_ACCOUNT" + const val NOTIFICATION_CHANNEL_ALERTS = "NOTIFICATION_CHANNEL_ALERTS" + + // Channels removed or superseded by a higher-versioned ID; deleted on next launch. + val LEGACY_NOTIFICATION_CHANNELS = listOf( + "NOTIFICATION_CHANNEL_EXPORT", + "NOTIFICATION_CHANNEL_INSTALL", + "NOTIFICATION_CHANNEL_ACCOUNT" + ) const val GITLAB_URL = "https://gitlab.com/AuroraOSS/AuroraStore" const val URL_DISPENSER = "https://auroraoss.com/api/auth" diff --git a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt index 36b6cea7a..a75b276d0 100644 --- a/app/src/main/java/com/aurora/store/util/NotificationUtil.kt +++ b/app/src/main/java/com/aurora/store/util/NotificationUtil.kt @@ -32,38 +32,42 @@ object NotificationUtil { fun createNotificationChannel(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val notificationManager = context.getSystemService() + val notificationManager = context.getSystemService()!! + + // Drop channels retired or superseded by a lower-importance replacement, so the + // user no longer sees stale entries in system settings. + Constants.LEGACY_NOTIFICATION_CHANNELS.forEach { + notificationManager.deleteNotificationChannel(it) + } + val channels = ArrayList() - channels.add( - NotificationChannel( - Constants.NOTIFICATION_CHANNEL_INSTALL, - context.getString(R.string.notification_channel_install), - NotificationManager.IMPORTANCE_HIGH - ).apply { - setSound(null, null) - } - ) - channels.add( - NotificationChannel( - Constants.NOTIFICATION_CHANNEL_EXPORT, - context.getString(R.string.notification_channel_export), - NotificationManager.IMPORTANCE_HIGH - ) - ) - channels.add( - NotificationChannel( - Constants.NOTIFICATION_CHANNEL_ACCOUNT, - context.getString(R.string.notification_channel_account), - NotificationManager.IMPORTANCE_HIGH - ) - ) + + // Quiet, ongoing progress for active downloads. MIN keeps it collapsed and out + // of the status bar where possible. channels.add( NotificationChannel( Constants.NOTIFICATION_CHANNEL_DOWNLOADS, context.getString(R.string.notification_channel_downloads), NotificationManager.IMPORTANCE_MIN - ) + ).apply { + setSound(null, null) + setShowBadge(false) + } ) + + // Silent confirmation that an app finished installing/updating. LOW so it lands + // in the shade without buzzing for every app during a bulk update. + channels.add( + NotificationChannel( + Constants.NOTIFICATION_CHANNEL_INSTALL, + context.getString(R.string.notification_channel_install), + NotificationManager.IMPORTANCE_LOW + ).apply { + setSound(null, null) + } + ) + + // New updates are available to install. DEFAULT but silent. channels.add( NotificationChannel( Constants.NOTIFICATION_CHANNEL_UPDATES, @@ -73,7 +77,29 @@ object NotificationUtil { setSound(null, null) } ) - notificationManager!!.createNotificationChannels(channels) + + // Background app export. LOW since it's user-initiated and non-urgent. + channels.add( + NotificationChannel( + Constants.NOTIFICATION_CHANNEL_EXPORT, + context.getString(R.string.notification_channel_export), + NotificationManager.IMPORTANCE_LOW + ).apply { + setSound(null, null) + } + ) + + // Things the user genuinely needs to act on: failed downloads/installs, pending + // user action and authentication prompts. HIGH so these aren't missed. + channels.add( + NotificationChannel( + Constants.NOTIFICATION_CHANNEL_ALERTS, + context.getString(R.string.notification_channel_alerts), + NotificationManager.IMPORTANCE_HIGH + ) + ) + + notificationManager.createNotificationChannels(channels) } } @@ -91,7 +117,14 @@ object NotificationUtil { largeIcon: Bitmap? = null, message: String? = null ): Notification { - val builder = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_DOWNLOADS) + // Terminal failures go to the high-importance alerts channel so they aren't lost in + // the silent downloads channel; everything else stays quiet. + val channelId = if (download.status == DownloadStatus.FAILED) { + Constants.NOTIFICATION_CHANNEL_ALERTS + } else { + Constants.NOTIFICATION_CHANNEL_DOWNLOADS + } + val builder = NotificationCompat.Builder(context, channelId) builder.setSmallIcon(R.drawable.ic_notification_outlined) builder.setContentTitle(download.displayName) builder.setContentIntent(getContentIntentForDetails(context, download.packageName)) @@ -197,11 +230,12 @@ object NotificationUtil { packageName: String, displayName: String, content: String? - ): Notification = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_INSTALL) + ): Notification = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ALERTS) .setSmallIcon(R.drawable.ic_install) .setContentTitle(displayName) .setContentText(content) .setContentIntent(getContentIntentForDetails(context, packageName)) + .setCategory(Notification.CATEGORY_ERROR) .build() fun getUpdateNotification(context: Context): Notification = @@ -289,7 +323,7 @@ object NotificationUtil { .build() fun getUnarchiveAuthNotification(context: Context, packageName: String): Notification = - NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ACCOUNT) + NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_ALERTS) .setSmallIcon(R.drawable.ic_account) .setContentTitle(context.getString(R.string.authentication_required_title)) .setContentText(context.getString(R.string.authentication_required_unarchive)) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8712221e4..81707e8b8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -211,7 +211,7 @@ "Optionally you can choose Native installer, but then you can not install bundled (split) APKs, so choice is yours." "New update available" "You can not install bundled (split) apps via Native Installer. Change your installer to Session, Services or Root." - Account-related notification + Errors & alerts App export notification Install notification Downloads notification