From 06d12e65627e2cf9ce24a754e7cec57fbde8fd83 Mon Sep 17 00:00:00 2001 From: 0x7673 <123292609+0x7673@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:49:46 +0530 Subject: [PATCH 01/13] Fix crash in library when selected category is deleted (#9044) (cherry picked from commit 13bb45b4bee6e31685d47a4157e10bcedf111bf0) --- .../kanade/presentation/library/components/LibraryContent.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt index cf1f2a2ef6..b8ccc2f116 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryContent.kt @@ -60,6 +60,9 @@ fun LibraryContent( var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) } if (showPageTabs && categories.size > 1) { + if (categories.size <= pagerState.currentPage) { + pagerState.currentPage = categories.size - 1 + } LibraryTabs( categories = categories, currentPageIndex = pagerState.currentPage, From 4d87ed496c67f3c0666b0870eceb4179436416a7 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:37:04 +0700 Subject: [PATCH 02/13] Remove FAB extra padding in DownloadQueueScreen (#9053) (cherry picked from commit ad762f8303f2cc37bd2b78fc366b01b80bbc012b) --- .../java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt index 003a85c018..8a81e748a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.animation.fadeOut import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PlayArrow @@ -226,7 +225,6 @@ object DownloadQueueScreen : Screen { } }, expanded = fabExpanded, - modifier = Modifier.navigationBarsPadding(), ) } }, From 48546c3db46f336f6aa72d07ccbe911f086c14d7 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:37:12 +0700 Subject: [PATCH 03/13] Scaffold: Fix snackbar bottom inset (#9052) (cherry picked from commit 34a586ce48d6c10ac9eadb697b955ba336044bc2) --- .../java/eu/kanade/presentation/components/Scaffold.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/Scaffold.kt b/app/src/main/java/eu/kanade/presentation/components/Scaffold.kt index 0475444878..aa76cd92e3 100644 --- a/app/src/main/java/eu/kanade/presentation/components/Scaffold.kt +++ b/app/src/main/java/eu/kanade/presentation/components/Scaffold.kt @@ -240,13 +240,16 @@ private fun ScaffoldLayout( ) }.fastMap { it.measure(looseConstraints) } - val bottomBarHeight = bottomBarPlaceables.fastMaxBy { it.height }?.height + val bottomBarHeight = bottomBarPlaceables + .fastMaxBy { it.height } + ?.height + ?.takeIf { it != 0 } val fabOffsetFromBottom = fabPlacement?.let { max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx() } val snackbarOffsetFromBottom = if (snackbarHeight != 0) { - snackbarHeight + (fabOffsetFromBottom ?: bottomBarHeight ?: bottomInset) + snackbarHeight + (fabOffsetFromBottom ?: max(bottomBarHeight ?: 0, bottomInset)) } else { 0 } From 5ef11e61d06df343007b4b393d24a3c905730ac3 Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 8 Feb 2023 21:35:59 -0500 Subject: [PATCH 04/13] Prioritize finding selected chapter when deduping reader chapters Fixes #9054 (cherry picked from commit 23432e44050cd8638f070745edf77be75aeffe21) --- .../java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 825f682463..a9d31c21c1 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 @@ -177,10 +177,11 @@ class ReaderViewModel( }.run { if (readerPreferences.skipDupe().get()) { groupBy { it.chapterNumber } - .mapValues { (_, chapters) -> - chapters.find { it.id == chapterId || it.scanlator == selectedChapter.scanlator } ?: chapters.first() + .map { (_, chapters) -> + chapters.find { it.id == selectedChapter.id } + ?: chapters.find { it.scanlator == selectedChapter.scanlator } + ?: chapters.first() } - .values } else { this } From f9e43f574fd3af2c223ccb04c50bafeee80a54bc Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 11 Feb 2023 10:38:59 +0700 Subject: [PATCH 05/13] MangaCoverDialog: Disable memory cache (#9066) (cherry picked from commit 1671a56f42c4bb1de3482c2ff264593531e5d39d) --- .../eu/kanade/presentation/manga/components/MangaCoverDialog.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt index 6b3c8c4e2e..e946383caa 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt @@ -40,6 +40,7 @@ import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.core.view.updatePadding import coil.imageLoader +import coil.request.CachePolicy import coil.request.ImageRequest import coil.size.Size import eu.kanade.presentation.components.DropdownMenu @@ -162,6 +163,7 @@ fun MangaCoverDialog( val request = ImageRequest.Builder(view.context) .data(coverDataProvider()) .size(Size.ORIGINAL) + .memoryCachePolicy(CachePolicy.DISABLED) .target { drawable -> // Copy bitmap in case it came from memory cache // Because SSIV needs to thoroughly read the image From 0ea3ac9807dd7f07b8882b458e1139b695c41368 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 12 Feb 2023 16:03:24 -0500 Subject: [PATCH 06/13] Avoid preload download check if chapter is already loaded or loading Maybe fixes #8953, #9060 (cherry picked from commit d522d6d545bfbd4e4f8f60975bddcd5f6bcc69ac) --- .../java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt | 4 ++++ 1 file changed, 4 insertions(+) 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 a9d31c21c1..3498f17456 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 @@ -365,6 +365,10 @@ class ReaderViewModel( * that the user doesn't have to wait too long to continue reading. */ private suspend fun preload(chapter: ReaderChapter) { + if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) { + return + } + if (chapter.pageLoader is HttpPageLoader) { val manga = manga ?: return val dbChapter = chapter.chapter From d61db5931efd3d55fc25f2026d0b487f9d2cca7c Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 12 Feb 2023 16:14:12 -0500 Subject: [PATCH 07/13] Move reader preloading to IO scope Maybe fixes #8440 (cherry picked from commit e052bdef96c133b92dfad214c2b05ab03d4c5866) --- .../tachiyomi/ui/reader/ReaderActivity.kt | 2 +- .../tachiyomi/ui/reader/ReaderViewModel.kt | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) 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 1fcf8a0fe1..0d1d2ef099 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 @@ -871,7 +871,7 @@ class ReaderActivity : BaseActivity() { * the viewer is reaching the beginning or end of a chapter or the transition page is active. */ fun requestPreloadChapter(chapter: ReaderChapter) { - lifecycleScope.launch { viewModel.preloadChapter(chapter) } + lifecycleScope.launchIO { viewModel.preloadChapter(chapter) } } /** 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 3498f17456..23a87204e2 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 @@ -388,20 +388,17 @@ class ReaderViewModel( return } - logcat { "Preloading ${chapter.chapter.url}" } - val loader = loader ?: return - withIOContext { - try { - loader.loadChapter(chapter) - } catch (e: Throwable) { - if (e is CancellationException) { - throw e - } - return@withIOContext + try { + logcat { "Preloading ${chapter.chapter.url}" } + loader.loadChapter(chapter) + } catch (e: Throwable) { + if (e is CancellationException) { + throw e } - eventChannel.trySend(Event.ReloadViewerChapters) + return } + eventChannel.trySend(Event.ReloadViewerChapters) } /** From d9969cea8afdce26eb8c7f57f214cd04cfb8c836 Mon Sep 17 00:00:00 2001 From: Two-Ai <81279822+Two-Ai@users.noreply.github.com> Date: Tue, 14 Feb 2023 11:46:31 -0500 Subject: [PATCH 08/13] Fix ID type mismatch in MigrateSearchScreenModel (#9090) `it.id` is the source ID of the source being sorted. `state.value.manga!!.id` is the manga ID of the selected manga. `state.value.manga!!.source` is the source ID of the selected manga. (cherry picked from commit dc2eaf07882ce1e7acaea9de7eb26de331ef6c77) --- .../ui/browse/migration/search/MigrateSearchScreenModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenModel.kt index 52da778878..6714f308d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreenModel.kt @@ -49,7 +49,7 @@ class MigrateSearchScreenModel( .filter { it.lang in enabledLanguages } .filterNot { "${it.id}" in disabledSources } .sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" })) - .sortedByDescending { it.id == state.value.manga!!.id } + .sortedByDescending { it.id == state.value.manga!!.source } } override fun updateSearchQuery(query: String?) { From 242aeb6a68cd02a3e99ed29f0ac0c73b1a8334d6 Mon Sep 17 00:00:00 2001 From: arkon Date: Wed, 15 Feb 2023 22:47:47 -0500 Subject: [PATCH 09/13] Avoid crashing if opening browse with unavailable source (cherry picked from commit 0ef7650c1a0ae7c4c6e17e458695191ce78944cb) --- .../presentation/browse/BrowseSourceScreen.kt | 30 ++++++++++++++-- .../browse/components/BrowseSourceToolbar.kt | 4 +-- .../kanade/tachiyomi/source/SourceManager.kt | 2 +- .../source/browse/BrowseSourceScreen.kt | 29 ++++++++++------ .../source/browse/BrowseSourceScreenModel.kt | 34 ++++++++++++------- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index 8faf022665..b1edd5cf1e 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.HelpOutline @@ -11,6 +12,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems @@ -18,19 +20,22 @@ import eu.kanade.data.source.NoResultsException import eu.kanade.presentation.browse.components.BrowseSourceComfortableGrid import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid import eu.kanade.presentation.browse.components.BrowseSourceList +import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.components.LoadingScreen +import eu.kanade.presentation.components.Scaffold import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceManager import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.manga.model.Manga @Composable fun BrowseSourceContent( - source: CatalogueSource?, + source: Source?, mangaList: LazyPagingItems>, columns: GridCells, displayMode: LibraryDisplayMode, @@ -139,3 +144,24 @@ fun BrowseSourceContent( } } } + +@Composable +fun MissingSourceScreen( + source: SourceManager.StubSource, + navigateUp: () -> Unit, +) { + Scaffold( + topBar = { scrollBehavior -> + AppBar( + title = source.name, + navigateUp = navigateUp, + scrollBehavior = scrollBehavior, + ) + }, + ) { paddingValues -> + EmptyScreen( + message = source.getSourceNotInstalledException().message!!, + modifier = Modifier.padding(paddingValues), + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt index 9d82dc4dd6..e0d4bc51a9 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt @@ -20,15 +20,15 @@ import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.RadioMenuItem import eu.kanade.presentation.components.SearchToolbar import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.source.Source import tachiyomi.domain.library.model.LibraryDisplayMode @Composable fun BrowseSourceToolbar( searchQuery: String?, onSearchQueryChange: (String?) -> Unit, - source: CatalogueSource?, + source: Source?, displayMode: LibraryDisplayMode, onDisplayModeChange: (LibraryDisplayMode) -> Unit, navigateUp: () -> Unit, diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt index 2c0f947007..a93084a33b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt @@ -152,6 +152,6 @@ class SourceManager( } } - inner class SourceNotInstalledException(val sourceString: String) : + inner class SourceNotInstalledException(sourceString: String) : Exception(context.getString(R.string.source_not_installed, sourceString)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index aa5aa3bc99..f14182f063 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -39,6 +39,7 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.browse.BrowseSourceContent +import eu.kanade.presentation.browse.MissingSourceScreen import eu.kanade.presentation.browse.components.BrowseSourceToolbar import eu.kanade.presentation.browse.components.RemoveMangaDialog import eu.kanade.presentation.components.ChangeCategoryDialog @@ -48,7 +49,9 @@ import eu.kanade.presentation.components.Scaffold import eu.kanade.presentation.util.AssistContentScreen import eu.kanade.presentation.util.padding import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.LocalSource +import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.category.CategoryScreen @@ -73,17 +76,10 @@ data class BrowseSourceScreen( @Composable override fun Content() { - val navigator = LocalNavigator.currentOrThrow - val scope = rememberCoroutineScope() - val context = LocalContext.current - val haptic = LocalHapticFeedback.current - val uriHandler = LocalUriHandler.current - val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) } val state by screenModel.state.collectAsState() - val snackbarHostState = remember { SnackbarHostState() } - + val navigator = LocalNavigator.currentOrThrow val navigateUp: () -> Unit = { when { !state.isUserQuery && state.toolbarQuery != null -> screenModel.setToolbarQuery(null) @@ -91,8 +87,21 @@ data class BrowseSourceScreen( } } - val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) } + if (screenModel.source is SourceManager.StubSource) { + MissingSourceScreen( + source = screenModel.source, + navigateUp = navigateUp, + ) + return + } + val scope = rememberCoroutineScope() + val context = LocalContext.current + val haptic = LocalHapticFeedback.current + val uriHandler = LocalUriHandler.current + val snackbarHostState = remember { SnackbarHostState() } + + val onHelpClick = { uriHandler.openUri(LocalSource.HELP_URL) } val onWebViewClick = f@{ val source = screenModel.source as? HttpSource ?: return@f navigator.push( @@ -147,7 +156,7 @@ data class BrowseSourceScreen( Text(text = stringResource(R.string.popular)) }, ) - if (screenModel.source.supportsLatest) { + if ((screenModel.source as CatalogueSource).supportsLatest) { FilterChip( selected = state.listing == Listing.Latest, onClick = { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index 6c2e5fc575..6b40f6b829 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -102,23 +102,25 @@ class BrowseSourceScreenModel( var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope) - val source = sourceManager.get(sourceId) as CatalogueSource + val source = sourceManager.getOrStub(sourceId) init { - mutableState.update { - var query: String? = null - var listing = it.listing + if (source is CatalogueSource) { + mutableState.update { + var query: String? = null + var listing = it.listing - if (listing is Listing.Search) { - query = listing.query - listing = Listing.Search(query, source.getFilterList()) + if (listing is Listing.Search) { + query = listing.query + listing = Listing.Search(query, source.getFilterList()) + } + + it.copy( + listing = listing, + filters = source.getFilterList(), + toolbarQuery = query, + ) } - - it.copy( - listing = listing, - filters = source.getFilterList(), - toolbarQuery = query, - ) } } @@ -162,6 +164,8 @@ class BrowseSourceScreenModel( } fun resetFilters() { + if (source !is CatalogueSource) return + mutableState.update { it.copy(filters = source.getFilterList()) } } @@ -170,6 +174,8 @@ class BrowseSourceScreenModel( } fun search(query: String? = null, filters: FilterList? = null) { + if (source !is CatalogueSource) return + val input = state.value.listing as? Listing.Search ?: Listing.Search(query = null, filters = source.getFilterList()) @@ -185,6 +191,8 @@ class BrowseSourceScreenModel( } fun searchGenre(genreName: String) { + if (source !is CatalogueSource) return + val defaultFilters = source.getFilterList() var genreExists = false From c58b495433511dfa68a7381c1b7623b357d2269f Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 18 Feb 2023 22:08:37 +0700 Subject: [PATCH 10/13] MainActivity: Avoid navigator-related crash when handling onNewIntent (#9104) (cherry picked from commit d3dadf71e8d7d029fdb87b44217e001f95f21c1a) --- .../kanade/tachiyomi/ui/main/MainActivity.kt | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) 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 dd81afa57e..cdbdf470bc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -3,12 +3,14 @@ package eu.kanade.tachiyomi.ui.main import android.animation.ValueAnimator import android.app.SearchManager import android.app.assist.AssistContent +import android.content.Context import android.content.Intent import android.graphics.Color import android.os.Build import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Box @@ -41,6 +43,7 @@ import androidx.core.animation.doOnEnd import androidx.core.net.toUri import androidx.core.splashscreen.SplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.core.util.Consumer import androidx.core.view.WindowCompat import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.interpolator.view.animation.LinearOutSlowInInterpolator @@ -85,7 +88,10 @@ import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.setComposeContent import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn @@ -119,8 +125,7 @@ class MainActivity : BaseActivity() { */ private var settingsSheet: LibrarySettingsSheet? = null - private var isHandlingShortcut: Boolean = false - private lateinit var navigator: Navigator + private var navigator: Navigator? = null override fun onCreate(savedInstanceState: Bundle?) { // Prevent splash screen showing up on configuration changes @@ -210,7 +215,7 @@ class MainActivity : BaseActivity() { if (savedInstanceState == null) { // Set start screen - handleIntentAction(intent) + handleIntentAction(intent, navigator) // Reset Incognito Mode on relaunch preferences.incognitoMode().set(false) @@ -257,6 +262,7 @@ class MainActivity : BaseActivity() { } CheckForUpdate() + HandleOnNewIntent(context = context, navigator = navigator) } var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) } @@ -288,7 +294,7 @@ class MainActivity : BaseActivity() { override fun onProvideAssistContent(outContent: AssistContent) { super.onProvideAssistContent(outContent) - when (val screen = navigator.lastItem) { + when (val screen = navigator?.lastItem) { is AssistContentScreen -> { screen.onProvideAssistUrl()?.let { outContent.webUri = it.toUri() } } @@ -319,6 +325,18 @@ class MainActivity : BaseActivity() { } } + @Composable + fun HandleOnNewIntent(context: Context, navigator: Navigator) { + LaunchedEffect(Unit) { + callbackFlow { + val componentActivity = context as ComponentActivity + val consumer = Consumer { trySend(it) } + componentActivity.addOnNewIntentListener(consumer) + awaitClose { componentActivity.removeOnNewIntentListener(consumer) } + }.collectLatest { handleIntentAction(it, navigator) } + } + } + @Composable private fun CheckForUpdate() { val context = LocalContext.current @@ -387,37 +405,26 @@ class MainActivity : BaseActivity() { } } - override fun onNewIntent(intent: Intent) { - lifecycleScope.launch { - val handle = handleIntentAction(intent) - if (!handle) { - super.onNewIntent(intent) - } - } - } - - private suspend fun handleIntentAction(intent: Intent): Boolean { + private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean { val notificationId = intent.getIntExtra("notificationId", -1) if (notificationId > -1) { NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0)) } - isHandlingShortcut = true - - when (intent.action) { - Constants.SHORTCUT_LIBRARY -> HomeScreen.openTab(HomeScreen.Tab.Library()) + val tabToOpen = when (intent.action) { + Constants.SHORTCUT_LIBRARY -> HomeScreen.Tab.Library() Constants.SHORTCUT_MANGA -> { val idToOpen = intent.extras?.getLong(Constants.MANGA_EXTRA) ?: return false navigator.popUntilRoot() - HomeScreen.openTab(HomeScreen.Tab.Library(idToOpen)) + HomeScreen.Tab.Library(idToOpen) } - Constants.SHORTCUT_UPDATES -> HomeScreen.openTab(HomeScreen.Tab.Updates) - Constants.SHORTCUT_HISTORY -> HomeScreen.openTab(HomeScreen.Tab.History) - Constants.SHORTCUT_SOURCES -> HomeScreen.openTab(HomeScreen.Tab.Browse(false)) - Constants.SHORTCUT_EXTENSIONS -> HomeScreen.openTab(HomeScreen.Tab.Browse(true)) + Constants.SHORTCUT_UPDATES -> HomeScreen.Tab.Updates + Constants.SHORTCUT_HISTORY -> HomeScreen.Tab.History + Constants.SHORTCUT_SOURCES -> HomeScreen.Tab.Browse(false) + Constants.SHORTCUT_EXTENSIONS -> HomeScreen.Tab.Browse(true) Constants.SHORTCUT_DOWNLOADS -> { navigator.popUntilRoot() - HomeScreen.openTab(HomeScreen.Tab.More(toDownloads = true)) + HomeScreen.Tab.More(toDownloads = true) } Intent.ACTION_SEARCH, Intent.ACTION_SEND, "com.google.android.gms.actions.SEARCH_ACTION" -> { // If the intent match the "standard" Android search intent @@ -429,6 +436,7 @@ class MainActivity : BaseActivity() { navigator.popUntilRoot() navigator.push(GlobalSearchScreen(query)) } + null } INTENT_SEARCH -> { val query = intent.getStringExtra(INTENT_SEARCH_QUERY) @@ -437,15 +445,16 @@ class MainActivity : BaseActivity() { navigator.popUntilRoot() navigator.push(GlobalSearchScreen(query, filter)) } + null } - else -> { - isHandlingShortcut = false - return false - } + else -> return false + } + + if (tabToOpen != null) { + lifecycleScope.launch { HomeScreen.openTab(tabToOpen) } } ready = true - isHandlingShortcut = false return true } @@ -456,7 +465,7 @@ class MainActivity : BaseActivity() { } override fun onBackPressed() { - if (navigator.size == 1 && + if (navigator?.size == 1 && !onBackPressedDispatcher.hasEnabledCallbacks() && libraryPreferences.autoClearChapterCache().get() ) { From f656a37045b6b9206929c5175b2e8a579d86dba7 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 18 Feb 2023 10:16:17 -0500 Subject: [PATCH 11/13] Avoid crashing if getChapterUrl is not implemented Fixes #9105 (cherry picked from commit ceaf579cb0b4b0ecdf2e56f245a2fad2b6ed2e85) --- .../java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 23a87204e2..203bcbff1d 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 @@ -582,7 +582,12 @@ class ReaderViewModel( val sChapter = getCurrentChapter()?.chapter ?: return null val source = getSource() ?: return null - return source.getChapterUrl(sChapter) + return try { + source.getChapterUrl(sChapter) + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) + null + } } /** From 83fda20078de449fba54ae6c31b25ce946fbfe57 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 19 Feb 2023 11:48:26 -0500 Subject: [PATCH 12/13] Avoid crashes if headers can't be built for usage in WebView (cherry picked from commit ec49411bee9d7099e13685a02d54edabed863557) --- .../java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt | 7 ++++++- .../eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt index 12ae057141..773e63d313 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt @@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.setComposeContent +import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrl import tachiyomi.core.util.system.logcat import uy.kohesive.injekt.injectLazy @@ -47,7 +48,11 @@ class WebViewActivity : BaseActivity() { var headers = emptyMap() (sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource)?.let { source -> - headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } + try { + headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) { "Failed to build headers" } + } } setComposeContent { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt index a050560860..bd78fefc75 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt @@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast +import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrl import tachiyomi.core.util.system.logcat import uy.kohesive.injekt.Injekt @@ -25,7 +26,11 @@ class WebViewScreenModel( init { sourceId?.let { sourceManager.get(it) as? HttpSource }?.let { source -> - headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } + try { + headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) { "Failed to build headers" } + } } } From b690de55e510f920ea845f4f34c89c8a2ff419ce Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 19 Feb 2023 11:59:11 -0500 Subject: [PATCH 13/13] Release v0.14.5 --- .github/ISSUE_TEMPLATE.md | 2 +- .github/ISSUE_TEMPLATE/report_issue.yml | 4 ++-- .github/ISSUE_TEMPLATE/request_feature.yml | 2 +- app/build.gradle.kts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 37c474194e..2ecec04231 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,7 +3,7 @@ I acknowledge that: - I have updated: - - To the latest version of the app (stable is v0.14.4) + - To the latest version of the app (stable is v0.14.5) - All extensions - I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/ - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index bb35e707da..0b7be7954d 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -53,7 +53,7 @@ body: label: Tachiyomi version description: You can find your Tachiyomi version in **More → About**. placeholder: | - Example: "0.14.4" + Example: "0.14.5" validations: required: true @@ -98,7 +98,7 @@ body: required: true - label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/). required: true - - label: I have updated the app to version **[0.14.4](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**. + - label: I have updated the app to version **[0.14.5](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**. required: true - label: I have updated all installed extensions. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index 733cb677e8..93e51ccd25 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -33,7 +33,7 @@ body: required: true - label: If this is an issue with an extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose). required: true - - label: I have updated the app to version **[0.14.4](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**. + - label: I have updated the app to version **[0.14.5](https://github.com/tachiyomiorg/tachiyomi/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b2c23342e4..9b06f69f82 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi" - versionCode = 95 - versionName = "0.14.4" + versionCode = 98 + versionName = "0.14.5" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")