From 0fa77a12e05dfb56ca860cf9bd7cf0f4972a6bc2 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Fri, 10 Jan 2025 20:34:54 +0100 Subject: [PATCH] Use bulk notification method --- .../NotificationFetcher.kt | 59 +++++++++++-------- .../NotificationHelper.java | 21 +++++-- .../keylesspalace/tusky/MainActivityTest.kt | 2 +- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationFetcher.kt b/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationFetcher.kt index 5f9b0c5908..4cf60c4a1b 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationFetcher.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationFetcher.kt @@ -1,9 +1,13 @@ package com.keylesspalace.tusky.components.systemnotifications +import android.Manifest import android.app.NotificationManager import android.content.Context +import android.content.pm.PackageManager import android.util.Log import androidx.annotation.WorkerThread +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationManagerCompat import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.NewNotificationsEvent import com.keylesspalace.tusky.components.systemnotifications.NotificationHelper.filterNotification @@ -16,8 +20,6 @@ import com.keylesspalace.tusky.util.HttpHeaderLink import com.keylesspalace.tusky.util.isLessThan import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject -import kotlin.time.Duration.Companion.milliseconds -import kotlinx.coroutines.delay /** Models next/prev links from the "Links" header in an API response */ data class Links(val next: String?, val prev: String?) { @@ -52,6 +54,10 @@ class NotificationFetcher @Inject constructor( private val eventHub: EventHub ) { suspend fun fetchAndShow() { + if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { + return + } + for (account in accountManager.getAllAccountsOrderedByActive()) { if (account.notificationsEnabled) { try { @@ -59,6 +65,8 @@ class NotificationFetcher @Inject constructor( Context.NOTIFICATION_SERVICE ) as NotificationManager + val notificationManagerCompat = NotificationManagerCompat.from(context) + // Create sorted list of new notifications val notifications = fetchNewNotifications(account) .filter { filterNotification(notificationManager, account, it) } @@ -70,37 +78,38 @@ class NotificationFetcher @Inject constructor( // (and should therefore adhere to the notification config). eventHub.dispatch(NewNotificationsEvent(account.accountId, notifications)) - val notificationsByType: Map> = notifications.groupBy { it.type } + val newNotifications = ArrayList() + val notificationsByType: Map> = notifications.groupBy { it.type } notificationsByType.forEach { notificationsGroup: Map.Entry> -> - // NOTE: Enqueue the summary first: Only this way one can avoid running into notification rate limits; - // which also would prohibit the group summary to be shown and thus proper grouping! - NotificationHelper.showSummaryNotification( - context, - notificationManager, - account, - notificationsGroup.key, - notificationsGroup.value - ) - - notificationsGroup.value.forEach { notification -> - val androidNotification = NotificationHelper.make( + // NOTE Enqueue the summary first: Needed to avoid rate limit problems: + // ie. single notification is enqueued but that later summary one is filtered and thus no grouping + // takes place. + newNotifications.add( + NotificationHelper.makeSummaryNotification( context, notificationManager, - notification, - account + account, + notificationsGroup.key, + notificationsGroup.value ) - notificationManager.notify( - notification.id, - account.id.toInt(), - androidNotification + ) + + notificationsGroup.value.forEach { notification -> + newNotifications.add( + NotificationHelper.make( + context, + notificationManager, + notification, + account + ) ) } - - // NOTE this can schedule multiple (summary) notifications in short succession (normally 1-3). - // They can "collapse" to only one audible one if fast enough. } + // NOTE having multiple summary notifications this here should still collapse them in only one occurrence + notificationManagerCompat.notify(newNotifications) + accountManager.saveAccount(account) } catch (e: Exception) { Log.e(TAG, "Error while fetching notifications", e) @@ -153,8 +162,6 @@ class NotificationFetcher @Inject constructor( Log.d(TAG, " localMarkerId: $localMarkerId") Log.d(TAG, " readingPosition: $readingPosition") - minId = "1405569" - Log.d(TAG, "getting Notifications for ${account.fullName}, min_id: $minId") // Fetch all outstanding notifications diff --git a/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationHelper.java b/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationHelper.java index 5451a3c614..6d2cfcb29d 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationHelper.java +++ b/app/src/main/java/com/keylesspalace/tusky/components/systemnotifications/NotificationHelper.java @@ -39,6 +39,7 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; import androidx.core.app.RemoteInput; import androidx.core.app.TaskStackBuilder; import androidx.work.Constraints; @@ -147,7 +148,16 @@ public class NotificationHelper { * @return the new notification */ @NonNull - public static android.app.Notification make(final @NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Notification body, @NonNull AccountEntity account) { + public static NotificationManagerCompat.NotificationWithIdAndTag make(final @NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Notification body, @NonNull AccountEntity account) { + return new NotificationManagerCompat.NotificationWithIdAndTag( + body.getId(), + (int)account.getId(), + NotificationHelper.makeBaseNotification(context, notificationManager, body, account) + ); + } + + @NonNull + public static android.app.Notification makeBaseNotification(final @NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull Notification body, @NonNull AccountEntity account) { body = body.rewriteToStatusTypeIfNeeded(account.getAccountId()); String mastodonNotificationId = body.getId(); int accountId = (int) account.getId(); @@ -242,7 +252,7 @@ public static android.app.Notification make(final @NonNull Context context, @Non } /** - * Updates the summary notifications for each notification type. + * Creates the summary notifications for a notification type. *

* Notifications are sent to channels. Within each channel they are grouped and the group has a summary. *

@@ -255,13 +265,13 @@ public static android.app.Notification make(final @NonNull Context context, @Non * @see Create a * notification group */ - public static void showSummaryNotification(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull AccountEntity account, @NonNull Notification.Type type, @NonNull List additionalNotifications) { + public static NotificationManagerCompat.NotificationWithIdAndTag makeSummaryNotification(@NonNull Context context, @NonNull NotificationManager notificationManager, @NonNull AccountEntity account, @NonNull Notification.Type type, @NonNull List additionalNotifications) { int accountId = (int) account.getId(); String typeChannelId = getChannelId(account, type); if (typeChannelId == null) { - return; + return null; } // Create a notification that summarises the other notifications in this group @@ -304,7 +314,8 @@ public static void showSummaryNotification(@NonNull Context context, @NonNull No setSoundVibrationLight(account, summaryBuilder); String summaryTag = GROUP_SUMMARY_TAG + "." + typeChannelId; - notificationManager.notify(summaryTag, accountId, summaryBuilder.build()); + + return new NotificationManagerCompat.NotificationWithIdAndTag(summaryTag, accountId, summaryBuilder.build()); } private static List getActiveNotifications(StatusBarNotification[] allNotifications, int accountId, String typeChannelId) { diff --git a/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt b/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt index 458c261ef5..9ee3e7d245 100644 --- a/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt +++ b/app/src/test/java/com/keylesspalace/tusky/MainActivityTest.kt @@ -98,7 +98,7 @@ class MainActivityTest { NotificationHelper.createNotificationChannelsForAccount(accountEntity, context) runInBackground { - val notification = NotificationHelper.make( + val notification = NotificationHelper.makeBaseNotification( context, notificationManager, Notification(