diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 80d4ddd16..aa702dae2 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -303,10 +303,7 @@ object SettingsDataScreen : SearchableSettings { val chapterCache = remember { Injekt.get() } var cacheReadableSizeSema by remember { mutableIntStateOf(0) } - var cacheReadableSize by remember { mutableStateOf(context.stringResource(MR.strings.calculating)) } - LaunchedEffect(cacheReadableSizeSema) { - cacheReadableSize = chapterCache.getReadableSize() - } + val cacheReadableSize = remember(cacheReadableSizeSema) { chapterCache.readableSize } // SY --> val pagePreviewCache = remember { Injekt.get() } diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/StorageInfo.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/StorageInfo.kt index a8578d97d..5fed6c6ef 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/StorageInfo.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/data/StorageInfo.kt @@ -9,18 +9,12 @@ import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import eu.kanade.tachiyomi.util.storage.DiskUtil -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource @@ -51,24 +45,10 @@ private fun StorageInfo( ) { val context = LocalContext.current - var available by remember(file) { mutableStateOf(-1L) } - var total by remember(file) { mutableStateOf(-1L) } - - LaunchedEffect(file) { - available = withContext(Dispatchers.IO) { DiskUtil.getAvailableStorageSpace(file) } - total = withContext(Dispatchers.IO) { DiskUtil.getTotalStorageSpace(file) } - } - - val availableText = if (available == -1L) { - stringResource(MR.strings.calculating) - } else { - Formatter.formatFileSize(context, available) - } - val totalText = if (total == -1L) { - stringResource(MR.strings.calculating) - } else { - Formatter.formatFileSize(context, total) - } + val available = remember(file) { DiskUtil.getAvailableStorageSpace(file) } + val availableText = remember(available) { Formatter.formatFileSize(context, available) } + val total = remember(file) { DiskUtil.getTotalStorageSpace(file) } + val totalText = remember(total) { Formatter.formatFileSize(context, total) } Column( verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), @@ -78,15 +58,13 @@ private fun StorageInfo( style = MaterialTheme.typography.header, ) - if (total > 0) { - LinearProgressIndicator( - modifier = Modifier - .clip(MaterialTheme.shapes.small) - .fillMaxWidth() - .height(12.dp), - progress = { (1 - (available / total.toFloat())) }, - ) - } + LinearProgressIndicator( + modifier = Modifier + .clip(MaterialTheme.shapes.small) + .fillMaxWidth() + .height(12.dp), + progress = { (1 - (available / total.toFloat())) }, + ) Text( text = stringResource(MR.strings.available_disk_space_info, availableText, totalText), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt index 1bfe291af..5de90cc2f 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json import logcat.LogPriority import okhttp3.Response @@ -64,13 +63,17 @@ class ChapterCache( */ private val cacheDir: File = diskCache.directory + /** + * Returns real size of directory. + */ + private val realSize: Long + get() = DiskUtil.getDirectorySize(cacheDir) + /** * Returns real size of directory in human readable format. */ - suspend fun getReadableSize(): String = withContext(Dispatchers.IO) { - val size = DiskUtil.getDirectorySize(cacheDir) - Formatter.formatFileSize(context, size) - } + val readableSize: String + get() = Formatter.formatFileSize(context, realSize) // --> EH // Cache size is in MB diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index d555ef127..bbcd845b5 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -109,10 +109,10 @@ class DownloadManager( return queueState.value.find { it.chapter.id == chapterId } } - suspend fun startDownloadNow(chapterId: Long) { + fun startDownloadNow(chapterId: Long) { val existingDownload = getQueuedDownloadOrNull(chapterId) // If not in queue try to start a new download - val toAdd = existingDownload ?: Download.fromChapterId(chapterId) ?: return + val toAdd = existingDownload ?: runBlocking { Download.fromChapterId(chapterId) } ?: return queueState.value.toMutableList().apply { existingDownload?.let { remove(it) } add(0, toAdd) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt index 56c2bd075..cd4d81a88 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt @@ -89,7 +89,7 @@ class DownloadStore( /** * Returns the list of downloads to restore. It should be called in a background thread. */ - suspend fun restore(): List { + fun restore(): List { val objs = preferences.all .mapNotNull { it.value as? String } .mapNotNull { deserialize(it) } @@ -100,10 +100,10 @@ class DownloadStore( val cachedManga = mutableMapOf() for ((mangaId, chapterId) in objs) { val manga = cachedManga.getOrPut(mangaId) { - getManga.await(mangaId) + runBlocking { getManga.await(mangaId) } } ?: continue val source = sourceManager.get(manga.source) as? HttpSource ?: continue - val chapter = getChapter.await(chapterId) ?: continue + val chapter = runBlocking { getChapter.await(chapterId) } ?: continue downloads.add(Download(source, manga, chapter)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 1161e83c5..c56245c42 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -25,6 +25,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asFlow @@ -49,6 +50,7 @@ import okhttp3.Response import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.storage.extension import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNow import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat @@ -119,9 +121,9 @@ class Downloader( var isPaused: Boolean = false init { - scope.launch { - val chapters = store.restore() - addAllToQueue(chapters) + launchNow { + val chapters = async { store.restore() } + addAllToQueue(chapters.await()) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index aba30d754..cef05ec81 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -23,7 +23,6 @@ import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.runBlocking import tachiyomi.core.common.Constants import tachiyomi.core.common.util.lang.launchIO -import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.interactor.UpdateChapter import tachiyomi.domain.chapter.model.Chapter @@ -85,18 +84,11 @@ class NotificationReceiver : BroadcastReceiver() { ACTION_CANCEL_APP_UPDATE_DOWNLOAD -> cancelDownloadAppUpdate(context) // Open reader activity ACTION_OPEN_CHAPTER -> { - val pendingResult = goAsync() - launchIO { - try { - openChapter( - context, - intent.getLongExtra(EXTRA_MANGA_ID, -1), - intent.getLongExtra(EXTRA_CHAPTER_ID, -1), - ) - } finally { - pendingResult.finish() - } - } + openChapter( + context, + intent.getLongExtra(EXTRA_MANGA_ID, -1), + intent.getLongExtra(EXTRA_CHAPTER_ID, -1), + ) } // Mark updated manga chapters as read ACTION_MARK_AS_READ -> { @@ -161,18 +153,16 @@ class NotificationReceiver : BroadcastReceiver() { * @param mangaId id of manga * @param chapterId id of chapter */ - private suspend fun openChapter(context: Context, mangaId: Long, chapterId: Long) { - val manga = getManga.await(mangaId) - val chapter = getChapter.await(chapterId) - withUIContext { - if (manga != null && chapter != null) { - val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP - } - context.startActivity(intent) - } else { - context.toast(MR.strings.chapter_error) + private fun openChapter(context: Context, mangaId: Long, chapterId: Long) { + val manga = runBlocking { getManga.await(mangaId) } + val chapter = runBlocking { getChapter.await(chapterId) } + if (manga != null && chapter != null) { + val intent = ReaderActivity.newIntent(context, manga.id, chapter.id).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP } + context.startActivity(intent) + } else { + context.toast(MR.strings.chapter_error) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 5fc15316f..bb20afe28 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch import logcat.LogPriority import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat @@ -141,22 +140,20 @@ class ExtensionManager( * Loads and registers the installed extensions. */ private fun initExtensions() { - scope.launch { - val extensions = ExtensionLoader.loadExtensions(context) + val extensions = ExtensionLoader.loadExtensions(context) - installedExtensionMapFlow.value = extensions - .filterIsInstance() - .associate { it.extension.pkgName to it.extension } + installedExtensionMapFlow.value = extensions + .filterIsInstance() + .associate { it.extension.pkgName to it.extension } - untrustedExtensionMapFlow.value = extensions - .filterIsInstance() - .associate { it.extension.pkgName to it.extension } - // SY --> - .filterNotBlacklisted() - // SY <-- + untrustedExtensionMapFlow.value = extensions + .filterIsInstance() + .associate { it.extension.pkgName to it.extension } + // SY --> + .filterNotBlacklisted() + // SY <-- - _isInitialized.value = true - } + _isInitialized.value = true } // EXH --> diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 02f150d8b..db6b0aa40 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -18,7 +18,6 @@ import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo import eu.kanade.tachiyomi.util.system.ChildFirstPathClassLoader import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.runBlocking import logcat.LogPriority import tachiyomi.core.common.util.system.logcat @@ -115,7 +114,7 @@ internal object ExtensionLoader { * * @param context The application context. */ - suspend fun loadExtensions(context: Context): List { + fun loadExtensions(context: Context): List { val pkgManager = context.packageManager val installedPkgs = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { @@ -161,10 +160,11 @@ internal object ExtensionLoader { if (extPkgs.isEmpty()) return emptyList() // Load each extension concurrently and wait for completion - return coroutineScope { - extPkgs.map { + return runBlocking { + val deferred = extPkgs.map { async { loadExtension(context, it) } - }.awaitAll() + } + deferred.awaitAll() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 584e81691..a19938b07 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -163,6 +163,13 @@ class MainActivity : BaseActivity() { super.onCreate(savedInstanceState) + val didMigration = if (isLaunch) { + addAnalytics() + Migrator.awaitAndRelease() + } else { + false + } + // Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079 if (!isTaskRoot) { finish() @@ -175,12 +182,6 @@ class MainActivity : BaseActivity() { // SY <-- setComposeContent { - var didMigration by remember { mutableStateOf(null) } - LaunchedEffect(Unit) { - addAnalytics() - didMigration = Migrator.awaitAndRelease() - } - val context = LocalContext.current var incognito by remember { mutableStateOf(getIncognitoState.await(null)) } @@ -308,7 +309,7 @@ class MainActivity : BaseActivity() { } // SY <-- - var showChangelog by remember { mutableStateOf(didMigration == true && !BuildConfig.DEBUG) } + var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) } if (showChangelog) { // SY --> WhatsNewDialog(onDismissRequest = { showChangelog = false }) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index 13e99e8b3..2ff6c7efa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -32,7 +32,6 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -79,7 +78,6 @@ import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.AddToLibraryFirst import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Error import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Success -import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterItem import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage @@ -103,8 +101,6 @@ import exh.source.isEhBasedSource import exh.ui.ifSourcesLoaded import exh.util.defaultReaderType import exh.util.mangaType -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableSet @@ -125,7 +121,6 @@ import tachiyomi.core.common.i18n.pluralStringResource import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchNonCancellable -import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.service.SourceManager @@ -399,36 +394,28 @@ class ReaderActivity : BaseActivity() { is ReaderViewModel.Dialog.ChapterList -> { var chapters by remember { - mutableStateOf?>(null) - } - LaunchedEffect(state.dialog) { - withIOContext { - chapters = viewModel.getChapters().toImmutableList() - } - } - - if (chapters != null) { - ChapterListDialog( - onDismissRequest = onDismissRequest, - screenModel = settingsScreenModel, - chapters = chapters ?: persistentListOf(), - onClickChapter = { - viewModel.loadNewChapterFromDialog(it) - onDismissRequest() - }, - onBookmark = { chapter -> - viewModel.toggleBookmark(chapter.id, !chapter.bookmark) - chapters = chapters?.map { - if (it.chapter.id == chapter.id) { - it.copy(chapter = chapter.copy(bookmark = !chapter.bookmark)) - } else { - it - } - }?.toImmutableList() - }, - state.dateRelativeTime, - ) + mutableStateOf(viewModel.getChapters().toImmutableList()) } + ChapterListDialog( + onDismissRequest = onDismissRequest, + screenModel = settingsScreenModel, + chapters = chapters, + onClickChapter = { + viewModel.loadNewChapterFromDialog(it) + onDismissRequest() + }, + onBookmark = { chapter -> + viewModel.toggleBookmark(chapter.id, !chapter.bookmark) + chapters = chapters.map { + if (it.chapter.id == chapter.id) { + it.copy(chapter = chapter.copy(bookmark = !chapter.bookmark)) + } else { + it + } + }.toImmutableList() + }, + state.dateRelativeTime, + ) } // SY --> ReaderViewModel.Dialog.AutoScrollHelp -> AlertDialog( @@ -604,9 +591,8 @@ class ReaderActivity : BaseActivity() { } else { cropBorderContinuousVertical } - val readerBottomButtons by remember { - readerPreferences.readerBottomButtons.changes().map { it.toImmutableSet() } - }.collectAsState(persistentSetOf()) + val readerBottomButtons by readerPreferences.readerBottomButtons.changes().map { it.toImmutableSet() } + .collectAsState(persistentSetOf()) val dualPageSplitPaged by readerPreferences.dualPageSplitPaged.collectAsState() val forceHorizontalSeekbar by readerPreferences.forceHorizontalSeekbar.collectAsState() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 3f8838c87..4a13ed643 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -59,6 +59,7 @@ import exh.source.isEhBasedManga import exh.util.defaultReaderType import exh.util.mangaType import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -182,26 +183,19 @@ class ReaderViewModel @JvmOverloads constructor( private var chapterToDownload: Download? = null - private var unfilteredChapterListCache: List? = null - private suspend fun getUnfilteredChapterList(): List { - if (unfilteredChapterListCache == null) { - val manga = manga!! - unfilteredChapterListCache = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = false) - } - return unfilteredChapterListCache!! + private val unfilteredChapterList by lazy { + val manga = manga!! + runBlocking { getChaptersByMangaId.await(manga.id, applyScanlatorFilter = false) } } /** * Chapter list for the active manga. It's retrieved lazily and should be accessed for the first * time in a background thread to avoid blocking the UI. */ - private var chapterListCache: List? = null - private suspend fun getChapterList(): List { - chapterListCache?.let { return it } - + private val chapterList by lazy { val manga = manga!! // SY --> - val (chapters, mangaMap) = + val (chapters, mangaMap) = runBlocking { if (manga.source == MERGED_SOURCE_ID) { getMergedChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to getMergedMangaById.await(manga.id) @@ -209,7 +203,7 @@ class ReaderViewModel @JvmOverloads constructor( } else { getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to null } - + } fun isChapterDownloaded(chapter: Chapter): Boolean { val chapterManga = mangaMap?.get(chapter.mangaId) ?: manga return downloadManager.isChapterDownloaded( @@ -259,7 +253,7 @@ class ReaderViewModel @JvmOverloads constructor( else -> chapters } - val result = chaptersForReader + chaptersForReader .sortedWith(getChapterSort(manga, sortDescending = false)) .run { if (readerPreferences.skipDupe.get()) { @@ -277,8 +271,6 @@ class ReaderViewModel @JvmOverloads constructor( } .map { it.toDbChapter() } .map(::ReaderChapter) - chapterListCache = result - return result } private val incognitoMode: Boolean by lazy { getIncognitoState.await(manga?.source) } @@ -417,7 +409,7 @@ class ReaderViewModel @JvmOverloads constructor( loadChapter( loader!!, - getChapterList().first { chapterId == it.chapter.id }, + chapterList.first { chapterId == it.chapter.id }, /* SY --> */page, /* SY <-- */ ) Result.success(true) @@ -435,10 +427,10 @@ class ReaderViewModel @JvmOverloads constructor( } // SY --> - suspend fun getChapters(): List { + fun getChapters(): List { val currentChapter = getCurrentChapter() - return getChapterList().map { + return chapterList.map { ReaderChapterItem( chapter = it.chapter.toDomainChapter()!!, manga = manga!!, @@ -462,7 +454,6 @@ class ReaderViewModel @JvmOverloads constructor( ): ViewerChapters { loader.loadChapter(chapter /* SY --> */, page/* SY <-- */) - val chapterList = getChapterList() val chapterPos = chapterList.indexOf(chapter) val newChapters = ViewerChapters( chapter, @@ -512,7 +503,7 @@ class ReaderViewModel @JvmOverloads constructor( fun loadNewChapterFromDialog(chapter: Chapter) { viewModelScope.launchIO { - val newChapter = getChapterList().firstOrNull { it.chapter.id == chapter.id } ?: return@launchIO + val newChapter = chapterList.firstOrNull { it.chapter.id == chapter.id } ?: return@launchIO loadAdjacent(newChapter) } } @@ -674,12 +665,11 @@ class ReaderViewModel @JvmOverloads constructor( * If both conditions are satisfied enqueues chapter for delete * @param currentChapter current chapter, which is going to be marked as read. */ - private suspend fun deleteChapterIfNeeded(currentChapter: ReaderChapter) { + private fun deleteChapterIfNeeded(currentChapter: ReaderChapter) { val removeAfterReadSlots = downloadPreferences.removeAfterReadSlots.get() if (removeAfterReadSlots == -1) return // Determine which chapter should be deleted and enqueue - val chapterList = getChapterList() val currentChapterPosition = chapterList.indexOf(currentChapter) val chapterToDelete = chapterList.getOrNull(currentChapterPosition - removeAfterReadSlots) @@ -749,7 +739,7 @@ class ReaderViewModel @JvmOverloads constructor( // SY --> if (manga?.isEhBasedManga() == true) { viewModelScope.launchNonCancellable { - val chapterUpdates = getUnfilteredChapterList() + val chapterUpdates = unfilteredChapterList .filter { it.sourceOrder > readerChapter.chapter.source_order } .map { chapter -> ChapterUpdate( @@ -769,7 +759,7 @@ class ReaderViewModel @JvmOverloads constructor( .contains(LibraryPreferences.MARK_DUPLICATE_CHAPTER_READ_EXISTING) if (!markDuplicateAsRead) return - val duplicateUnreadChapters = getUnfilteredChapterList() + val duplicateUnreadChapters = unfilteredChapterList .mapNotNull { chapter -> if ( !chapter.read && @@ -784,7 +774,7 @@ class ReaderViewModel @JvmOverloads constructor( updateChapter.awaitAll(duplicateUnreadChapters) // SY --> duplicateUnreadChapters.forEach { chapterUpdate -> - val chapter = getUnfilteredChapterList().first { it.id == chapterUpdate.id } + val chapter = unfilteredChapterList.first { it.id == chapterUpdate.id } deleteChapterIfNeeded(ReaderChapter(chapter)) } // SY <-- @@ -873,9 +863,9 @@ class ReaderViewModel @JvmOverloads constructor( // SY --> fun toggleBookmark(chapterId: Long, bookmarked: Boolean) { + val chapter = chapterList.find { it.chapter.id == chapterId }?.chapter ?: return + chapter.bookmark = bookmarked viewModelScope.launchNonCancellable { - val chapter = getChapterList().find { it.chapter.id == chapterId }?.chapter ?: return@launchNonCancellable - chapter.bookmark = bookmarked updateChapter.await( ChapterUpdate( id = chapterId, @@ -910,7 +900,7 @@ class ReaderViewModel @JvmOverloads constructor( */ fun setMangaReadingMode(readingMode: ReadingMode) { val manga = manga ?: return - viewModelScope.launchIO { + runBlocking(Dispatchers.IO) { setMangaViewerFlags.awaitSetReadingMode(manga.id, readingMode.flagValue.toLong()) val currChapters = state.value.viewerChapters if (currChapters != null) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index bd3692949..3bad43e5d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -251,7 +251,7 @@ class UpdatesScreenModel( } } - private suspend fun startDownloadingNow(chapterId: Long) { + private fun startDownloadingNow(chapterId: Long) { downloadManager.startDownloadNow(chapterId) } diff --git a/app/src/main/java/mihon/core/migration/Migrator.kt b/app/src/main/java/mihon/core/migration/Migrator.kt index 2168d58a1..c01a3873e 100644 --- a/app/src/main/java/mihon/core/migration/Migrator.kt +++ b/app/src/main/java/mihon/core/migration/Migrator.kt @@ -35,7 +35,7 @@ object Migrator { result = null } - suspend fun awaitAndRelease(): Boolean { - return await().also { release() } + fun awaitAndRelease(): Boolean = runBlocking { + await().also { release() } } } diff --git a/data/src/main/java/tachiyomi/data/TransactionContext.kt b/data/src/main/java/tachiyomi/data/TransactionContext.kt index 77154696b..f804a0b8e 100644 --- a/data/src/main/java/tachiyomi/data/TransactionContext.kt +++ b/data/src/main/java/tachiyomi/data/TransactionContext.kt @@ -5,8 +5,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.asContextElement import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.util.concurrent.RejectedExecutionException import kotlin.concurrent.atomics.AtomicInt @@ -19,10 +17,6 @@ import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.coroutineContext import kotlin.coroutines.resume -// Global mutex to serialize transaction entry and prevent thread pool exhaustion. -// If you have multiple distinct database files/handlers, this should be a property of AndroidDatabaseHandler. -private val transactionMutex = Mutex() - /** * Returns the transaction dispatcher if we are on a transaction, or the database dispatchers. */ @@ -45,41 +39,20 @@ internal suspend fun AndroidDatabaseHandler.getCurrentDatabaseContext(): Corouti * The dispatcher used to execute the given [block] will utilize threads from SQLDelight's query executor. */ internal suspend fun AndroidDatabaseHandler.withTransaction(block: suspend () -> T): T { - val transactionElement = coroutineContext[TransactionElement] - - // If we are already in a transaction, we don't need to lock the Mutex. - // We just reuse the existing thread/context. - if (transactionElement != null) { - return withContext(transactionElement.transactionDispatcher) { - transactionElement.acquire() - try { - db.transactionWithResult { - runBlocking(transactionElement.transactionDispatcher) { - block() - } + // Use inherited transaction context if available, this allows nested suspending transactions. + val transactionContext = + coroutineContext[TransactionElement]?.transactionDispatcher ?: createTransactionContext() + return withContext(transactionContext) { + val transactionElement = coroutineContext[TransactionElement]!! + transactionElement.acquire() + try { + db.transactionWithResult { + runBlocking(transactionContext) { + block() } - } finally { - transactionElement.release() - } - } - } - - // transaction: Acquire Mutex BEFORE acquiring a thread. - // This ensures we only block a real thread when we have exclusive access. - return transactionMutex.withLock { - val transactionContext = createTransactionContext() - withContext(transactionContext) { - val element = coroutineContext[TransactionElement]!! - element.acquire() - try { - db.transactionWithResult { - runBlocking(transactionContext) { - block() - } - } - } finally { - element.release() } + } finally { + transactionElement.release() } } } diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index ea15b5d12..ffc399b8c 100755 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -170,7 +170,6 @@ Loading… - Calculating… InternalError: Check crash logs for further information