From 05bee093bbf1b0e3d4ece0c8430b374ef5a1ef93 Mon Sep 17 00:00:00 2001 From: akesi seli Date: Thu, 24 Oct 2024 14:29:52 +0200 Subject: [PATCH] refactor: background notification checks (#43) * migrate worker to coroutines and remove dispatcher constructor params which prevent proper instantiation * refactor DefaultInboxNotificationChecker --- .../notification/CheckNotificationWorker.kt | 56 ++++++++++--------- ....kt => DefaultInboxNotificationChecker.kt} | 39 +++++++++++-- 2 files changed, 62 insertions(+), 33 deletions(-) rename domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/{DefaultNotificationChecker.kt => DefaultInboxNotificationChecker.kt} (61%) diff --git a/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/CheckNotificationWorker.kt b/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/CheckNotificationWorker.kt index 69a6a77bc..3e062166a 100644 --- a/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/CheckNotificationWorker.kt +++ b/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/CheckNotificationWorker.kt @@ -6,59 +6,43 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent -import androidx.work.Worker +import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.livefast.eattrash.raccoonforlemmy.domain.inbox.InboxCoordinator import com.livefast.eattrash.raccoonforlemmy.domain.inbox.R -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.launch -import org.koin.java.KoinJavaComponent +import kotlinx.coroutines.withContext +import org.koin.java.KoinJavaComponent.inject import java.util.Collections.max import com.livefast.eattrash.raccoonforlemmy.core.resources.R as resourcesR internal class CheckNotificationWorker( private val context: Context, parameters: WorkerParameters, - dispatcher: CoroutineDispatcher = Dispatchers.IO, -) : Worker(context, parameters) { - private val inboxCoordinator by KoinJavaComponent.inject(InboxCoordinator::class.java) - private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher) +) : CoroutineWorker(context, parameters) { + private val inboxCoordinator by inject(InboxCoordinator::class.java) - override fun doWork(): Result { - scope.launch { + override suspend fun doWork() = + withContext(Dispatchers.IO) { inboxCoordinator.updateUnreadCount() val unread = inboxCoordinator.totalUnread.value if (unread > 0) { sendNotification(unread) } + Result.success() } - return Result.success() - } - @SuppressLint("StringFormatInvalid") private fun sendNotification(count: Int) { - val intent = - Intent( - context, - Class.forName("com.livefast.eattrash.raccoonforlemmy.android.MainActivity"), - ).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } - val pendingIntent: PendingIntent = - PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) - val title = context.getString(R.string.inbox_notification_title) val content = context.getString(R.string.inbox_notification_content, count) val notification = - Notification.Builder(context, NotificationConstants.CHANNEL_ID) + Notification + .Builder(context, NotificationConstants.CHANNEL_ID) .setContentTitle(title) .setContentText(content) .setSmallIcon(resourcesR.drawable.ic_monochrome) - .setContentIntent(pendingIntent) + .setContentIntent(getPendingIntent()) .setNumber(count) .build() val notificationManager: NotificationManager = @@ -71,6 +55,19 @@ internal class CheckNotificationWorker( ) } + private fun getPendingIntent(): PendingIntent { + val intent = + Intent( + context, + Class.forName(MAIN_ACTIVITY_NAME), + ).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + val pendingIntent: PendingIntent = + PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) + return pendingIntent + } + private fun getNextNotificationId(): Int { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -82,4 +79,9 @@ internal class CheckNotificationWorker( max(activeNotifications.map { it.id }) + 1 } } + + companion object { + private const val MAIN_ACTIVITY_NAME = + "com.livefast.eattrash.raccoonforlemmy.android.MainActivity" + } } diff --git a/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/DefaultNotificationChecker.kt b/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/DefaultInboxNotificationChecker.kt similarity index 61% rename from domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/DefaultNotificationChecker.kt rename to domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/DefaultInboxNotificationChecker.kt index 16efb1cd3..f31e14aaa 100644 --- a/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/DefaultNotificationChecker.kt +++ b/domain/inbox/src/androidMain/kotlin/com/livefast/eattrash/raccoonforlemmy/domain/inbox/notification/DefaultInboxNotificationChecker.kt @@ -3,12 +3,14 @@ package com.livefast.eattrash.raccoonforlemmy.domain.inbox.notification import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context +import androidx.work.Constraints +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.OutOfQuotaPolicy import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import java.util.concurrent.TimeUnit -private const val TAG = "InboxNotificationCheck" - class DefaultInboxNotificationChecker( private val context: Context, ) : InboxNotificationChecker { @@ -23,13 +25,34 @@ class DefaultInboxNotificationChecker( WorkManager.getInstance(context).cancelAllWorkByTag(TAG) createNotificationChannel() + + val constraints = + Constraints + .Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + // check immediately with an expedited one-time request + OneTimeWorkRequestBuilder() + .addTag(TAG) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints(constraints) + .build() + .also { req -> + WorkManager.getInstance(context).enqueue(req) + } + PeriodicWorkRequestBuilder( repeatInterval = intervalMinutes, repeatIntervalTimeUnit = TimeUnit.MINUTES, - ) - .addTag(TAG) - .build().apply { - WorkManager.getInstance(context).enqueue(this) + ).addTag(TAG) + .setConstraints(constraints) + .setInitialDelay( + 5, + TimeUnit.MINUTES, + ).build() + .also { req -> + WorkManager.getInstance(context).enqueue(req) } } @@ -52,4 +75,8 @@ class DefaultInboxNotificationChecker( context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } + + companion object { + private const val TAG = "InboxNotificationChecker" + } }