From fcd30469936bbfb9d433d36fb9330e89549b5f33 Mon Sep 17 00:00:00 2001 From: dragonx943 Date: Sun, 16 Nov 2025 06:24:47 +0700 Subject: [PATCH 1/4] debug(feat): Check for new chapters, attempt 1 --- gradle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index d0dd90a..0304522 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,9 +18,9 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" android.enableR8.fullMode=true android.nonFinalResIds=false -org.gradle.caching=true -org.gradle.configuration-cache=true +# org.gradle.caching=true +# org.gradle.configuration-cache=true org.gradle.vfs.watch=true org.gradle.parallel=true org.gradle.workers.max=8 -org.gradle.configuration-cache.max-problems=8 +# org.gradle.configuration-cache.max-problems=8 From 9caf901d55e06ee180635969d0114f56ee43ff01 Mon Sep 17 00:00:00 2001 From: dragonx943 Date: Sun, 16 Nov 2025 06:28:15 +0700 Subject: [PATCH 2/4] debug(feat): Check for new chapters, attempt 1 --- .../main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt b/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt index f15a619..cbcfd5d 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt @@ -115,7 +115,7 @@ class TrackWorker @AssistedInject constructor( } val notifications = checkUpdatesAsync(tracks) - if (notifications.isNotEmpty() && applicationContext.checkNotificationPermission(null)) { + if (notifications.isNotEmpty() && applicationContext.checkNotificationPermission(TrackerNotificationHelper.CHANNEL_ID)) { val groupNotification = notificationHelper.createGroupNotification(notifications) notifications.forEach { notificationManager.notify(it.tag, it.id, it.notification) } if (groupNotification != null) { From ba40bada5d40a490cba5a5998c97c34a7c9b7a49 Mon Sep 17 00:00:00 2001 From: dragonx943 Date: Mon, 17 Nov 2025 01:53:19 +0700 Subject: [PATCH 3/4] chore(feat): Throw notification immediately while checking for new chapters --- app/src/main/AndroidManifest.xml | 1203 ++++++++++++++++- .../doki/core/parser/MangaLinkResolver.kt | 4 +- .../anilist/data/AniListRepository.kt | 2 +- .../scrobbling/kitsu/ui/KitsuAuthActivity.kt | 2 +- .../doki/scrobbling/mal/data/MALRepository.kt | 2 +- .../shikimori/data/ShikimoriRepository.kt | 2 +- .../dokiteam/doki/tracker/work/TrackWorker.kt | 163 ++- 7 files changed, 1277 insertions(+), 101 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d7527cf..a2d2590 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -101,6 +101,7 @@ + @@ -469,34 +470,1180 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/kotlin/org/dokiteam/doki/core/parser/MangaLinkResolver.kt b/app/src/main/kotlin/org/dokiteam/doki/core/parser/MangaLinkResolver.kt index 030d0de..8c03647 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/core/parser/MangaLinkResolver.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/core/parser/MangaLinkResolver.kt @@ -118,7 +118,9 @@ class MangaLinkResolver @Inject constructor( companion object { fun isValidLink(str: String): Boolean { - return str.isHttpUrl() || str.startsWith("doki://", ignoreCase = true) + return str.isHttpUrl() || + str.startsWith("doki://", ignoreCase = true) || + str.startsWith("kotatsu://", ignoreCase = true) } } } diff --git a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/anilist/data/AniListRepository.kt b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/anilist/data/AniListRepository.kt index cfddde7..b8c2c97 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/anilist/data/AniListRepository.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/anilist/data/AniListRepository.kt @@ -28,7 +28,7 @@ import javax.inject.Inject import javax.inject.Singleton import kotlin.math.roundToInt -private const val REDIRECT_URI = "doki://anilist-auth" +private const val REDIRECT_URI = "kotatsu://anilist-auth" private const val BASE_URL = "https://anilist.co/api/v2/" private const val ENDPOINT = "https://graphql.anilist.co" private const val MANGA_PAGE_SIZE = 10 diff --git a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/kitsu/ui/KitsuAuthActivity.kt b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/kitsu/ui/KitsuAuthActivity.kt index f9ce976..968f330 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/kitsu/ui/KitsuAuthActivity.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/kitsu/ui/KitsuAuthActivity.kt @@ -103,7 +103,7 @@ class KitsuAuthActivity : BaseActivity(), private fun continueAuth() { val email = viewBinding.editEmail.text?.toString()?.trim().orEmpty() val password = viewBinding.editPassword.text?.toString()?.trim().orEmpty() - val url = "doki://kitsu-auth?code=" + "$email;$password".urlEncoded() + val url = "kotatsu://kitsu-auth?code=" + "$email;$password".urlEncoded() val intent = Intent(Intent.ACTION_VIEW, url.toUri()) startActivity(intent) finishAfterTransition() diff --git a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/mal/data/MALRepository.kt b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/mal/data/MALRepository.kt index 0b5f467..19b64dc 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/mal/data/MALRepository.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/mal/data/MALRepository.kt @@ -26,7 +26,7 @@ import java.security.SecureRandom import javax.inject.Inject import javax.inject.Singleton -private const val REDIRECT_URI = "doki://mal-auth" +private const val REDIRECT_URI = "kotatsu://mal-auth" private const val BASE_WEB_URL = "https://myanimelist.net" private const val BASE_API_URL = "https://api.myanimelist.net/v2" diff --git a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/shikimori/data/ShikimoriRepository.kt b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/shikimori/data/ShikimoriRepository.kt index 3aae7d6..9cc527e 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/scrobbling/shikimori/data/ShikimoriRepository.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/scrobbling/shikimori/data/ShikimoriRepository.kt @@ -28,7 +28,7 @@ import javax.inject.Inject import javax.inject.Singleton private const val DOMAIN = "shikimori.one" -private const val REDIRECT_URI = "doki://shikimori-auth" +private const val REDIRECT_URI = "kotatsu://shikimori-auth" private const val BASE_URL = "https://$DOMAIN/" private const val MANGA_PAGE_SIZE = 10 diff --git a/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt b/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt index cbcfd5d..951985e 100644 --- a/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt +++ b/app/src/main/kotlin/org/dokiteam/doki/tracker/work/TrackWorker.kt @@ -33,9 +33,8 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapNotNull -import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit @@ -95,7 +94,7 @@ class TrackWorker @AssistedInject constructor( doWorkImpl(isFullRun = isForeground && TAG_ONESHOT in tags) } catch (e: CancellationException) { throw e - } catch (e: Throwable) { + } catch (e: Throwable) { e.printStackTraceDebug() Result.failure() } finally { @@ -105,74 +104,102 @@ class TrackWorker @AssistedInject constructor( } } - private suspend fun doWorkImpl(isFullRun: Boolean): Result { - if (!settings.isTrackerEnabled) { - return Result.success() - } - val tracks = getTracksUseCase(if (isFullRun) Int.MAX_VALUE else BATCH_SIZE) - if (tracks.isEmpty()) { - return Result.success() - } + private suspend fun doWorkImpl(isFullRun: Boolean): Result { + if (!settings.isTrackerEnabled) { + return Result.success() + } + val tracks = getTracksUseCase(if (isFullRun) Int.MAX_VALUE else BATCH_SIZE) + if (tracks.isEmpty()) { + return Result.success() + } - val notifications = checkUpdatesAsync(tracks) - if (notifications.isNotEmpty() && applicationContext.checkNotificationPermission(TrackerNotificationHelper.CHANNEL_ID)) { - val groupNotification = notificationHelper.createGroupNotification(notifications) - notifications.forEach { notificationManager.notify(it.tag, it.id, it.notification) } - if (groupNotification != null) { - notificationManager.notify(TAG, TrackerNotificationHelper.GROUP_NOTIFICATION_ID, groupNotification) - } - } - return Result.success() - } + checkUpdatesAsync(tracks) + return Result.success() + } - @CheckResult - private suspend fun checkUpdatesAsync(tracks: List): List { - val semaphore = Semaphore(MAX_PARALLELISM) - return channelFlow { - for (track in tracks) { - launch { - semaphore.withPermit { - send( - runCatchingCancellable { - checkNewChaptersUseCase.invoke(track) - }.getOrElse { error -> - MangaUpdates.Failure( - manga = track.manga, - error = error, - ) - }, - ) - } - } - } - }.onEachIndexed { index, it -> - if (applicationContext.checkNotificationPermission(WORKER_CHANNEL_ID)) { - notificationManager.notify(WORKER_NOTIFICATION_ID, createWorkerNotification(tracks.size, index + 1)) - } - when (it) { - is MangaUpdates.Failure -> { - val e = it.error - if (e is CloudFlareException) { - captchaHandler.handle(e) - } - } + @CheckResult + private suspend fun checkUpdatesAsync(tracks: List) { + val semaphore = Semaphore(MAX_PARALLELISM) + val groupNotifications = mutableListOf() - is MangaUpdates.Success -> processDownload(it) - } - }.mapNotNull { - when (it) { - is MangaUpdates.Failure -> null - is MangaUpdates.Success -> if (it.isValid && it.isNotEmpty()) { - notificationHelper.createNotification( - manga = it.manga, - newChapters = it.newChapters, - ) - } else { - null - } - } - }.toList() - } + try { + channelFlow { + for (track in tracks) { + launch { + semaphore.withPermit { + send( + runCatchingCancellable { + checkNewChaptersUseCase.invoke(track) + }.getOrElse { error -> + MangaUpdates.Failure( + manga = track.manga, + error = error, + ) + }, + ) + } + } + } + }.onEachIndexed { index, it -> + if (applicationContext.checkNotificationPermission(WORKER_CHANNEL_ID)) { + notificationManager.notify( + WORKER_NOTIFICATION_ID, + createWorkerNotification(tracks.size, index + 1) + ) + } + + when (it) { + is MangaUpdates.Failure -> { + val e = it.error + if (e is CloudFlareException) { + captchaHandler.handle(e) + } + } + + is MangaUpdates.Success -> { + processDownload(it) + + if (it.isValid && it.isNotEmpty()) { + val notificationInfo = notificationHelper.createNotification( + manga = it.manga, + newChapters = it.newChapters, + ) + + if (notificationInfo != null && + applicationContext.checkNotificationPermission(TrackerNotificationHelper.CHANNEL_ID)) { + notificationManager.notify( + notificationInfo.tag, + notificationInfo.id, + notificationInfo.notification + ) + + synchronized(groupNotifications) { + groupNotifications.add(notificationInfo) + } + } + } + } + } + }.collect() + + } catch (e: CancellationException) { + e.printStackTraceDebug() + } finally { + withContext(NonCancellable) { + if (groupNotifications.size > 1 && + applicationContext.checkNotificationPermission(TrackerNotificationHelper.CHANNEL_ID)) { + val groupNotification = notificationHelper.createGroupNotification(groupNotifications) + if (groupNotification != null) { + notificationManager.notify( + TAG, + TrackerNotificationHelper.GROUP_NOTIFICATION_ID, + groupNotification + ) + } + } + } + } + } override suspend fun getForegroundInfo(): ForegroundInfo { val channel = NotificationChannelCompat.Builder( From 732d0bff9f58ddbe74dc2e4970a5ec534ea262bf Mon Sep 17 00:00:00 2001 From: Jerry <149695375+jeremychoco@users.noreply.github.com> Date: Mon, 17 Nov 2025 01:57:29 +0700 Subject: [PATCH 4/4] Enable gradle caches for building --- gradle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 0304522..d0dd90a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,9 +18,9 @@ kotlin.code.style=official org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" android.enableR8.fullMode=true android.nonFinalResIds=false -# org.gradle.caching=true -# org.gradle.configuration-cache=true +org.gradle.caching=true +org.gradle.configuration-cache=true org.gradle.vfs.watch=true org.gradle.parallel=true org.gradle.workers.max=8 -# org.gradle.configuration-cache.max-problems=8 +org.gradle.configuration-cache.max-problems=8