Compare commits

..

13 Commits

Author SHA1 Message Date
b690de55e5 Release v0.14.5 2023-02-19 15:25:35 -05:00
83fda20078 Avoid crashes if headers can't be built for usage in WebView
(cherry picked from commit ec49411bee)
2023-02-19 11:52:09 -05:00
f656a37045 Avoid crashing if getChapterUrl is not implemented
Fixes #9105

(cherry picked from commit ceaf579cb0)
2023-02-19 11:51:53 -05:00
c58b495433 MainActivity: Avoid navigator-related crash when handling onNewIntent (#9104)
(cherry picked from commit d3dadf71e8)
2023-02-19 11:51:44 -05:00
242aeb6a68 Avoid crashing if opening browse with unavailable source
(cherry picked from commit 0ef7650c1a)
2023-02-19 11:50:33 -05:00
d9969cea8a 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 dc2eaf0788)
2023-02-19 11:50:26 -05:00
d61db5931e Move reader preloading to IO scope
Maybe fixes #8440

(cherry picked from commit e052bdef96)
2023-02-19 11:50:05 -05:00
0ea3ac9807 Avoid preload download check if chapter is already loaded or loading
Maybe fixes #8953, #9060

(cherry picked from commit d522d6d545)
2023-02-19 11:49:58 -05:00
f9e43f574f MangaCoverDialog: Disable memory cache (#9066)
(cherry picked from commit 1671a56f42)
2023-02-19 11:49:51 -05:00
5ef11e61d0 Prioritize finding selected chapter when deduping reader chapters
Fixes #9054

(cherry picked from commit 23432e4405)
2023-02-19 11:49:44 -05:00
48546c3db4 Scaffold: Fix snackbar bottom inset (#9052)
(cherry picked from commit 34a586ce48)
2023-02-19 11:49:38 -05:00
4d87ed496c Remove FAB extra padding in DownloadQueueScreen (#9053)
(cherry picked from commit ad762f8303)
2023-02-19 11:49:32 -05:00
06d12e6562 Fix crash in library when selected category is deleted (#9044)
(cherry picked from commit 13bb45b4be)
2023-02-19 11:49:24 -05:00
19 changed files with 162 additions and 87 deletions

View File

@ -3,7 +3,7 @@
I acknowledge that: I acknowledge that:
- I have updated: - 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 - All extensions
- I have tried the troubleshooting guide: https://tachiyomi.org/help/guides/troubleshooting-problems/ - 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 - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions

View File

@ -53,7 +53,7 @@ body:
label: Tachiyomi version label: Tachiyomi version
description: You can find your Tachiyomi version in **More → About**. description: You can find your Tachiyomi version in **More → About**.
placeholder: | placeholder: |
Example: "0.14.4" Example: "0.14.5"
validations: validations:
required: true required: true
@ -98,7 +98,7 @@ body:
required: true required: true
- label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/). - label: I have tried the [troubleshooting guide](https://tachiyomi.org/help/guides/troubleshooting/).
required: true 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 required: true
- label: I have updated all installed extensions. - label: I have updated all installed extensions.
required: true required: true

View File

@ -33,7 +33,7 @@ body:
required: true 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). - 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 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 required: true
- label: I will fill out all of the requested information in this form. - label: I will fill out all of the requested information in this form.
required: true required: true

View File

@ -22,8 +22,8 @@ android {
defaultConfig { defaultConfig {
applicationId = "eu.kanade.tachiyomi" applicationId = "eu.kanade.tachiyomi"
versionCode = 95 versionCode = 98
versionName = "0.14.4" versionName = "0.14.5"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")

View File

@ -1,6 +1,7 @@
package eu.kanade.presentation.browse package eu.kanade.presentation.browse
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.HelpOutline import androidx.compose.material.icons.outlined.HelpOutline
@ -11,6 +12,7 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems 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.BrowseSourceComfortableGrid
import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid import eu.kanade.presentation.browse.components.BrowseSourceCompactGrid
import eu.kanade.presentation.browse.components.BrowseSourceList import eu.kanade.presentation.browse.components.BrowseSourceList
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.EmptyScreen import eu.kanade.presentation.components.EmptyScreen
import eu.kanade.presentation.components.EmptyScreenAction import eu.kanade.presentation.components.EmptyScreenAction
import eu.kanade.presentation.components.LoadingScreen import eu.kanade.presentation.components.LoadingScreen
import eu.kanade.presentation.components.Scaffold
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@Composable @Composable
fun BrowseSourceContent( fun BrowseSourceContent(
source: CatalogueSource?, source: Source?,
mangaList: LazyPagingItems<StateFlow<Manga>>, mangaList: LazyPagingItems<StateFlow<Manga>>,
columns: GridCells, columns: GridCells,
displayMode: LibraryDisplayMode, 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),
)
}
}

View File

@ -20,15 +20,15 @@ import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.RadioMenuItem import eu.kanade.presentation.components.RadioMenuItem
import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.components.SearchToolbar
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
@Composable @Composable
fun BrowseSourceToolbar( fun BrowseSourceToolbar(
searchQuery: String?, searchQuery: String?,
onSearchQueryChange: (String?) -> Unit, onSearchQueryChange: (String?) -> Unit,
source: CatalogueSource?, source: Source?,
displayMode: LibraryDisplayMode, displayMode: LibraryDisplayMode,
onDisplayModeChange: (LibraryDisplayMode) -> Unit, onDisplayModeChange: (LibraryDisplayMode) -> Unit,
navigateUp: () -> Unit, navigateUp: () -> Unit,

View File

@ -240,13 +240,16 @@ private fun ScaffoldLayout(
) )
}.fastMap { it.measure(looseConstraints) } }.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 { val fabOffsetFromBottom = fabPlacement?.let {
max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx() max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx()
} }
val snackbarOffsetFromBottom = if (snackbarHeight != 0) { val snackbarOffsetFromBottom = if (snackbarHeight != 0) {
snackbarHeight + (fabOffsetFromBottom ?: bottomBarHeight ?: bottomInset) snackbarHeight + (fabOffsetFromBottom ?: max(bottomBarHeight ?: 0, bottomInset))
} else { } else {
0 0
} }

View File

@ -60,6 +60,9 @@ fun LibraryContent(
var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) } var isRefreshing by remember(pagerState.currentPage) { mutableStateOf(false) }
if (showPageTabs && categories.size > 1) { if (showPageTabs && categories.size > 1) {
if (categories.size <= pagerState.currentPage) {
pagerState.currentPage = categories.size - 1
}
LibraryTabs( LibraryTabs(
categories = categories, categories = categories,
currentPageIndex = pagerState.currentPage, currentPageIndex = pagerState.currentPage,

View File

@ -40,6 +40,7 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import coil.imageLoader import coil.imageLoader
import coil.request.CachePolicy
import coil.request.ImageRequest import coil.request.ImageRequest
import coil.size.Size import coil.size.Size
import eu.kanade.presentation.components.DropdownMenu import eu.kanade.presentation.components.DropdownMenu
@ -162,6 +163,7 @@ fun MangaCoverDialog(
val request = ImageRequest.Builder(view.context) val request = ImageRequest.Builder(view.context)
.data(coverDataProvider()) .data(coverDataProvider())
.size(Size.ORIGINAL) .size(Size.ORIGINAL)
.memoryCachePolicy(CachePolicy.DISABLED)
.target { drawable -> .target { drawable ->
// Copy bitmap in case it came from memory cache // Copy bitmap in case it came from memory cache
// Because SSIV needs to thoroughly read the image // Because SSIV needs to thoroughly read the image

View File

@ -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)) Exception(context.getString(R.string.source_not_installed, sourceString))
} }

View File

@ -49,7 +49,7 @@ class MigrateSearchScreenModel(
.filter { it.lang in enabledLanguages } .filter { it.lang in enabledLanguages }
.filterNot { "${it.id}" in disabledSources } .filterNot { "${it.id}" in disabledSources }
.sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" })) .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?) { override fun updateSearchQuery(query: String?) {

View File

@ -39,6 +39,7 @@ import cafe.adriel.voyager.core.screen.uniqueScreenKey
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.browse.BrowseSourceContent 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.BrowseSourceToolbar
import eu.kanade.presentation.browse.components.RemoveMangaDialog import eu.kanade.presentation.browse.components.RemoveMangaDialog
import eu.kanade.presentation.components.ChangeCategoryDialog 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.AssistContentScreen
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing
import eu.kanade.tachiyomi.ui.category.CategoryScreen import eu.kanade.tachiyomi.ui.category.CategoryScreen
@ -73,17 +76,10 @@ data class BrowseSourceScreen(
@Composable @Composable
override fun Content() { 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 screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) }
val state by screenModel.state.collectAsState() val state by screenModel.state.collectAsState()
val snackbarHostState = remember { SnackbarHostState() } val navigator = LocalNavigator.currentOrThrow
val navigateUp: () -> Unit = { val navigateUp: () -> Unit = {
when { when {
!state.isUserQuery && state.toolbarQuery != null -> screenModel.setToolbarQuery(null) !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 onWebViewClick = f@{
val source = screenModel.source as? HttpSource ?: return@f val source = screenModel.source as? HttpSource ?: return@f
navigator.push( navigator.push(
@ -147,7 +156,7 @@ data class BrowseSourceScreen(
Text(text = stringResource(R.string.popular)) Text(text = stringResource(R.string.popular))
}, },
) )
if (screenModel.source.supportsLatest) { if ((screenModel.source as CatalogueSource).supportsLatest) {
FilterChip( FilterChip(
selected = state.listing == Listing.Latest, selected = state.listing == Listing.Latest,
onClick = { onClick = {

View File

@ -102,9 +102,10 @@ class BrowseSourceScreenModel(
var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope) var displayMode by sourcePreferences.sourceDisplayMode().asState(coroutineScope)
val source = sourceManager.get(sourceId) as CatalogueSource val source = sourceManager.getOrStub(sourceId)
init { init {
if (source is CatalogueSource) {
mutableState.update { mutableState.update {
var query: String? = null var query: String? = null
var listing = it.listing var listing = it.listing
@ -121,6 +122,7 @@ class BrowseSourceScreenModel(
) )
} }
} }
}
/** /**
* Sheet containing filter items. * Sheet containing filter items.
@ -162,6 +164,8 @@ class BrowseSourceScreenModel(
} }
fun resetFilters() { fun resetFilters() {
if (source !is CatalogueSource) return
mutableState.update { it.copy(filters = source.getFilterList()) } mutableState.update { it.copy(filters = source.getFilterList()) }
} }
@ -170,6 +174,8 @@ class BrowseSourceScreenModel(
} }
fun search(query: String? = null, filters: FilterList? = null) { fun search(query: String? = null, filters: FilterList? = null) {
if (source !is CatalogueSource) return
val input = state.value.listing as? Listing.Search val input = state.value.listing as? Listing.Search
?: Listing.Search(query = null, filters = source.getFilterList()) ?: Listing.Search(query = null, filters = source.getFilterList())
@ -185,6 +191,8 @@ class BrowseSourceScreenModel(
} }
fun searchGenre(genreName: String) { fun searchGenre(genreName: String) {
if (source !is CatalogueSource) return
val defaultFilters = source.getFilterList() val defaultFilters = source.getFilterList()
var genreExists = false var genreExists = false

View File

@ -8,7 +8,6 @@ import androidx.compose.animation.fadeOut
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
@ -226,7 +225,6 @@ object DownloadQueueScreen : Screen {
} }
}, },
expanded = fabExpanded, expanded = fabExpanded,
modifier = Modifier.navigationBarsPadding(),
) )
} }
}, },

View File

@ -3,12 +3,14 @@ package eu.kanade.tachiyomi.ui.main
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.app.SearchManager import android.app.SearchManager
import android.app.assist.AssistContent import android.app.assist.AssistContent
import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -41,6 +43,7 @@ import androidx.core.animation.doOnEnd
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.splashscreen.SplashScreen import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.util.Consumer
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.interpolator.view.animation.FastOutSlowInInterpolator import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator 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.system.toast
import eu.kanade.tachiyomi.util.view.setComposeContent import eu.kanade.tachiyomi.util.view.setComposeContent
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -119,8 +125,7 @@ class MainActivity : BaseActivity() {
*/ */
private var settingsSheet: LibrarySettingsSheet? = null private var settingsSheet: LibrarySettingsSheet? = null
private var isHandlingShortcut: Boolean = false private var navigator: Navigator? = null
private lateinit var navigator: Navigator
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// Prevent splash screen showing up on configuration changes // Prevent splash screen showing up on configuration changes
@ -210,7 +215,7 @@ class MainActivity : BaseActivity() {
if (savedInstanceState == null) { if (savedInstanceState == null) {
// Set start screen // Set start screen
handleIntentAction(intent) handleIntentAction(intent, navigator)
// Reset Incognito Mode on relaunch // Reset Incognito Mode on relaunch
preferences.incognitoMode().set(false) preferences.incognitoMode().set(false)
@ -257,6 +262,7 @@ class MainActivity : BaseActivity() {
} }
CheckForUpdate() CheckForUpdate()
HandleOnNewIntent(context = context, navigator = navigator)
} }
var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) } var showChangelog by remember { mutableStateOf(didMigration && !BuildConfig.DEBUG) }
@ -288,7 +294,7 @@ class MainActivity : BaseActivity() {
override fun onProvideAssistContent(outContent: AssistContent) { override fun onProvideAssistContent(outContent: AssistContent) {
super.onProvideAssistContent(outContent) super.onProvideAssistContent(outContent)
when (val screen = navigator.lastItem) { when (val screen = navigator?.lastItem) {
is AssistContentScreen -> { is AssistContentScreen -> {
screen.onProvideAssistUrl()?.let { outContent.webUri = it.toUri() } screen.onProvideAssistUrl()?.let { outContent.webUri = it.toUri() }
} }
@ -319,6 +325,18 @@ class MainActivity : BaseActivity() {
} }
} }
@Composable
fun HandleOnNewIntent(context: Context, navigator: Navigator) {
LaunchedEffect(Unit) {
callbackFlow<Intent> {
val componentActivity = context as ComponentActivity
val consumer = Consumer<Intent> { trySend(it) }
componentActivity.addOnNewIntentListener(consumer)
awaitClose { componentActivity.removeOnNewIntentListener(consumer) }
}.collectLatest { handleIntentAction(it, navigator) }
}
}
@Composable @Composable
private fun CheckForUpdate() { private fun CheckForUpdate() {
val context = LocalContext.current val context = LocalContext.current
@ -387,37 +405,26 @@ class MainActivity : BaseActivity() {
} }
} }
override fun onNewIntent(intent: Intent) { private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
lifecycleScope.launch {
val handle = handleIntentAction(intent)
if (!handle) {
super.onNewIntent(intent)
}
}
}
private suspend fun handleIntentAction(intent: Intent): Boolean {
val notificationId = intent.getIntExtra("notificationId", -1) val notificationId = intent.getIntExtra("notificationId", -1)
if (notificationId > -1) { if (notificationId > -1) {
NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0)) NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0))
} }
isHandlingShortcut = true val tabToOpen = when (intent.action) {
Constants.SHORTCUT_LIBRARY -> HomeScreen.Tab.Library()
when (intent.action) {
Constants.SHORTCUT_LIBRARY -> HomeScreen.openTab(HomeScreen.Tab.Library())
Constants.SHORTCUT_MANGA -> { Constants.SHORTCUT_MANGA -> {
val idToOpen = intent.extras?.getLong(Constants.MANGA_EXTRA) ?: return false val idToOpen = intent.extras?.getLong(Constants.MANGA_EXTRA) ?: return false
navigator.popUntilRoot() navigator.popUntilRoot()
HomeScreen.openTab(HomeScreen.Tab.Library(idToOpen)) HomeScreen.Tab.Library(idToOpen)
} }
Constants.SHORTCUT_UPDATES -> HomeScreen.openTab(HomeScreen.Tab.Updates) Constants.SHORTCUT_UPDATES -> HomeScreen.Tab.Updates
Constants.SHORTCUT_HISTORY -> HomeScreen.openTab(HomeScreen.Tab.History) Constants.SHORTCUT_HISTORY -> HomeScreen.Tab.History
Constants.SHORTCUT_SOURCES -> HomeScreen.openTab(HomeScreen.Tab.Browse(false)) Constants.SHORTCUT_SOURCES -> HomeScreen.Tab.Browse(false)
Constants.SHORTCUT_EXTENSIONS -> HomeScreen.openTab(HomeScreen.Tab.Browse(true)) Constants.SHORTCUT_EXTENSIONS -> HomeScreen.Tab.Browse(true)
Constants.SHORTCUT_DOWNLOADS -> { Constants.SHORTCUT_DOWNLOADS -> {
navigator.popUntilRoot() 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" -> { Intent.ACTION_SEARCH, Intent.ACTION_SEND, "com.google.android.gms.actions.SEARCH_ACTION" -> {
// If the intent match the "standard" Android search intent // If the intent match the "standard" Android search intent
@ -429,6 +436,7 @@ class MainActivity : BaseActivity() {
navigator.popUntilRoot() navigator.popUntilRoot()
navigator.push(GlobalSearchScreen(query)) navigator.push(GlobalSearchScreen(query))
} }
null
} }
INTENT_SEARCH -> { INTENT_SEARCH -> {
val query = intent.getStringExtra(INTENT_SEARCH_QUERY) val query = intent.getStringExtra(INTENT_SEARCH_QUERY)
@ -437,15 +445,16 @@ class MainActivity : BaseActivity() {
navigator.popUntilRoot() navigator.popUntilRoot()
navigator.push(GlobalSearchScreen(query, filter)) navigator.push(GlobalSearchScreen(query, filter))
} }
null
} }
else -> { else -> return false
isHandlingShortcut = false
return false
} }
if (tabToOpen != null) {
lifecycleScope.launch { HomeScreen.openTab(tabToOpen) }
} }
ready = true ready = true
isHandlingShortcut = false
return true return true
} }
@ -456,7 +465,7 @@ class MainActivity : BaseActivity() {
} }
override fun onBackPressed() { override fun onBackPressed() {
if (navigator.size == 1 && if (navigator?.size == 1 &&
!onBackPressedDispatcher.hasEnabledCallbacks() && !onBackPressedDispatcher.hasEnabledCallbacks() &&
libraryPreferences.autoClearChapterCache().get() libraryPreferences.autoClearChapterCache().get()
) { ) {

View File

@ -871,7 +871,7 @@ class ReaderActivity : BaseActivity() {
* the viewer is reaching the beginning or end of a chapter or the transition page is active. * the viewer is reaching the beginning or end of a chapter or the transition page is active.
*/ */
fun requestPreloadChapter(chapter: ReaderChapter) { fun requestPreloadChapter(chapter: ReaderChapter) {
lifecycleScope.launch { viewModel.preloadChapter(chapter) } lifecycleScope.launchIO { viewModel.preloadChapter(chapter) }
} }
/** /**

View File

@ -177,10 +177,11 @@ class ReaderViewModel(
}.run { }.run {
if (readerPreferences.skipDupe().get()) { if (readerPreferences.skipDupe().get()) {
groupBy { it.chapterNumber } groupBy { it.chapterNumber }
.mapValues { (_, chapters) -> .map { (_, chapters) ->
chapters.find { it.id == chapterId || it.scanlator == selectedChapter.scanlator } ?: chapters.first() chapters.find { it.id == selectedChapter.id }
?: chapters.find { it.scanlator == selectedChapter.scanlator }
?: chapters.first()
} }
.values
} else { } else {
this this
} }
@ -364,6 +365,10 @@ class ReaderViewModel(
* that the user doesn't have to wait too long to continue reading. * that the user doesn't have to wait too long to continue reading.
*/ */
private suspend fun preload(chapter: ReaderChapter) { private suspend fun preload(chapter: ReaderChapter) {
if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) {
return
}
if (chapter.pageLoader is HttpPageLoader) { if (chapter.pageLoader is HttpPageLoader) {
val manga = manga ?: return val manga = manga ?: return
val dbChapter = chapter.chapter val dbChapter = chapter.chapter
@ -383,21 +388,18 @@ class ReaderViewModel(
return return
} }
logcat { "Preloading ${chapter.chapter.url}" }
val loader = loader ?: return val loader = loader ?: return
withIOContext {
try { try {
logcat { "Preloading ${chapter.chapter.url}" }
loader.loadChapter(chapter) loader.loadChapter(chapter)
} catch (e: Throwable) { } catch (e: Throwable) {
if (e is CancellationException) { if (e is CancellationException) {
throw e throw e
} }
return@withIOContext return
} }
eventChannel.trySend(Event.ReloadViewerChapters) eventChannel.trySend(Event.ReloadViewerChapters)
} }
}
/** /**
* Called every time a page changes on the reader. Used to mark the flag of chapters being * Called every time a page changes on the reader. Used to mark the flag of chapters being
@ -580,7 +582,12 @@ class ReaderViewModel(
val sChapter = getCurrentChapter()?.chapter ?: return null val sChapter = getCurrentChapter()?.chapter ?: return null
val source = getSource() ?: return null val source = getSource() ?: return null
return source.getChapterUrl(sChapter) return try {
source.getChapterUrl(sChapter)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
null
}
} }
/** /**

View File

@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setComposeContent import eu.kanade.tachiyomi.util.view.setComposeContent
import logcat.LogPriority
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
@ -47,7 +48,11 @@ class WebViewActivity : BaseActivity() {
var headers = emptyMap<String, String>() var headers = emptyMap<String, String>()
(sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource)?.let { source -> (sourceManager.get(intent.extras!!.getLong(SOURCE_KEY)) as? HttpSource)?.let { source ->
try {
headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to build headers" }
}
} }
setComposeContent { setComposeContent {

View File

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import logcat.LogPriority
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import tachiyomi.core.util.system.logcat import tachiyomi.core.util.system.logcat
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -25,7 +26,11 @@ class WebViewScreenModel(
init { init {
sourceId?.let { sourceManager.get(it) as? HttpSource }?.let { source -> sourceId?.let { sourceManager.get(it) as? HttpSource }?.let { source ->
try {
headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } headers = source.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" }
} catch (e: Exception) {
logcat(LogPriority.ERROR, e) { "Failed to build headers" }
}
} }
} }