From ec478cbb1bc0a80c18957169cbcc13505d46f37d Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 1 Jan 2024 09:53:21 -0500 Subject: [PATCH 1/2] Defer ACRA reporting until device is idle/not low battery/on unmetered network --- app/build.gradle.kts | 2 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 13 ++++++++++++- gradle/libs.versions.toml | 5 ++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b3b735dd1..96c7ac385 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -246,7 +246,7 @@ dependencies { implementation(libs.logcat) // Crash reports/analytics - implementation(libs.acra.http) + implementation(libs.bundles.acra) "standardImplementation"(libs.firebase.analytics) // Shizuku diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 27235ba64..f155c5bb9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi import android.annotation.SuppressLint import android.app.Application import android.app.PendingIntent +import android.app.job.JobInfo import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -51,10 +52,12 @@ import logcat.AndroidLogcatLogger import logcat.LogPriority import logcat.LogcatLogger import org.acra.config.httpSender +import org.acra.config.scheduler import org.acra.ktx.initAcra import org.acra.sender.HttpSender import org.conscrypt.Conscrypt import tachiyomi.core.i18n.stringResource +import tachiyomi.core.preference.Preference import tachiyomi.core.util.system.logcat import tachiyomi.i18n.MR import tachiyomi.presentation.widget.WidgetManager @@ -199,12 +202,20 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { if (isPreviewBuildType || isReleaseBuildType) { initAcra { buildConfigClass = BuildConfig::class.java - excludeMatchingSharedPreferencesKeys = listOf(".*username.*", ".*password.*", ".*token.*") + excludeMatchingSharedPreferencesKeys = listOf( + Preference.privateKey(".*"), ".*username.*", ".*password.*", ".*token.*", + ) httpSender { uri = BuildConfig.ACRA_URI httpMethod = HttpSender.Method.PUT } + + scheduler { + requiresBatteryNotLow = true + requiresDeviceIdle = true + requiresNetworkType = JobInfo.NETWORK_TYPE_UNMETERED + } } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d95133a4d..b04b8cd4b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] aboutlib_version = "10.10.0" +acra = "5.11.3" leakcanary = "2.12" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" @@ -67,7 +68,8 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -acra-http = "ch.acra:acra-http:5.11.3" +acra-http = { module = "ch.acra:acra-http", version.ref = "acra" } +acra-scheduler = { module = "ch.acra:acra-advanced-scheduler", version.ref = "acra" } firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.0" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } @@ -97,6 +99,7 @@ voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", vers ktlint = "org.jlleitschuh.gradle:ktlint-gradle:12.0.3" [bundles] +acra = ["acra-http", "acra-scheduler"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] js-engine = ["quickjs-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] From 22589a9c3056312dcbd0dfca08b53987cbc4a73d Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 1 Jan 2024 18:32:21 -0500 Subject: [PATCH 2/2] Fix next expected update being weird number sometimes Occurs if manga.lastUpdate has never been set yet. --- .../interactor/SyncChaptersWithSource.kt | 47 ++++++++----------- .../manga/components/MangaDialogs.kt | 1 - .../domain/manga/interactor/FetchInterval.kt | 11 ++--- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt index e58f40a71..abd13a849 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt @@ -22,7 +22,6 @@ import tachiyomi.domain.chapter.service.ChapterRecognition import tachiyomi.domain.manga.model.Manga import tachiyomi.source.local.isLocal import java.lang.Long.max -import java.time.Instant import java.time.ZonedDateTime import java.util.TreeSet @@ -57,6 +56,7 @@ class SyncChaptersWithSource( } val now = ZonedDateTime.now() + val nowMillis = now.toInstant().toEpochMilli() val sourceChapters = rawSourceChapters .distinctBy { it.url } @@ -67,36 +67,27 @@ class SyncChaptersWithSource( .copy(mangaId = manga.id, sourceOrder = i.toLong()) } - // Chapters from db. val dbChapters = getChaptersByMangaId.await(manga.id) - // Chapters from the source not in db. - val toAdd = mutableListOf() - - // Chapters whose metadata have changed. - val toChange = mutableListOf() - - // Chapters from the db not in source. - val toDelete = dbChapters.filterNot { dbChapter -> + val newChapters = mutableListOf() + val updatedChapters = mutableListOf() + val removedChapters = dbChapters.filterNot { dbChapter -> sourceChapters.any { sourceChapter -> dbChapter.url == sourceChapter.url } } - val rightNow = Instant.now().toEpochMilli() - // Used to not set upload date of older chapters // to a higher value than newer chapters var maxSeenUploadDate = 0L - val sManga = manga.toSManga() for (sourceChapter in sourceChapters) { var chapter = sourceChapter // Update metadata from source if necessary. if (source is HttpSource) { val sChapter = chapter.toSChapter() - source.prepareNewChapter(sChapter, sManga) + source.prepareNewChapter(sChapter, manga.toSManga()) chapter = chapter.copyFromSChapter(sChapter) } @@ -108,13 +99,13 @@ class SyncChaptersWithSource( if (dbChapter == null) { val toAddChapter = if (chapter.dateUpload == 0L) { - val altDateUpload = if (maxSeenUploadDate == 0L) rightNow else maxSeenUploadDate + val altDateUpload = if (maxSeenUploadDate == 0L) nowMillis else maxSeenUploadDate chapter.copy(dateUpload = altDateUpload) } else { maxSeenUploadDate = max(maxSeenUploadDate, sourceChapter.dateUpload) chapter } - toAdd.add(toAddChapter) + newChapters.add(toAddChapter) } else { if (shouldUpdateDbChapter.await(dbChapter, chapter)) { val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) && @@ -134,13 +125,13 @@ class SyncChaptersWithSource( if (chapter.dateUpload != 0L) { toChangeChapter = toChangeChapter.copy(dateUpload = chapter.dateUpload) } - toChange.add(toChangeChapter) + updatedChapters.add(toChangeChapter) } } } - // Return if there's nothing to add, delete or change, avoiding unnecessary db transactions. - if (toAdd.isEmpty() && toDelete.isEmpty() && toChange.isEmpty()) { + // Return if there's nothing to add, delete, or update to avoid unnecessary db transactions. + if (newChapters.isEmpty() && removedChapters.isEmpty() && updatedChapters.isEmpty()) { if (manualFetch || manga.fetchInterval == 0 || manga.nextUpdate < fetchWindow.first) { updateManga.awaitUpdateFetchInterval( manga, @@ -157,20 +148,20 @@ class SyncChaptersWithSource( val deletedReadChapterNumbers = TreeSet() val deletedBookmarkedChapterNumbers = TreeSet() - toDelete.forEach { chapter -> + removedChapters.forEach { chapter -> if (chapter.read) deletedReadChapterNumbers.add(chapter.chapterNumber) if (chapter.bookmark) deletedBookmarkedChapterNumbers.add(chapter.chapterNumber) deletedChapterNumbers.add(chapter.chapterNumber) } - val deletedChapterNumberDateFetchMap = toDelete.sortedByDescending { it.dateFetch } + val deletedChapterNumberDateFetchMap = removedChapters.sortedByDescending { it.dateFetch } .associate { it.chapterNumber to it.dateFetch } // Date fetch is set in such a way that the upper ones will have bigger value than the lower ones // Sources MUST return the chapters from most to less recent, which is common. - var itemCount = toAdd.size - var updatedToAdd = toAdd.map { toAddItem -> - var chapter = toAddItem.copy(dateFetch = rightNow + itemCount--) + var itemCount = newChapters.size + var updatedToAdd = newChapters.map { toAddItem -> + var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--) if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter @@ -189,8 +180,8 @@ class SyncChaptersWithSource( chapter } - if (toDelete.isNotEmpty()) { - val toDeleteIds = toDelete.map { it.id } + if (removedChapters.isNotEmpty()) { + val toDeleteIds = removedChapters.map { it.id } chapterRepository.removeChaptersWithIds(toDeleteIds) } @@ -198,8 +189,8 @@ class SyncChaptersWithSource( updatedToAdd = chapterRepository.addAll(updatedToAdd) } - if (toChange.isNotEmpty()) { - val chapterUpdates = toChange.map { it.toChapterUpdate() } + if (updatedChapters.isNotEmpty()) { + val chapterUpdates = updatedChapters.map { it.toChapterUpdate() } updateChapter.awaitAll(chapterUpdates) } updateManga.awaitUpdateFetchInterval(manga, now, fetchWindow) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt index 5d899c046..f59b4574a 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaDialogs.kt @@ -77,7 +77,6 @@ fun SetIntervalDialog( title = { Text(stringResource(MR.strings.manga_modify_calculated_interval_title)) }, text = { Column { - // TODO: figure out why nextUpdate is a weird number sometimes if (nextUpdateDays >= 0) { Text( stringResource( diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt index 7570b2a11..6c8413a75 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt @@ -19,16 +19,15 @@ class FetchInterval( dateTime: ZonedDateTime, window: Pair, ): MangaUpdate? { + val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval( + chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true), + zone = dateTime.zone, + ) val currentWindow = if (window.first == 0L && window.second == 0L) { getWindow(ZonedDateTime.now()) } else { window } - val chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) - val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval( - chapters, - dateTime.zone, - ) val nextUpdate = calculateNextUpdate(manga, interval, dateTime, currentWindow) return if (manga.nextUpdate == nextUpdate && manga.fetchInterval == interval) { @@ -102,7 +101,7 @@ class FetchInterval( manga.fetchInterval == 0 ) { val latestDate = ZonedDateTime.ofInstant( - Instant.ofEpochMilli(manga.lastUpdate), + if (manga.lastUpdate > 0) Instant.ofEpochMilli(manga.lastUpdate) else Instant.now(), dateTime.zone, ) .toLocalDate()