@@ -33,9 +33,8 @@ import kotlinx.coroutines.CancellationException
3333import kotlinx.coroutines.NonCancellable
3434import kotlinx.coroutines.flow.Flow
3535import kotlinx.coroutines.flow.channelFlow
36+ import kotlinx.coroutines.flow.collect
3637import kotlinx.coroutines.flow.map
37- import kotlinx.coroutines.flow.mapNotNull
38- import kotlinx.coroutines.flow.toList
3938import kotlinx.coroutines.launch
4039import kotlinx.coroutines.sync.Semaphore
4140import kotlinx.coroutines.sync.withPermit
@@ -95,7 +94,7 @@ class TrackWorker @AssistedInject constructor(
9594 doWorkImpl(isFullRun = isForeground && TAG_ONESHOT in tags)
9695 } catch (e: CancellationException ) {
9796 throw e
98- } catch (e: Throwable ) {
97+ } catch (e: Throwable ) {
9998 e.printStackTraceDebug()
10099 Result .failure()
101100 } finally {
@@ -105,74 +104,102 @@ class TrackWorker @AssistedInject constructor(
105104 }
106105 }
107106
108- private suspend fun doWorkImpl (isFullRun : Boolean ): Result {
109- if (! settings.isTrackerEnabled) {
110- return Result .success()
111- }
112- val tracks = getTracksUseCase(if (isFullRun) Int .MAX_VALUE else BATCH_SIZE )
113- if (tracks.isEmpty()) {
114- return Result .success()
115- }
107+ private suspend fun doWorkImpl (isFullRun : Boolean ): Result {
108+ if (! settings.isTrackerEnabled) {
109+ return Result .success()
110+ }
111+ val tracks = getTracksUseCase(if (isFullRun) Int .MAX_VALUE else BATCH_SIZE )
112+ if (tracks.isEmpty()) {
113+ return Result .success()
114+ }
116115
117- val notifications = checkUpdatesAsync(tracks)
118- if (notifications.isNotEmpty() && applicationContext.checkNotificationPermission(TrackerNotificationHelper .CHANNEL_ID )) {
119- val groupNotification = notificationHelper.createGroupNotification(notifications)
120- notifications.forEach { notificationManager.notify(it.tag, it.id, it.notification) }
121- if (groupNotification != null ) {
122- notificationManager.notify(TAG , TrackerNotificationHelper .GROUP_NOTIFICATION_ID , groupNotification)
123- }
124- }
125- return Result .success()
126- }
116+ checkUpdatesAsync(tracks)
117+ return Result .success()
118+ }
127119
128- @CheckResult
129- private suspend fun checkUpdatesAsync (tracks : List <MangaTracking >): List <NotificationInfo > {
130- val semaphore = Semaphore (MAX_PARALLELISM )
131- return channelFlow {
132- for (track in tracks) {
133- launch {
134- semaphore.withPermit {
135- send(
136- runCatchingCancellable {
137- checkNewChaptersUseCase.invoke(track)
138- }.getOrElse { error ->
139- MangaUpdates .Failure (
140- manga = track.manga,
141- error = error,
142- )
143- },
144- )
145- }
146- }
147- }
148- }.onEachIndexed { index, it ->
149- if (applicationContext.checkNotificationPermission(WORKER_CHANNEL_ID )) {
150- notificationManager.notify(WORKER_NOTIFICATION_ID , createWorkerNotification(tracks.size, index + 1 ))
151- }
152- when (it) {
153- is MangaUpdates .Failure -> {
154- val e = it.error
155- if (e is CloudFlareException ) {
156- captchaHandler.handle(e)
157- }
158- }
120+ @CheckResult
121+ private suspend fun checkUpdatesAsync (tracks : List <MangaTracking >) {
122+ val semaphore = Semaphore (MAX_PARALLELISM )
123+ val groupNotifications = mutableListOf<NotificationInfo >()
159124
160- is MangaUpdates .Success -> processDownload(it)
161- }
162- }.mapNotNull {
163- when (it) {
164- is MangaUpdates .Failure -> null
165- is MangaUpdates .Success -> if (it.isValid && it.isNotEmpty()) {
166- notificationHelper.createNotification(
167- manga = it.manga,
168- newChapters = it.newChapters,
169- )
170- } else {
171- null
172- }
173- }
174- }.toList()
175- }
125+ try {
126+ channelFlow {
127+ for (track in tracks) {
128+ launch {
129+ semaphore.withPermit {
130+ send(
131+ runCatchingCancellable {
132+ checkNewChaptersUseCase.invoke(track)
133+ }.getOrElse { error ->
134+ MangaUpdates .Failure (
135+ manga = track.manga,
136+ error = error,
137+ )
138+ },
139+ )
140+ }
141+ }
142+ }
143+ }.onEachIndexed { index, it ->
144+ if (applicationContext.checkNotificationPermission(WORKER_CHANNEL_ID )) {
145+ notificationManager.notify(
146+ WORKER_NOTIFICATION_ID ,
147+ createWorkerNotification(tracks.size, index + 1 )
148+ )
149+ }
150+
151+ when (it) {
152+ is MangaUpdates .Failure -> {
153+ val e = it.error
154+ if (e is CloudFlareException ) {
155+ captchaHandler.handle(e)
156+ }
157+ }
158+
159+ is MangaUpdates .Success -> {
160+ processDownload(it)
161+
162+ if (it.isValid && it.isNotEmpty()) {
163+ val notificationInfo = notificationHelper.createNotification(
164+ manga = it.manga,
165+ newChapters = it.newChapters,
166+ )
167+
168+ if (notificationInfo != null &&
169+ applicationContext.checkNotificationPermission(TrackerNotificationHelper .CHANNEL_ID )) {
170+ notificationManager.notify(
171+ notificationInfo.tag,
172+ notificationInfo.id,
173+ notificationInfo.notification
174+ )
175+
176+ synchronized(groupNotifications) {
177+ groupNotifications.add(notificationInfo)
178+ }
179+ }
180+ }
181+ }
182+ }
183+ }.collect()
184+
185+ } catch (e: CancellationException ) {
186+ e.printStackTraceDebug()
187+ } finally {
188+ withContext(NonCancellable ) {
189+ if (groupNotifications.size > 1 &&
190+ applicationContext.checkNotificationPermission(TrackerNotificationHelper .CHANNEL_ID )) {
191+ val groupNotification = notificationHelper.createGroupNotification(groupNotifications)
192+ if (groupNotification != null ) {
193+ notificationManager.notify(
194+ TAG ,
195+ TrackerNotificationHelper .GROUP_NOTIFICATION_ID ,
196+ groupNotification
197+ )
198+ }
199+ }
200+ }
201+ }
202+ }
176203
177204 override suspend fun getForegroundInfo (): ForegroundInfo {
178205 val channel = NotificationChannelCompat .Builder (
0 commit comments