diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e913bc8cf..cee48345e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -69,11 +69,39 @@
android:name=".crash.CrashActivity"
android:exported="false" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
index 31ad332ee..7a5312801 100644
--- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
+++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
@@ -1,6 +1,8 @@
package eu.kanade.domain.base
import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
import eu.kanade.tachiyomi.core.preference.PreferenceStore
import eu.kanade.tachiyomi.core.preference.getEnum
import eu.kanade.tachiyomi.data.preference.PreferenceValues
@@ -26,4 +28,6 @@ class BasePreferences(
// acra is disabled
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", false)
+
+ fun deviceHasPip() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt
index f57bdf62d..d5ea26fd6 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeExtensionDetailsScreen.kt
@@ -158,7 +158,7 @@ private fun AnimeExtensionDetails(
when {
extension.isUnofficial ->
item {
- WarningBanner(R.string.unofficial_extension_message)
+ WarningBanner(R.string.unofficial_anime_extension_message)
}
extension.isObsolete ->
item {
diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt
index 23d7d0705..2ed02ec86 100644
--- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt
@@ -30,12 +30,12 @@ fun TabbedScreen(
@StringRes titleRes: Int?,
tabs: List,
startIndex: Int? = null,
- searchQuery: String? = null,
- onChangeSearchQuery: (String?) -> Unit = {},
+ mangaSearchQuery: String? = null,
+ onChangeMangaSearchQuery: (String?) -> Unit = {},
state: PagerState = rememberPagerState(),
scrollable: Boolean = false,
- searchQueryAnime: String? = null,
- onChangeSearchQueryAnime: (String?) -> Unit = {},
+ animeSearchQuery: String? = null,
+ onChangeAnimeSearchQuery: (String?) -> Unit = {},
) {
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
@@ -53,13 +53,13 @@ fun TabbedScreen(
val searchEnabled = tab.searchEnabled
val actualQuery = when (state.currentPage % 2) {
- 1 -> searchQuery // History and Browse
- else -> searchQueryAnime
+ 1 -> mangaSearchQuery // History and Browse
+ else -> animeSearchQuery
}
val actualOnChange = when (state.currentPage % 2) {
- 1 -> onChangeSearchQuery // History and Browse
- else -> onChangeSearchQueryAnime
+ 1 -> onChangeMangaSearchQuery // History and Browse
+ else -> onChangeAnimeSearchQuery
}
SearchToolbar(
diff --git a/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt
index b8722d365..f479c76a7 100644
--- a/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/history/anime/AnimeHistoryScreen.kt
@@ -20,6 +20,7 @@ import java.util.Date
fun AnimeHistoryScreen(
state: AnimeHistoryState,
contentPadding: PaddingValues,
+ searchQuery: String? = null,
snackbarHostState: SnackbarHostState,
onClickCover: (animeId: Long) -> Unit,
onClickResume: (animeId: Long, episodeId: Long) -> Unit,
@@ -32,7 +33,7 @@ fun AnimeHistoryScreen(
if (it == null) {
LoadingScreen(modifier = Modifier.padding(contentPadding))
} else if (it.isEmpty()) {
- val msg = if (!state.searchQuery.isNullOrEmpty()) {
+ val msg = if (!searchQuery.isNullOrEmpty()) {
R.string.no_results_found
} else {
R.string.information_no_recent_anime
diff --git a/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt
index 14ec46b27..c8eca6d9f 100644
--- a/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/history/manga/MangaHistoryScreen.kt
@@ -19,6 +19,7 @@ import java.util.Date
fun MangaHistoryScreen(
state: HistoryState,
contentPadding: PaddingValues,
+ searchQuery: String? = null,
snackbarHostState: SnackbarHostState,
onClickCover: (mangaId: Long) -> Unit,
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
@@ -31,7 +32,7 @@ fun MangaHistoryScreen(
if (it == null) {
LoadingScreen(modifier = Modifier.padding(contentPadding))
} else if (it.isEmpty()) {
- val msg = if (!state.searchQuery.isNullOrEmpty()) {
+ val msg = if (!searchQuery.isNullOrEmpty()) {
R.string.no_results_found
} else {
R.string.information_no_recent_manga
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt
index 8db6d17ba..8ce678436 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt
@@ -100,7 +100,7 @@ class ClearDatabaseScreen : Screen {
Scaffold(
topBar = { scrollBehavior ->
AppBar(
- title = stringResource(R.string.pref_clear_database),
+ title = stringResource(R.string.pref_clear_manga_database),
navigateUp = navigator::pop,
actions = {
if (s.items.isNotEmpty()) {
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
index f767728c2..a4ca27535 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
@@ -205,8 +205,8 @@ object SettingsAdvancedScreen : SearchableSettings {
},
),
Preference.PreferenceItem.TextPreference(
- title = stringResource(R.string.pref_clear_database),
- subtitle = stringResource(R.string.pref_clear_database_summary),
+ title = stringResource(R.string.pref_clear_manga_database),
+ subtitle = stringResource(R.string.pref_clear_manga_database_summary),
onClick = { navigator.push(ClearDatabaseScreen()) },
),
Preference.PreferenceItem.TextPreference(
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt
index 7f98ade71..632bbf0fc 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt
@@ -51,7 +51,7 @@ object SettingsBrowseScreen : SearchableSettings {
),
Preference.PreferenceItem.SwitchPreference(
pref = sourcePreferences.searchPinnedMangaSourcesOnly(),
- title = stringResource(R.string.pref_search_pinned_sources_only),
+ title = stringResource(R.string.pref_search_pinned_manga_sources_only),
),
),
),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
index 97ee8d619..8bf3d4d50 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
@@ -200,7 +200,7 @@ object SettingsDownloadScreen : SearchableSettings {
): Preference.PreferenceItem.MultiSelectListPreference {
return Preference.PreferenceItem.MultiSelectListPreference(
pref = downloadPreferences.removeExcludeCategories(),
- title = stringResource(R.string.pref_remove_exclude_categories),
+ title = stringResource(R.string.pref_remove_exclude_categories_manga),
entries = categories().associate { it.id.toString() to it.visualName },
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
index 6cca63e34..48080730a 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
@@ -197,7 +197,7 @@ object SettingsLibraryScreen : SearchableSettings {
),
Preference.PreferenceItem.ListPreference(
pref = libraryPreferences.defaultMangaCategory(),
- title = stringResource(R.string.default_category),
+ title = stringResource(R.string.default_manga_category),
subtitle = selectedCategory?.visualName ?: stringResource(R.string.default_category_summary),
entries = mangaIds.zip(mangaLabels).toMap(),
),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt
index e7760581c..6f569bd3e 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt
@@ -1,7 +1,6 @@
package eu.kanade.presentation.more.settings.screen
import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
import android.os.Build
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Box
@@ -42,8 +41,9 @@ object SettingsPlayerScreen : SearchableSettings {
override fun getPreferences(): List {
val playerPreferences = remember { Injekt.get() }
val basePreferences = remember { Injekt.get() }
+ val deviceSupportsPip = basePreferences.deviceHasPip()
- return listOf(
+ return listOfNotNull(
Preference.PreferenceItem.ListPreference(
pref = playerPreferences.progressPreference(),
title = stringResource(R.string.pref_progress_mark_as_seen),
@@ -61,13 +61,74 @@ object SettingsPlayerScreen : SearchableSettings {
pref = playerPreferences.preserveWatchingPosition(),
title = stringResource(R.string.pref_preserve_watching_position),
),
+ getInternalPlayerGroup(playerPreferences = playerPreferences),
+ getVolumeAndBrightnessGroup(playerPreferences = playerPreferences),
getOrientationGroup(playerPreferences = playerPreferences),
- getInternalPlayerGroup(playerPreferences = playerPreferences, basePreferences = basePreferences),
- getAniskipGroup(playerPreferences = playerPreferences),
+ getSeekingGroup(playerPreferences = playerPreferences),
+ if (deviceSupportsPip) getPipGroup(playerPreferences = playerPreferences) else null,
getExternalPlayerGroup(playerPreferences = playerPreferences, basePreferences = basePreferences),
)
}
+ @Composable
+ private fun getInternalPlayerGroup(playerPreferences: PlayerPreferences): Preference.PreferenceGroup {
+ val scope = rememberCoroutineScope()
+ val playerFullscreen = playerPreferences.playerFullscreen()
+ val playerHideControls = playerPreferences.hideControls()
+ val mpvConf = playerPreferences.mpvConf()
+
+ return Preference.PreferenceGroup(
+ title = stringResource(R.string.pref_category_internal_player),
+ preferenceItems = listOf(
+ Preference.PreferenceItem.SwitchPreference(
+ pref = playerFullscreen,
+ title = stringResource(R.string.pref_player_fullscreen),
+ enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P,
+ ),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = playerHideControls,
+ title = stringResource(R.string.pref_player_hide_controls),
+ ),
+ Preference.PreferenceItem.MultiLineEditTextPreference(
+ pref = mpvConf,
+ title = stringResource(R.string.pref_mpv_conf),
+ subtitle = mpvConf.asState(scope).value
+ .lines().take(2)
+ .joinToString(
+ separator = "\n",
+ postfix = if (mpvConf.asState(scope).value.lines().size > 2) "\n..." else "",
+ ),
+
+ ),
+ ),
+ )
+ }
+
+ @Composable
+ private fun getVolumeAndBrightnessGroup(playerPreferences: PlayerPreferences): Preference.PreferenceGroup {
+ val enableVolumeBrightnessGestures = playerPreferences.gestureVolumeBrightness()
+ val rememberPlayerBrightness = playerPreferences.rememberPlayerBrightness()
+ val rememberPlayerVolume = playerPreferences.rememberPlayerVolume()
+
+ return Preference.PreferenceGroup(
+ title = stringResource(R.string.pref_category_volume_brightness),
+ preferenceItems = listOf(
+ Preference.PreferenceItem.SwitchPreference(
+ pref = enableVolumeBrightnessGestures,
+ title = stringResource(R.string.enable_volume_brightness_gestures),
+ ),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = rememberPlayerBrightness,
+ title = stringResource(R.string.pref_remember_brightness),
+ ),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = rememberPlayerVolume,
+ title = stringResource(R.string.pref_remember_volume),
+ ),
+ ),
+ )
+ }
+
@Composable
private fun getOrientationGroup(playerPreferences: PlayerPreferences): Preference.PreferenceGroup {
val defaultPlayerOrientationType = playerPreferences.defaultPlayerOrientationType()
@@ -118,21 +179,12 @@ object SettingsPlayerScreen : SearchableSettings {
}
@Composable
- private fun getInternalPlayerGroup(playerPreferences: PlayerPreferences, basePreferences: BasePreferences): Preference.PreferenceGroup {
+ private fun getSeekingGroup(playerPreferences: PlayerPreferences): Preference.PreferenceGroup {
val scope = rememberCoroutineScope()
+ val enableHorizontalSeekGesture = playerPreferences.gestureHorizontalSeek()
val defaultSkipIntroLength by playerPreferences.defaultIntroLength().stateIn(scope).collectAsState()
val skipLengthPreference = playerPreferences.skipLengthPreference()
val playerSmoothSeek = playerPreferences.playerSmoothSeek()
- val playerFullscreen = playerPreferences.playerFullscreen()
- val playerHideControls = playerPreferences.hideControls()
- val pipEpisodeToasts = playerPreferences.pipEpisodeToasts()
- val pipOnExit = playerPreferences.pipOnExit()
- val rememberPlayerBrightness = playerPreferences.rememberPlayerBrightness()
- val rememberPlayerVolume = playerPreferences.rememberPlayerVolume()
- val mpvConf = playerPreferences.mpvConf()
-
- val deviceHasPip = basePreferences.context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) &&
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
var showDialog by rememberSaveable { mutableStateOf(false) }
if (showDialog) {
@@ -146,9 +198,21 @@ object SettingsPlayerScreen : SearchableSettings {
)
}
+ // Aniskip
+ val enableAniSkip = playerPreferences.aniSkipEnabled()
+ val enableAutoAniSkip = playerPreferences.autoSkipAniSkip()
+ val enableNetflixAniSkip = playerPreferences.enableNetflixStyleAniSkip()
+ val waitingTimeAniSkip = playerPreferences.waitingTimeAniSkip()
+
+ val isAniSkipEnabled by enableAniSkip.collectAsState()
+
return Preference.PreferenceGroup(
- title = stringResource(R.string.pref_category_internal_player),
+ title = stringResource(R.string.pref_category_player_seeking),
preferenceItems = listOf(
+ Preference.PreferenceItem.SwitchPreference(
+ pref = enableHorizontalSeekGesture,
+ title = stringResource(R.string.enable_horizontal_seek_gesture),
+ ),
Preference.PreferenceItem.TextPreference(
title = stringResource(R.string.pref_default_intro_length),
subtitle = "${defaultSkipIntroLength}s",
@@ -170,60 +234,9 @@ object SettingsPlayerScreen : SearchableSettings {
title = stringResource(R.string.pref_player_smooth_seek),
subtitle = stringResource(R.string.pref_player_smooth_seek_summary),
),
- Preference.PreferenceItem.SwitchPreference(
- pref = playerFullscreen,
- title = stringResource(R.string.pref_player_fullscreen),
- enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P,
+ Preference.PreferenceItem.InfoPreference(
+ title = stringResource(R.string.pref_category_player_aniskip_info),
),
- Preference.PreferenceItem.SwitchPreference(
- pref = playerHideControls,
- title = stringResource(R.string.pref_player_hide_controls),
- ),
- Preference.PreferenceItem.SwitchPreference(
- pref = pipEpisodeToasts,
- title = stringResource(R.string.pref_pip_episode_toasts),
- enabled = deviceHasPip,
- ),
- Preference.PreferenceItem.SwitchPreference(
- pref = pipOnExit,
- title = stringResource(R.string.pref_pip_on_exit),
- enabled = deviceHasPip,
- ),
- Preference.PreferenceItem.SwitchPreference(
- pref = rememberPlayerBrightness,
- title = stringResource(R.string.pref_remember_brightness),
- ),
- Preference.PreferenceItem.SwitchPreference(
- pref = rememberPlayerVolume,
- title = stringResource(R.string.pref_remember_volume),
- ),
- Preference.PreferenceItem.MultiLineEditTextPreference(
- pref = mpvConf,
- title = stringResource(R.string.pref_mpv_conf),
- subtitle = mpvConf.asState(scope).value
- .lines().take(2)
- .joinToString(
- separator = "\n",
- postfix = if (mpvConf.asState(scope).value.lines().size > 2) "\n..." else "",
- ),
-
- ),
- ),
- )
- }
-
- @Composable
- private fun getAniskipGroup(playerPreferences: PlayerPreferences): Preference.PreferenceGroup {
- val enableAniSkip = playerPreferences.aniSkipEnabled()
- val enableAutoAniSkip = playerPreferences.autoSkipAniSkip()
- val enableNetflixAniSkip = playerPreferences.enableNetflixStyleAniSkip()
- val waitingTimeAniSkip = playerPreferences.waitingTimeAniSkip()
-
- val isAniSkipEnabled by enableAniSkip.collectAsState()
-
- return Preference.PreferenceGroup(
- title = stringResource(R.string.pref_category_player_aniskip),
- preferenceItems = listOf(
Preference.PreferenceItem.SwitchPreference(
pref = enableAniSkip,
title = stringResource(R.string.pref_enable_aniskip),
@@ -249,6 +262,36 @@ object SettingsPlayerScreen : SearchableSettings {
9 to stringResource(R.string.pref_waiting_time_aniskip_9),
10 to stringResource(R.string.pref_waiting_time_aniskip_10),
),
+ enabled = isAniSkipEnabled,
+ ),
+ ),
+ )
+ }
+
+ @Composable
+ private fun getPipGroup(playerPreferences: PlayerPreferences): Preference.PreferenceGroup {
+ val enablePip = playerPreferences.enablePip()
+ val pipEpisodeToasts = playerPreferences.pipEpisodeToasts()
+ val pipOnExit = playerPreferences.pipOnExit()
+
+ val isPipEnabled by enablePip.collectAsState()
+
+ return Preference.PreferenceGroup(
+ title = stringResource(R.string.pref_category_pip),
+ preferenceItems = listOf(
+ Preference.PreferenceItem.SwitchPreference(
+ pref = enablePip,
+ title = stringResource(R.string.pref_enable_pip),
+ ),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = pipEpisodeToasts,
+ title = stringResource(R.string.pref_pip_episode_toasts),
+ enabled = isPipEnabled,
+ ),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = pipOnExit,
+ title = stringResource(R.string.pref_pip_on_exit),
+ enabled = isPipEnabled,
),
),
)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadCache.kt
index 0678c3447..04b1bdaf2 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadCache.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadCache.kt
@@ -101,9 +101,23 @@ class AnimeDownloadCache(
episodeScanlator: String?,
animeTitle: String,
sourceId: Long,
+ skipCache: Boolean,
): Boolean {
- val source = sourceManager.getOrStub(sourceId)
- return provider.findEpisodeDir(episodeName, episodeScanlator, animeTitle, source) != null
+ if (skipCache) {
+ val source = sourceManager.getOrStub(sourceId)
+ return provider.findEpisodeDir(episodeName, episodeScanlator, animeTitle, source) != null
+ }
+
+ renewCache()
+
+ val sourceDir = rootDownloadsDir.sourceDirs[sourceId]
+ if (sourceDir != null) {
+ val animeDir = sourceDir.animeDirs[provider.getAnimeDirName(animeTitle)]
+ if (animeDir != null) {
+ return provider.getValidEpisodeDirNames(episodeName, episodeScanlator).any { it in animeDir.episodeDirs }
+ }
+ }
+ return false
}
/**
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt
index 82df9d5cd..d0406243d 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt
@@ -202,14 +202,16 @@ class AnimeDownloadManager(
* @param episodeScanlator scanlator of the episode to query
* @param animeTitle the title of the anime to query.
* @param sourceId the id of the source of the episode.
+ * @param skipCache whether to skip the directory cache and check in the filesystem.
*/
fun isEpisodeDownloaded(
episodeName: String,
episodeScanlator: String?,
animeTitle: String,
sourceId: Long,
+ skipCache: Boolean = false,
): Boolean {
- return cache.isEpisodeDownloaded(episodeName, episodeScanlator, animeTitle, sourceId)
+ return cache.isEpisodeDownloaded(episodeName, episodeScanlator, animeTitle, sourceId, skipCache)
}
/**
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt
index eba90a080..812b58d11 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt
@@ -92,10 +92,11 @@ internal class AnimeDownloadNotifier(private val context: Context) {
)
}
- val downloadingProgressText = context.getString(
- R.string.episode_downloading_progress,
- download.progress,
- )
+ val downloadingProgressText = if (download.totalProgress == 0) {
+ context.getString(R.string.update_check_notification_download_in_progress)
+ } else {
+ context.getString(R.string.episode_downloading_progress, download.progress)
+ }
if (preferences.hideNotificationContent().get()) {
setContentTitle(downloadingProgressText)
@@ -103,12 +104,15 @@ internal class AnimeDownloadNotifier(private val context: Context) {
} else {
val title = download.anime.title.chop(15)
val quotedTitle = Pattern.quote(title)
- val chapter = download.episode.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
- setContentTitle("$title - $chapter".chop(30))
+ val episode = download.episode.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
+ setContentTitle("$title - $episode".chop(30))
setContentText(downloadingProgressText)
}
-
- setProgress(100, download.progress, false)
+ if (download.totalProgress == 0) {
+ setProgress(100, download.progress, true)
+ } else {
+ setProgress(100, download.progress, false)
+ }
setOngoing(true)
show(Notifications.ID_DOWNLOAD_EPISODE_PROGRESS)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt
index 897835805..4f8c04f97 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/BrowseTab.kt
@@ -64,10 +64,10 @@ data class BrowseTab(
migrateMangaSourceTab(),
),
startIndex = 2.takeIf { toExtensions },
- searchQuery = mangaExtensionsQuery,
- onChangeSearchQuery = mangaExtensionsScreenModel::search,
- searchQueryAnime = animeExtensionsQuery,
- onChangeSearchQueryAnime = animeExtensionsScreenModel::search,
+ mangaSearchQuery = mangaExtensionsQuery,
+ onChangeMangaSearchQuery = mangaExtensionsScreenModel::search,
+ animeSearchQuery = animeExtensionsQuery,
+ onChangeAnimeSearchQuery = animeExtensionsScreenModel::search,
scrollable = true,
)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadHolder.kt
index 1fdc643c8..40e771595 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/anime/AnimeDownloadHolder.kt
@@ -60,14 +60,23 @@ class AnimeDownloadHolder(private val view: View, val adapter: AnimeDownloadAdap
if (binding.downloadProgress.max == 1) {
binding.downloadProgress.max = 100
}
- binding.downloadProgress.setProgressCompat(download.totalProgress, true)
+ if (download.totalProgress == 0) {
+ binding.downloadProgress.isIndeterminate = true
+ } else {
+ binding.downloadProgress.isIndeterminate = false
+ binding.downloadProgress.setProgressCompat(download.totalProgress, true)
+ }
}
/**
* Updates the text field of the number of downloaded pages.
*/
fun notifyDownloadedPages() {
- binding.downloadProgressText.text = view.context.getString(R.string.episode_download_progress, download.progress)
+ binding.downloadProgressText.text = if (download.totalProgress == 0) {
+ view.context.getString(R.string.update_check_notification_download_in_progress)
+ } else {
+ view.context.getString(R.string.episode_download_progress, download.progress)
+ }
}
override fun onItemReleased(position: Int) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoriesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoriesTab.kt
index 44929cad1..18cef597d 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoriesTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoriesTab.kt
@@ -5,6 +5,8 @@ import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import cafe.adriel.voyager.core.model.rememberScreenModel
@@ -47,9 +49,11 @@ data class HistoriesTab(
override fun Content() {
val context = LocalContext.current
// Hoisted for history tab's search bar
- val historyScreenModel = rememberScreenModel { MangaHistoryScreenModel() }
+ val mangaHistoryScreenModel = rememberScreenModel { MangaHistoryScreenModel() }
+ val mangaSearchQuery by mangaHistoryScreenModel.query.collectAsState()
val animeHistoryScreenModel = rememberScreenModel { AnimeHistoryScreenModel() }
+ val animeSearchQuery by animeHistoryScreenModel.query.collectAsState()
TabbedScreen(
titleRes = R.string.label_recent_manga,
@@ -57,10 +61,10 @@ data class HistoriesTab(
animeHistoryTab(context, fromMore),
mangaHistoryTab(context, fromMore),
),
- searchQuery = historyScreenModel.getSearchQuery,
- onChangeSearchQuery = historyScreenModel::updateSearchQuery,
- searchQueryAnime = animeHistoryScreenModel.getSearchQuery,
- onChangeSearchQueryAnime = animeHistoryScreenModel::updateSearchQuery,
+ mangaSearchQuery = mangaSearchQuery,
+ onChangeMangaSearchQuery = mangaHistoryScreenModel::search,
+ animeSearchQuery = animeSearchQuery,
+ onChangeAnimeSearchQuery = animeHistoryScreenModel::search,
)
LaunchedEffect(Unit) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt
index 3ec7ce80e..f1b7d24c2 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryScreenModel.kt
@@ -17,9 +17,12 @@ import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
@@ -39,21 +42,28 @@ class AnimeHistoryScreenModel(
private val _events: Channel = Channel(Channel.UNLIMITED)
val events: Flow = _events.receiveAsFlow()
+ private val _query: MutableStateFlow = MutableStateFlow(null)
+ val query: StateFlow = _query.asStateFlow()
+
init {
coroutineScope.launch {
- state.map { it.searchQuery }
- .distinctUntilChanged()
- .flatMapLatest { query ->
- getHistory.subscribe(query ?: "")
- .distinctUntilChanged()
- .catch { error ->
- logcat(LogPriority.ERROR, error)
- _events.send(Event.InternalError)
- }
- .map { it.toAnimeHistoryUiModels() }
- .flowOn(Dispatchers.IO)
- }
- .collect { newList -> mutableState.update { it.copy(list = newList) } }
+ _query.collectLatest { query ->
+ getHistory.subscribe(query ?: "")
+ .distinctUntilChanged()
+ .catch { error ->
+ logcat(LogPriority.ERROR, error)
+ _events.send(Event.InternalError)
+ }
+ .map { it.toAnimeHistoryUiModels() }
+ .flowOn(Dispatchers.IO)
+ .collect { newList -> mutableState.update { it.copy(list = newList) } }
+ }
+ }
+ }
+
+ fun search(query: String?) {
+ coroutineScope.launchIO {
+ _query.emit(query)
}
}
@@ -105,12 +115,6 @@ class AnimeHistoryScreenModel(
}
}
- val getSearchQuery = mutableState.value.searchQuery
-
- fun updateSearchQuery(query: String?) {
- mutableState.update { it.copy(searchQuery = query) }
- }
-
fun setDialog(dialog: Dialog?) {
mutableState.update { it.copy(dialog = dialog) }
}
@@ -129,7 +133,6 @@ class AnimeHistoryScreenModel(
@Immutable
data class AnimeHistoryState(
- val searchQuery: String? = null,
val list: List? = null,
val dialog: AnimeHistoryScreenModel.Dialog? = null,
)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryTab.kt
index 92b393fdd..b52d8cc41 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/anime/AnimeHistoryTab.kt
@@ -42,6 +42,7 @@ fun Screen.animeHistoryTab(
val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { AnimeHistoryScreenModel() }
val state by screenModel.state.collectAsState()
+ val searchQuery by screenModel.query.collectAsState()
suspend fun openEpisode(context: Context, episode: Episode?) {
val playerPreferences: PlayerPreferences by injectLazy()
@@ -67,6 +68,7 @@ fun Screen.animeHistoryTab(
AnimeHistoryScreen(
state = state,
contentPadding = contentPadding,
+ searchQuery = searchQuery,
snackbarHostState = snackbarHostState,
onClickCover = { navigator.push(AnimeScreen(it)) },
onClickResume = screenModel::getNextEpisodeForAnime,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt
index f7b6c5864..1b052249c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryScreenModel.kt
@@ -17,9 +17,12 @@ import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
@@ -39,21 +42,28 @@ class MangaHistoryScreenModel(
private val _events: Channel = Channel(Channel.UNLIMITED)
val events: Flow = _events.receiveAsFlow()
+ private val _query: MutableStateFlow = MutableStateFlow(null)
+ val query: StateFlow = _query.asStateFlow()
+
init {
coroutineScope.launch {
- state.map { it.searchQuery }
- .distinctUntilChanged()
- .flatMapLatest { query ->
- getHistory.subscribe(query ?: "")
- .distinctUntilChanged()
- .catch { error ->
- logcat(LogPriority.ERROR, error)
- _events.send(Event.InternalError)
- }
- .map { it.toHistoryUiModels() }
- .flowOn(Dispatchers.IO)
- }
- .collect { newList -> mutableState.update { it.copy(list = newList) } }
+ _query.collectLatest { query ->
+ getHistory.subscribe(query ?: "")
+ .distinctUntilChanged()
+ .catch { error ->
+ logcat(LogPriority.ERROR, error)
+ _events.send(Event.InternalError)
+ }
+ .map { it.toHistoryUiModels() }
+ .flowOn(Dispatchers.IO)
+ .collect { newList -> mutableState.update { it.copy(list = newList) } }
+ }
+ }
+ }
+
+ fun search(query: String?) {
+ coroutineScope.launchIO {
+ _query.emit(query)
}
}
@@ -105,12 +115,6 @@ class MangaHistoryScreenModel(
}
}
- val getSearchQuery = mutableState.value.searchQuery
-
- fun updateSearchQuery(query: String?) {
- mutableState.update { it.copy(searchQuery = query) }
- }
-
fun setDialog(dialog: Dialog?) {
mutableState.update { it.copy(dialog = dialog) }
}
@@ -129,7 +133,6 @@ class MangaHistoryScreenModel(
@Immutable
data class HistoryState(
- val searchQuery: String? = null,
val list: List? = null,
val dialog: MangaHistoryScreenModel.Dialog? = null,
)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryTab.kt
index 8eef8e152..5aaddbeda 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/manga/MangaHistoryTab.kt
@@ -39,6 +39,7 @@ fun Screen.mangaHistoryTab(
val navigator = LocalNavigator.currentOrThrow
val screenModel = rememberScreenModel { MangaHistoryScreenModel() }
val state by screenModel.state.collectAsState()
+ val searchQuery by screenModel.query.collectAsState()
suspend fun openChapter(context: Context, chapter: Chapter?) {
if (chapter != null) {
@@ -58,6 +59,7 @@ fun Screen.mangaHistoryTab(
MangaHistoryScreen(
state = state,
contentPadding = contentPadding,
+ searchQuery = searchQuery,
snackbarHostState = snackbarHostState,
onClickCover = { navigator.push(MangaScreen(it)) },
onClickResume = screenModel::getNextChapterForManga,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibrarySettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibrarySettingsSheet.kt
index 51595fb5d..6d72b3faa 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibrarySettingsSheet.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibrarySettingsSheet.kt
@@ -353,7 +353,7 @@ class MangaLibrarySettingsSheet(
inner class BadgeGroup : Group {
private val downloadBadge = Item.CheckboxGroup(R.string.action_display_download_badge, this)
private val unreadBadge = Item.CheckboxGroup(R.string.action_display_unread_badge, this)
- private val localBadge = Item.CheckboxGroup(R.string.action_display_local_badge, this)
+ private val localBadge = Item.CheckboxGroup(R.string.action_display_local_badge_manga, this)
private val languageBadge = Item.CheckboxGroup(R.string.action_display_language_badge, this)
override val header = Item.Header(R.string.badges_header)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkAnimeActivity.kt
similarity index 91%
rename from app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkActivity.kt
rename to app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkAnimeActivity.kt
index 1e381c09a..d9e7e4798 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkAnimeActivity.kt
@@ -4,7 +4,7 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
-class DeepLinkActivity : Activity() {
+class DeepLinkAnimeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkMangaActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkMangaActivity.kt
new file mode 100644
index 000000000..db2dc12c2
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/DeepLinkMangaActivity.kt
@@ -0,0 +1,19 @@
+package eu.kanade.tachiyomi.ui.main
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+
+class DeepLinkMangaActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ intent.apply {
+ flags = flags or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
+ setClass(applicationContext, MainActivity::class.java)
+ }
+ startActivity(intent)
+ finish()
+ }
+}
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 de85c66c2..68f0a2ed8 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
@@ -76,6 +76,7 @@ import eu.kanade.tachiyomi.data.updater.AppUpdateResult
import eu.kanade.tachiyomi.data.updater.RELEASE_URL
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.browse.anime.source.browse.BrowseAnimeSourceScreen
+import eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch.GlobalAnimeSearchScreen
import eu.kanade.tachiyomi.ui.browse.manga.source.browse.BrowseMangaSourceScreen
import eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch.GlobalMangaSearchScreen
import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreen
@@ -492,7 +493,7 @@ class MainActivity : BaseActivity() {
if (query != null && query.isNotEmpty()) {
val filter = intent.getStringExtra(INTENT_SEARCH_FILTER) ?: ""
navigator.popUntilRoot()
- navigator.push(GlobalMangaSearchScreen(query, filter))
+ navigator.push(GlobalAnimeSearchScreen(query, filter))
}
}
else -> {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/player/EpisodeLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/player/EpisodeLoader.kt
index 6d471507a..9136a171f 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/player/EpisodeLoader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/player/EpisodeLoader.kt
@@ -22,7 +22,7 @@ class EpisodeLoader {
fun getLinks(episode: Episode, anime: Anime, source: AnimeSource): Observable> {
val downloadManager: AnimeDownloadManager = Injekt.get()
- val isDownloaded = downloadManager.isEpisodeDownloaded(episode.name, episode.scanlator, anime.title, anime.source)
+ val isDownloaded = downloadManager.isEpisodeDownloaded(episode.name, episode.scanlator, anime.title, anime.source, skipCache = true)
return when {
isDownloaded -> isDownloaded(episode, anime, source, downloadManager)
source is AnimeHttpSource -> isHttp(episode, source)
@@ -33,7 +33,7 @@ class EpisodeLoader {
fun isDownloaded(episode: Episode, anime: Anime): Boolean {
val downloadManager: AnimeDownloadManager = Injekt.get()
- return downloadManager.isEpisodeDownloaded(episode.name, episode.scanlator, anime.title, anime.source)
+ return downloadManager.isEpisodeDownloaded(episode.name, episode.scanlator, anime.title, anime.source, skipCache = true)
}
fun getLink(episode: Episode, anime: Anime, source: AnimeSource): Observable