From 4882896f4d65acabb82771699906b797b3de7662 Mon Sep 17 00:00:00 2001 From: Semen <55978818+semenvav@users.noreply.github.com> Date: Fri, 7 Jul 2023 16:57:02 +0300 Subject: [PATCH 01/25] Add function to delete downloaded chapters when migrating manga (#9621) add function to delete downloaded chapters when migrating manga and getFlagsFromPositions fix --- .../ui/browse/migration/MigrationFlags.kt | 31 ++++++++++++++----- .../browse/migration/search/MigrateDialog.kt | 10 ++++++ i18n/src/main/res/values/strings.xml | 1 + 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt index 79e342030..f0643d81c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/MigrationFlags.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.browse.migration import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.data.download.DownloadCache import kotlinx.coroutines.runBlocking import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.track.interactor.GetTracks @@ -12,15 +13,18 @@ import uy.kohesive.injekt.injectLazy object MigrationFlags { - private const val CHAPTERS = 0b0001 - private const val CATEGORIES = 0b0010 - private const val TRACK = 0b0100 - private const val CUSTOM_COVER = 0b1000 + private const val CHAPTERS = 0b00001 + private const val CATEGORIES = 0b00010 + private const val TRACK = 0b00100 + private const val CUSTOM_COVER = 0b01000 + private const val DELETE_DOWNLOADED = 0b10000 private val coverCache: CoverCache by injectLazy() private val getTracks: GetTracks = Injekt.get() + private val downloadCache: DownloadCache by injectLazy() - val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK, CUSTOM_COVER) + val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK, CUSTOM_COVER, DELETE_DOWNLOADED) + private var enableFlags = emptyList().toMutableList() fun hasChapters(value: Int): Boolean { return value and CHAPTERS != 0 @@ -38,23 +42,36 @@ object MigrationFlags { return value and CUSTOM_COVER != 0 } + fun hasDeleteDownloaded(value: Int): Boolean { + return value and DELETE_DOWNLOADED != 0 + } + fun getEnabledFlagsPositions(value: Int): List { return flags.mapIndexedNotNull { index, flag -> if (value and flag != 0) index else null } } fun getFlagsFromPositions(positions: Array): Int { - return positions.fold(0) { accumulated, position -> accumulated or (1 shl position) } + val fold = positions.fold(0) { accumulated, position -> accumulated or enableFlags[position] } + enableFlags.clear() + return fold } fun titles(manga: Manga?): Array { + enableFlags.add(CHAPTERS) + enableFlags.add(CATEGORIES) val titles = arrayOf(R.string.chapters, R.string.categories).toMutableList() if (manga != null) { if (runBlocking { getTracks.await(manga.id) }.isNotEmpty()) { titles.add(R.string.track) + enableFlags.add(TRACK) } - if (manga.hasCustomCover(coverCache)) { titles.add(R.string.custom_cover) + enableFlags.add(CUSTOM_COVER) + } + if (downloadCache.getDownloadCount(manga) > 0) { + titles.add(R.string.delete_downloaded) + enableFlags.add(DELETE_DOWNLOADED) } } return titles.toTypedArray() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt index c3358a88b..496a40c58 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt @@ -34,6 +34,7 @@ import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.toSManga import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.Source @@ -161,6 +162,7 @@ internal fun MigrateDialog( internal class MigrateDialogScreenModel( private val sourceManager: SourceManager = Injekt.get(), + private val downloadManager: DownloadManager = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(), @@ -219,6 +221,7 @@ internal class MigrateDialogScreenModel( val migrateCategories = MigrationFlags.hasCategories(flags) val migrateTracks = MigrationFlags.hasTracks(flags) val migrateCustomCover = MigrationFlags.hasCustomCover(flags) + val deleteDownloaded = MigrationFlags.hasDeleteDownloaded(flags) try { syncChaptersWithSource.await(sourceChapters, newManga, newSource) @@ -283,6 +286,13 @@ internal class MigrateDialogScreenModel( insertTrack.awaitAll(tracks) } + // Delete downloaded + if (deleteDownloaded) { + if (oldSource != null) { + downloadManager.deleteManga(oldManga, oldSource) + } + } + if (replace) { updateManga.await(MangaUpdate(oldManga.id, favorite = false, dateAdded = 0)) } diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 03783484e..e03781151 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Library entries Chapters Tracking + Delete downloaded History From 53c6230afebe8e8cddca216f281fdec70e450a33 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 7 Jul 2023 17:46:39 -0400 Subject: [PATCH 02/25] Change auto clear cache to occur on app launch instead Fixes #9564 Avoids the issue of clearing the cache when the app is backgrounded despite being in the reader. We could do a job on idle, but we'd still need to be careful around whether the reader is active, so this is just simpler considering it's a separate activity. --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 8 -------- .../kanade/tachiyomi/ui/main/MainActivity.kt | 18 +++++++++++++----- i18n/src/main/res/values/strings.xml | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index dbb6ed6a5..822c66b81 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -28,7 +28,6 @@ import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.tachiyomi.crash.CrashActivity import eu.kanade.tachiyomi.crash.GlobalExceptionHandler -import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer import eu.kanade.tachiyomi.data.coil.MangaKeyer @@ -54,7 +53,6 @@ import org.acra.ktx.initAcra import org.acra.sender.HttpSender import org.conscrypt.Conscrypt import tachiyomi.core.util.system.logcat -import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.presentation.widget.TachiyomiWidgetManager import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -64,11 +62,9 @@ import java.security.Security class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { private val basePreferences: BasePreferences by injectLazy() - private val libraryPreferences: LibraryPreferences by injectLazy() private val networkPreferences: NetworkPreferences by injectLazy() private val disableIncognitoReceiver = DisableIncognitoReceiver() - private val chapterCache: ChapterCache by injectLazy() @SuppressLint("LaunchActivityFromNotification") override fun onCreate() { @@ -172,10 +168,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { override fun onStop(owner: LifecycleOwner) { SecureActivityDelegate.onApplicationStopped() - - if (libraryPreferences.autoClearChapterCache().get()) { - chapterCache.clear() - } } override fun getPackageName(): String { 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 d92a60e74..4612eec34 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 @@ -63,6 +63,7 @@ import eu.kanade.presentation.util.collectAsState import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.updater.AppUpdateChecker @@ -105,6 +106,7 @@ class MainActivity : BaseActivity() { private val preferences: BasePreferences by injectLazy() private val downloadCache: DownloadCache by injectLazy() + private val chapterCache: ChapterCache by injectLazy() // To be checked by splash screen. If true then splash screen will be removed. var ready = false @@ -112,12 +114,14 @@ class MainActivity : BaseActivity() { private var navigator: Navigator? = null override fun onCreate(savedInstanceState: Bundle?) { + val isLaunch = savedInstanceState == null + // Prevent splash screen showing up on configuration changes - val splashScreen = if (savedInstanceState == null) installSplashScreen() else null + val splashScreen = if (isLaunch) installSplashScreen() else null super.onCreate(savedInstanceState) - val didMigration = if (savedInstanceState == null) { + val didMigration = if (isLaunch) { Migrations.upgrade( context = applicationContext, basePreferences = preferences, @@ -149,7 +153,7 @@ class MainActivity : BaseActivity() { val downloadOnly by preferences.downloadedOnly().collectAsState() val indexing by downloadCache.isInitializing.collectAsState() - // Set statusbar color considering the top app state banner + // Set status bar color considering the top app state banner val systemUiController = rememberSystemUiController() val isSystemInDarkTheme = isSystemInDarkTheme() val statusBarBackgroundColor = when { @@ -189,7 +193,7 @@ class MainActivity : BaseActivity() { LaunchedEffect(navigator) { this@MainActivity.navigator = navigator - if (savedInstanceState == null) { + if (isLaunch) { // Set start screen handleIntentAction(intent, navigator) @@ -267,6 +271,10 @@ class MainActivity : BaseActivity() { elapsed <= SPLASH_MIN_DURATION || (!ready && elapsed <= SPLASH_MAX_DURATION) } setSplashScreenExitAnimation(splashScreen) + + if (isLaunch && libraryPreferences.autoClearChapterCache().get()) { + chapterCache.clear() + } } override fun onProvideAssistContent(outContent: AssistContent) { @@ -279,7 +287,7 @@ class MainActivity : BaseActivity() { } @Composable - fun HandleOnNewIntent(context: Context, navigator: Navigator) { + private fun HandleOnNewIntent(context: Context, navigator: Navigator) { LaunchedEffect(Unit) { callbackFlow { val componentActivity = context as ComponentActivity diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index e03781151..06c68f233 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -538,7 +538,7 @@ Used: %1$s Cache cleared. %1$d files have been deleted Error occurred while clearing - Clear chapter cache on app close + Clear chapter cache on app launch Invalidate downloads index Force app to recheck downloaded chapters Clear database From cf3f2d0380e5ab70211e6aab3f45bc81da43fcf4 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 7 Jul 2023 17:57:29 -0400 Subject: [PATCH 03/25] Adjust manga FAB to only say "Start" if there's no unread chapters in unfiltered list Closes #9479 --- .../java/eu/kanade/presentation/manga/MangaScreen.kt | 8 ++++---- .../eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 74d89ebc2..c0c2b3f6a 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -258,7 +258,7 @@ private fun MangaScreenSmallImpl( ) { val chapterListState = rememberLazyListState() - val chapters = remember(state) { state.processedChapters.toList() } + val chapters = remember(state) { state.processedChapters } val internalOnBackPressed = { if (chapters.fastAny { it.selected }) { @@ -320,7 +320,7 @@ private fun MangaScreenSmallImpl( ) { ExtendedFloatingActionButton( text = { - val id = if (chapters.fastAny { it.chapter.read }) { + val id = if (state.chapters.fastAny { it.chapter.read }) { R.string.action_resume } else { R.string.action_start @@ -485,7 +485,7 @@ fun MangaScreenLargeImpl( val layoutDirection = LocalLayoutDirection.current val density = LocalDensity.current - val chapters = remember(state) { state.processedChapters.toList() } + val chapters = remember(state) { state.processedChapters } val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues() var topBarHeight by remember { mutableIntStateOf(0) } @@ -555,7 +555,7 @@ fun MangaScreenLargeImpl( ) { ExtendedFloatingActionButton( text = { - val id = if (chapters.fastAny { it.chapter.read }) { + val id = if (state.chapters.fastAny { it.chapter.read }) { R.string.action_resume } else { R.string.action_start diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index a731e48a5..98e4338d3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -119,7 +119,7 @@ class MangaInfoScreenModel( private val allChapters: List? get() = successState?.chapters - private val filteredChapters: Sequence? + private val filteredChapters: List? get() = successState?.processedChapters val chapterSwipeEndAction = libraryPreferences.swipeEndAction().get() @@ -576,7 +576,7 @@ class MangaInfoScreenModel( } private fun getUnreadChapters(): List { - val chapterItems = if (skipFiltered) filteredChapters.orEmpty().toList() else allChapters.orEmpty() + val chapterItems = if (skipFiltered) filteredChapters.orEmpty() else allChapters.orEmpty() return chapterItems .filter { (chapter, dlStatus) -> !chapter.read && dlStatus == Download.State.NOT_DOWNLOADED } .map { it.chapter } @@ -664,7 +664,7 @@ class MangaInfoScreenModel( fun markPreviousChapterRead(pointer: Chapter) { val successState = successState ?: return - val chapters = filteredChapters.orEmpty().map { it.chapter }.toList() + val chapters = filteredChapters.orEmpty().map { it.chapter } val prevChapters = if (successState.manga.sortDescending()) chapters.asReversed() else chapters val pointerPos = prevChapters.indexOf(pointer) if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true) @@ -987,8 +987,9 @@ sealed class MangaScreenState { val hasPromptedToAddBefore: Boolean = false, ) : MangaScreenState() { - val processedChapters: Sequence - get() = chapters.applyFilters(manga) + val processedChapters by lazy { + chapters.applyFilters(manga).toList() + } val trackingAvailable: Boolean get() = trackItems.isNotEmpty() From d32409bd6e68a3d5e32a69eb1b3afbb407741a00 Mon Sep 17 00:00:00 2001 From: arkon Date: Fri, 7 Jul 2023 19:58:53 -0400 Subject: [PATCH 04/25] Fix up icon direction when RTL --- .../kanade/presentation/components/AppBar.kt | 22 ++++++++++++++----- .../manga/components/MangaToolbar.kt | 8 ++----- .../more/settings/PreferenceScaffold.kt | 10 ++------- .../settings/screen/SettingsMainScreen.kt | 8 ++----- .../settings/screen/SettingsSearchScreen.kt | 8 ++----- .../details/SourcePreferencesScreen.kt | 10 ++------- 6 files changed, 26 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt index e2ca263c2..1acd220fc 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.TextFieldDefaults import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.ArrowBack +import androidx.compose.material.icons.outlined.ArrowForward import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.MoreVert import androidx.compose.material.icons.outlined.Search @@ -38,12 +39,14 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import eu.kanade.tachiyomi.R @@ -62,7 +65,7 @@ fun AppBar( subtitle: String? = null, // Up button navigateUp: (() -> Unit)? = null, - navigationIcon: ImageVector = Icons.Outlined.ArrowBack, + navigationIcon: ImageVector? = null, // Menu actions: @Composable RowScope.() -> Unit = {}, // Action mode @@ -107,7 +110,7 @@ fun AppBar( titleContent: @Composable () -> Unit, // Up button navigateUp: (() -> Unit)? = null, - navigationIcon: ImageVector = Icons.Outlined.ArrowBack, + navigationIcon: ImageVector? = null, // Menu actions: @Composable RowScope.() -> Unit = {}, // Action mode @@ -131,10 +134,7 @@ fun AppBar( } else { navigateUp?.let { IconButton(onClick = it) { - Icon( - imageVector = navigationIcon, - contentDescription = stringResource(R.string.abc_action_bar_up_description), - ) + UpIcon(navigationIcon) } } } @@ -360,6 +360,16 @@ fun SearchToolbar( ) } +@Composable +fun UpIcon(navigationIcon: ImageVector? = null) { + val icon = navigationIcon + ?: if (LocalLayoutDirection.current == LayoutDirection.Ltr) Icons.Outlined.ArrowBack else Icons.Outlined.ArrowForward + Icon( + imageVector = icon, + contentDescription = stringResource(R.string.abc_action_bar_up_description), + ) +} + sealed interface AppBar { sealed interface AppBarAction diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt index 533a36bb2..b99d58b58 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaToolbar.kt @@ -2,13 +2,11 @@ package eu.kanade.presentation.manga.components import androidx.compose.foundation.layout.Column import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Close import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.FilterList import androidx.compose.material.icons.outlined.FlipToBack import androidx.compose.material.icons.outlined.SelectAll -import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -29,6 +27,7 @@ import androidx.compose.ui.unit.dp import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.DownloadDropdownMenu +import eu.kanade.presentation.components.UpIcon import eu.kanade.presentation.manga.DownloadAction import eu.kanade.tachiyomi.R import tachiyomi.presentation.core.theme.active @@ -67,10 +66,7 @@ fun MangaToolbar( }, navigationIcon = { IconButton(onClick = onBackClicked) { - Icon( - imageVector = if (isActionMode) Icons.Outlined.Close else Icons.Outlined.ArrowBack, - contentDescription = stringResource(R.string.abc_action_bar_up_description), - ) + UpIcon(Icons.Outlined.Close.takeIf { isActionMode }) } }, actions = { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceScaffold.kt b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceScaffold.kt index 012e1f01d..fb9a3de2a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceScaffold.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/PreferenceScaffold.kt @@ -2,15 +2,12 @@ package eu.kanade.presentation.more.settings import androidx.annotation.StringRes import androidx.compose.foundation.layout.RowScope -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack -import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource -import eu.kanade.tachiyomi.R +import eu.kanade.presentation.components.UpIcon import tachiyomi.presentation.core.components.material.Scaffold @Composable @@ -27,10 +24,7 @@ fun PreferenceScaffold( navigationIcon = { if (onBackPressed != null) { IconButton(onClick = onBackPressed) { - Icon( - imageVector = Icons.Outlined.ArrowBack, - contentDescription = stringResource(R.string.abc_action_bar_up_description), - ) + UpIcon() } } }, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index c788640c7..5b094865b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.ChromeReaderMode import androidx.compose.material.icons.outlined.Code import androidx.compose.material.icons.outlined.CollectionsBookmark @@ -20,7 +19,6 @@ import androidx.compose.material.icons.outlined.Search import androidx.compose.material.icons.outlined.Security import androidx.compose.material.icons.outlined.SettingsBackupRestore import androidx.compose.material.icons.outlined.Sync -import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme @@ -45,6 +43,7 @@ import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions +import eu.kanade.presentation.components.UpIcon import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.Screen @@ -94,10 +93,7 @@ object SettingsMainScreen : Screen() { }, navigationIcon = { IconButton(onClick = backPress::invoke) { - Icon( - imageVector = Icons.Outlined.ArrowBack, - contentDescription = stringResource(R.string.abc_action_bar_up_description), - ) + UpIcon() } }, actions = { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt index cad51c044..fa8392140 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.material.icons.outlined.Close import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -49,6 +48,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.presentation.components.UpIcon import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R @@ -97,11 +97,7 @@ class SettingsSearchScreen : Screen() { val canPop = remember { navigator.canPop } if (canPop) { IconButton(onClick = navigator::pop) { - Icon( - imageVector = Icons.Outlined.ArrowBack, - contentDescription = stringResource(R.string.abc_action_bar_up_description), - tint = MaterialTheme.colorScheme.onSurfaceVariant, - ) + UpIcon() } } }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt index 3dab9f821..862f810c5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt @@ -7,9 +7,6 @@ import android.view.View import androidx.appcompat.view.ContextThemeWrapper import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.ArrowBack -import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar @@ -21,7 +18,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource import androidx.compose.ui.viewinterop.AndroidView import androidx.core.os.bundleOf import androidx.fragment.app.FragmentActivity @@ -38,6 +34,7 @@ import androidx.preference.forEach import androidx.preference.getOnBindEditTextListener import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.presentation.components.UpIcon import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore @@ -62,10 +59,7 @@ class SourcePreferencesScreen(val sourceId: Long) : Screen() { title = { Text(text = Injekt.get().getOrStub(sourceId).toString()) }, navigationIcon = { IconButton(onClick = navigator::pop) { - Icon( - imageVector = Icons.Outlined.ArrowBack, - contentDescription = stringResource(R.string.abc_action_bar_up_description), - ) + UpIcon() } }, scrollBehavior = it, From 8287c9d1935ac16980f9dd0ac4e0284775554a74 Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Sat, 8 Jul 2023 21:02:20 +0700 Subject: [PATCH 05/25] MangaChapterListItem: Replace swipe action method (#9682) Using swipe (the library) and added haptic feedback --- app/build.gradle.kts | 3 +- .../manga/components/MangaChapterListItem.kt | 412 ++++++++---------- gradle/libs.versions.toml | 2 + 3 files changed, 182 insertions(+), 235 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a08149ee5..d178945dd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi" - + versionCode = 103 versionName = "0.14.6" @@ -239,6 +239,7 @@ dependencies { implementation(libs.bundles.voyager) implementation(libs.compose.materialmotion) implementation(libs.compose.simpleicons) + implementation(libs.swipe) // Logging implementation(libs.logcat) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index b168e5ee0..63fdfecca 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -1,20 +1,12 @@ package eu.kanade.presentation.manga.components -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.sizeIn -import androidx.compose.material.DismissDirection -import androidx.compose.material.DismissValue -import androidx.compose.material.SwipeToDismiss import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.filled.Circle @@ -25,26 +17,28 @@ import androidx.compose.material.icons.outlined.Done import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.FileDownloadOff import androidx.compose.material.icons.outlined.RemoveDone -import androidx.compose.material.rememberDismissState import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text -import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.platform.ViewConfiguration import androidx.compose.ui.res.stringResource @@ -53,10 +47,13 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.model.Download +import me.saket.swipe.SwipeableActionsBox +import me.saket.swipe.rememberSwipeableActionsState import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.presentation.core.components.material.ReadItemAlpha import tachiyomi.presentation.core.components.material.SecondaryItemAlpha import tachiyomi.presentation.core.util.selectedBackground +import kotlin.math.absoluteValue @Composable fun MangaChapterListItem( @@ -78,6 +75,12 @@ fun MangaChapterListItem( onDownloadClick: ((ChapterDownloadAction) -> Unit)?, onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, ) { + val haptic = LocalHapticFeedback.current + val density = LocalDensity.current + + val textAlpha = if (read) ReadItemAlpha else 1f + val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha + // Increase touch slop of swipe action to reduce accidental trigger val configuration = LocalViewConfiguration.current CompositionLocalProvider( @@ -85,247 +88,188 @@ fun MangaChapterListItem( override val touchSlop: Float = configuration.touchSlop * 3f }, ) { - val textAlpha = if (read) ReadItemAlpha else 1f - val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha - - val chapterSwipeStartEnabled = chapterSwipeStartAction != LibraryPreferences.ChapterSwipeAction.Disabled - val chapterSwipeEndEnabled = chapterSwipeEndAction != LibraryPreferences.ChapterSwipeAction.Disabled - - val dismissState = rememberDismissState() - val dismissDirections = remember { mutableSetOf() } - var lastDismissDirection: DismissDirection? by remember { mutableStateOf(null) } - if (lastDismissDirection == null) { - if (chapterSwipeStartEnabled) { - dismissDirections.add(DismissDirection.EndToStart) - } - if (chapterSwipeEndEnabled) { - dismissDirections.add(DismissDirection.StartToEnd) - } - } - val animateDismissContentAlpha by animateFloatAsState( - label = "animateDismissContentAlpha", - targetValue = if (lastDismissDirection != null) 1f else 0f, - animationSpec = tween(durationMillis = if (lastDismissDirection != null) 500 else 0), - finishedListener = { - lastDismissDirection = null - }, + val start = getSwipeAction( + action = chapterSwipeStartAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = MaterialTheme.colorScheme.primaryContainer, + onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, ) - val dismissContentAlpha = if (lastDismissDirection != null) animateDismissContentAlpha else 1f - val backgroundColor = if (chapterSwipeEndEnabled && (dismissState.dismissDirection == DismissDirection.StartToEnd || lastDismissDirection == DismissDirection.StartToEnd)) { - MaterialTheme.colorScheme.primary - } else if (chapterSwipeStartEnabled && (dismissState.dismissDirection == DismissDirection.EndToStart || lastDismissDirection == DismissDirection.EndToStart)) { - MaterialTheme.colorScheme.primary - } else { - Color.Unspecified + val end = getSwipeAction( + action = chapterSwipeEndAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = MaterialTheme.colorScheme.primaryContainer, + onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, + ) + + val swipeableActionsState = rememberSwipeableActionsState() + LaunchedEffect(Unit) { + // Haptic effect when swipe over threshold + val swipeActionThresholdPx = with(density) { swipeActionThreshold.toPx() } + snapshotFlow { swipeableActionsState.offset.value.absoluteValue > swipeActionThresholdPx } + .collect { if (it) haptic.performHapticFeedback(HapticFeedbackType.LongPress) } } - LaunchedEffect(dismissState.currentValue) { - when (dismissState.currentValue) { - DismissValue.DismissedToEnd -> { - lastDismissDirection = DismissDirection.StartToEnd - val dismissDirectionsCopy = dismissDirections.toSet() - dismissDirections.clear() - onChapterSwipe(chapterSwipeEndAction) - dismissState.snapTo(DismissValue.Default) - dismissDirections.addAll(dismissDirectionsCopy) - } - DismissValue.DismissedToStart -> { - lastDismissDirection = DismissDirection.EndToStart - val dismissDirectionsCopy = dismissDirections.toSet() - dismissDirections.clear() - onChapterSwipe(chapterSwipeStartAction) - dismissState.snapTo(DismissValue.Default) - dismissDirections.addAll(dismissDirectionsCopy) - } - DismissValue.Default -> { } - } - } - - SwipeToDismiss( - state = dismissState, - directions = dismissDirections, - background = { - Box( - modifier = Modifier - .fillMaxSize() - .background(backgroundColor), + SwipeableActionsBox( + modifier = Modifier.clipToBounds(), + state = swipeableActionsState, + startActions = listOfNotNull(start), + endActions = listOfNotNull(end), + swipeThreshold = swipeActionThreshold, + backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, + ) { + Row( + modifier = modifier + .selectedBackground(selected) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + ) + .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(6.dp), ) { - if (dismissState.dismissDirection in dismissDirections) { - val downloadState = downloadStateProvider() - SwipeBackgroundIcon( - modifier = Modifier - .padding(start = 16.dp) - .align(Alignment.CenterStart) - .alpha( - if (dismissState.dismissDirection == DismissDirection.StartToEnd) 1f else 0f, - ), - tint = contentColorFor(backgroundColor), - swipeAction = chapterSwipeEndAction, - read = read, - bookmark = bookmark, - downloadState = downloadState, - ) - SwipeBackgroundIcon( - modifier = Modifier - .padding(end = 16.dp) - .align(Alignment.CenterEnd) - .alpha( - if (dismissState.dismissDirection == DismissDirection.EndToStart) 1f else 0f, - ), - tint = contentColorFor(backgroundColor), - swipeAction = chapterSwipeStartAction, - read = read, - bookmark = bookmark, - downloadState = downloadState, - ) - } - } - }, - dismissContent = { - Row( - modifier = modifier - .background( - MaterialTheme.colorScheme.background.copy(dismissContentAlpha), - ) - .selectedBackground(selected) - .alpha(dismissContentAlpha) - .combinedClickable( - onClick = onClick, - onLongClick = onLongClick, - ) - .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), - ) { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(6.dp), + Row( + horizontalArrangement = Arrangement.spacedBy(2.dp), + verticalAlignment = Alignment.CenterVertically, ) { - Row( - horizontalArrangement = Arrangement.spacedBy(2.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - var textHeight by remember { mutableIntStateOf(0) } - if (!read) { - Icon( - imageVector = Icons.Filled.Circle, - contentDescription = stringResource(R.string.unread), - modifier = Modifier - .height(8.dp) - .padding(end = 4.dp), - tint = MaterialTheme.colorScheme.primary, - ) - } - if (bookmark) { - Icon( - imageVector = Icons.Filled.Bookmark, - contentDescription = stringResource(R.string.action_filter_bookmarked), - modifier = Modifier - .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), - tint = MaterialTheme.colorScheme.primary, - ) - } - Text( - text = title, - style = MaterialTheme.typography.bodyMedium, - color = LocalContentColor.current.copy(alpha = textAlpha), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - onTextLayout = { textHeight = it.size.height }, + var textHeight by remember { mutableIntStateOf(0) } + if (!read) { + Icon( + imageVector = Icons.Filled.Circle, + contentDescription = stringResource(R.string.unread), + modifier = Modifier + .height(8.dp) + .padding(end = 4.dp), + tint = MaterialTheme.colorScheme.primary, ) } + if (bookmark) { + Icon( + imageVector = Icons.Filled.Bookmark, + contentDescription = stringResource(R.string.action_filter_bookmarked), + modifier = Modifier + .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), + tint = MaterialTheme.colorScheme.primary, + ) + } + Text( + text = title, + style = MaterialTheme.typography.bodyMedium, + color = LocalContentColor.current.copy(alpha = textAlpha), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + onTextLayout = { textHeight = it.size.height }, + ) + } - Row { - ProvideTextStyle( - value = MaterialTheme.typography.bodyMedium.copy( - fontSize = 12.sp, - color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), - ), - ) { - if (date != null) { - Text( - text = date, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - if (readProgress != null || scanlator != null) DotSeparatorText() - } - if (readProgress != null) { - Text( - text = readProgress, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.alpha(ReadItemAlpha), - ) - if (scanlator != null) DotSeparatorText() - } - if (scanlator != null) { - Text( - text = scanlator, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } + Row { + ProvideTextStyle( + value = MaterialTheme.typography.bodyMedium.copy( + fontSize = 12.sp, + color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), + ), + ) { + if (date != null) { + Text( + text = date, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + if (readProgress != null || scanlator != null) DotSeparatorText() + } + if (readProgress != null) { + Text( + text = readProgress, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.alpha(ReadItemAlpha), + ) + if (scanlator != null) DotSeparatorText() + } + if (scanlator != null) { + Text( + text = scanlator, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) } } } - - if (onDownloadClick != null) { - ChapterDownloadIndicator( - enabled = downloadIndicatorEnabled, - modifier = Modifier.padding(start = 4.dp), - downloadStateProvider = downloadStateProvider, - downloadProgressProvider = downloadProgressProvider, - onClick = onDownloadClick, - ) - } } - }, - ) + + if (onDownloadClick != null) { + ChapterDownloadIndicator( + enabled = downloadIndicatorEnabled, + modifier = Modifier.padding(start = 4.dp), + downloadStateProvider = downloadStateProvider, + downloadProgressProvider = downloadProgressProvider, + onClick = onDownloadClick, + ) + } + } + } } } -@Composable -private fun SwipeBackgroundIcon( - modifier: Modifier = Modifier, - tint: Color, - swipeAction: LibraryPreferences.ChapterSwipeAction, +private fun getSwipeAction( + action: LibraryPreferences.ChapterSwipeAction, read: Boolean, bookmark: Boolean, downloadState: Download.State, -) { - val imageVector = when (swipeAction) { - LibraryPreferences.ChapterSwipeAction.ToggleRead -> { - if (!read) { - Icons.Outlined.Done - } else { - Icons.Outlined.RemoveDone - } - } - LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> { - if (!bookmark) { - Icons.Outlined.BookmarkAdd - } else { - Icons.Outlined.BookmarkRemove - } - } - LibraryPreferences.ChapterSwipeAction.Download -> { - when (downloadState) { - Download.State.NOT_DOWNLOADED, - Download.State.ERROR, - -> { Icons.Outlined.Download } - Download.State.QUEUE, - Download.State.DOWNLOADING, - -> { Icons.Outlined.FileDownloadOff } - Download.State.DOWNLOADED -> { Icons.Outlined.Delete } - } - } + background: Color, + onSwipe: () -> Unit, +): me.saket.swipe.SwipeAction? { + return when (action) { + LibraryPreferences.ChapterSwipeAction.ToggleRead -> SwipeAction( + icon = if (!read) Icons.Outlined.Done else Icons.Outlined.RemoveDone, + background = background, + isUndo = read, + onSwipe = onSwipe, + ) + LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> SwipeAction( + icon = if (!bookmark) Icons.Outlined.BookmarkAdd else Icons.Outlined.BookmarkRemove, + background = background, + isUndo = bookmark, + onSwipe = onSwipe, + ) + LibraryPreferences.ChapterSwipeAction.Download -> SwipeAction( + icon = when (downloadState) { + Download.State.NOT_DOWNLOADED, Download.State.ERROR -> Icons.Outlined.Download + Download.State.QUEUE, Download.State.DOWNLOADING -> Icons.Outlined.FileDownloadOff + Download.State.DOWNLOADED -> Icons.Outlined.Delete + }, + background = background, + onSwipe = onSwipe, + ) LibraryPreferences.ChapterSwipeAction.Disabled -> null } - imageVector?.let { - Icon( - modifier = modifier, - imageVector = imageVector, - tint = tint, - contentDescription = null, - ) - } } + +private fun SwipeAction( + onSwipe: () -> Unit, + icon: ImageVector, + background: Color, + isUndo: Boolean = false, +): me.saket.swipe.SwipeAction { + return me.saket.swipe.SwipeAction( + icon = { + Icon( + modifier = Modifier.padding(16.dp), + imageVector = icon, + tint = MaterialTheme.colorScheme.onSurface, + contentDescription = null, + ) + }, + background = background, + onSwipe = onSwipe, + isUndo = isUndo, + ) +} + +private val swipeActionThreshold = 56.dp diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b136821c4..c6675a008 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,6 +61,8 @@ insetter = "dev.chrisbanes.insetter:insetter:0.6.1" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.3" compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0" +swipe = "me.saket.swipe:swipe:1.2.0" + logcat = "com.squareup.logcat:logcat:0.1" acra-http = "ch.acra:acra-http:5.10.1" From db3343757770f8008151607f6731775b348cd6b7 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 10:06:55 -0400 Subject: [PATCH 06/25] Upgrade Okio --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c6675a008..f42bd035c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ flowreactivenetwork = "ru.beryukhov:flowreactivenetwork:1.0.4" okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_version" } okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" } okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" } -okio = "com.squareup.okio:okio:3.3.0" +okio = "com.squareup.okio:okio:3.4.0" conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2" From 67c6dbea0d25e82a38a501fa5d1366d1a64804cc Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Sat, 8 Jul 2023 21:55:47 +0200 Subject: [PATCH 07/25] Translations update from Hosted Weblate (#9671) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Weblate translations Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ca/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cs/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/cv/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/el/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/es/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fil/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/it/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ne/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sc/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/ Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/ Translation: Tachiyomi/Tachiyomi 0.x Co-authored-by: Ajeje Brazorf Co-authored-by: Alessandro Jean Co-authored-by: C201 Co-authored-by: DarKCroX Co-authored-by: Dexroneum Co-authored-by: Eduard Ereza Martínez Co-authored-by: Efe Devirgen Co-authored-by: FateXBlood Co-authored-by: Giorgio Sanna Co-authored-by: ID-86 Co-authored-by: Lyfja Co-authored-by: Lzmxya Co-authored-by: Nick Mariño Co-authored-by: Om Mishra Co-authored-by: Pitpe11 Co-authored-by: Swyter Co-authored-by: stevenlele --- i18n/src/main/res/values-ca/strings.xml | 1 + i18n/src/main/res/values-cs/strings.xml | 3 +- i18n/src/main/res/values-cv/strings.xml | 3 +- i18n/src/main/res/values-de/strings.xml | 5 +-- i18n/src/main/res/values-el/strings.xml | 3 +- i18n/src/main/res/values-es/strings.xml | 3 +- i18n/src/main/res/values-fil/strings.xml | 6 ++-- i18n/src/main/res/values-hi/strings.xml | 7 +++- i18n/src/main/res/values-it/strings.xml | 3 +- i18n/src/main/res/values-ms/strings.xml | 3 +- i18n/src/main/res/values-ne/strings.xml | 5 +-- i18n/src/main/res/values-pt-rBR/strings.xml | 3 +- i18n/src/main/res/values-ru/strings.xml | 3 +- i18n/src/main/res/values-sc/strings.xml | 36 +++++++++++++++++++-- i18n/src/main/res/values-tr/strings.xml | 1 + i18n/src/main/res/values-zh-rCN/strings.xml | 3 +- i18n/src/main/res/values-zh-rTW/strings.xml | 8 +++-- 17 files changed, 75 insertions(+), 21 deletions(-) diff --git a/i18n/src/main/res/values-ca/strings.xml b/i18n/src/main/res/values-ca/strings.xml index 4c142d399..8c4374b31 100644 --- a/i18n/src/main/res/values-ca/strings.xml +++ b/i18n/src/main/res/values-ca/strings.xml @@ -831,4 +831,5 @@ S’ha omès perquè no se n’espera cap publicació avui Període de comprovació superat Es recomana un període de gràcia baix per a minimitzar la sobrecàrrega de les fonts. Com més comprovacions fallides es produeixin, més interval hi haurà entre comprovacions, fins a un màxim de 28 dies. + Suprimeix els baixats \ No newline at end of file diff --git a/i18n/src/main/res/values-cs/strings.xml b/i18n/src/main/res/values-cs/strings.xml index ae32421e3..9dd14029b 100644 --- a/i18n/src/main/res/values-cs/strings.xml +++ b/i18n/src/main/res/values-cs/strings.xml @@ -639,7 +639,7 @@ Vypisovat podrobné informace do systémového protokolu (sníží výkon aplikace) Aktualizace aplikace Varování: velké aktualizace poškozují zdroje a můžou vést k pomalejším aktualizacím a zvýšenému využití baterie. Klepnutím se dozvíte více. - Vymazat mezipaměť kapitol při zavření aplikace + Vymazat mezipaměť kapitol při spuštění aplikace Chyba v získání seznamu rozšíření Zásady ochrany osobních údajů %1$d neknihovní záznamy v databázi @@ -849,4 +849,5 @@ Odebrat také z %s OK Odebrat sledování %s\? + Odstranit stažené \ No newline at end of file diff --git a/i18n/src/main/res/values-cv/strings.xml b/i18n/src/main/res/values-cv/strings.xml index 97f868376..37c0f11b6 100644 --- a/i18n/src/main/res/values-cv/strings.xml +++ b/i18n/src/main/res/values-cv/strings.xml @@ -565,7 +565,7 @@ Темӑ, кун тата вӑхӑт хармачӗ Пухмӑшсем, пӗтӗмӗшле ҫӗнетӳ Вулав тытӑмӗ, кӑтартӑнни, куҫӑм - Пӳрӳлле + Йаланхилле Ӑнсӑрт ҫырав уҫ Ҫӗр ҫырли тайккирийӗ Ҫур ҫӗр ӗнтрӗкӗ @@ -630,4 +630,5 @@ Версси Йӗрлеве хӑй-хальлӗн ҫӗнетни Пурне те ҫӗнет + Тийесе илнисене катерт \ No newline at end of file diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml index 1cb520d8b..32647105a 100644 --- a/i18n/src/main/res/values-de/strings.xml +++ b/i18n/src/main/res/values-de/strings.xml @@ -386,7 +386,7 @@ Zur Bibliothek hinzufügen Zum Beenden nochmal die Zurück-Taste drücken WebView ist für Tachiyomi erforderlich - Quelloffene Lizenzen + Open-Source-Lizenzen Webseite Nur Heruntergeladenes Kap. %1$s - %2$s @@ -628,7 +628,7 @@ Achtung: Große Downloads könnten dazu führen, dass Quellen langsamer werden und/oder Tachiyomi blockieren. Tippe, um mehr zu erfahren. Alle aktualisieren Anwendungsaktualisierungen - Kapitel-Zwischenspeicher beim Schließen der App löschen + Kapitel-Zwischenspeicher beim Öffnen der App löschen %1$d Nicht-Bibliothekseinträge in der Datenbank Nichts zu bereinigen Herunterladen der Erweiterungsliste ist fehlgeschlagen @@ -831,4 +831,5 @@ Auch aus %s entfernen OK Tracking von %s entfernen\? + Heruntergeladenes löschen \ No newline at end of file diff --git a/i18n/src/main/res/values-el/strings.xml b/i18n/src/main/res/values-el/strings.xml index 9c4a82acf..065a7da30 100644 --- a/i18n/src/main/res/values-el/strings.xml +++ b/i18n/src/main/res/values-el/strings.xml @@ -628,7 +628,7 @@ Προειδοποίηση: οι μαζικές λήψεις ενδέχεται να οδηγήσουν σε επιβράδυνση των πηγών ή/και αποκλεισμό του Tachiyomi. Πατήστε για να μάθετε περισσότερα. Ενημέρωση όλων Ενημερώσεις εφαρμογής - Εκκαθάριση της προσωρινής μνήμης κεφαλαίων στο κλείσιμο της εφαρμογής + Εκκαθάριση της προσωρινής μνήμης κεφαλαίων κατά την εκκίνηση της εφαρμογής %1$d καταχωρήσεις εκτός βιβλιοθήκης στη βάση δεδομένων Τίποτα προς εκκαθάριση Απέτυχε η λήψη λίστας επεκτάσεων @@ -831,4 +831,5 @@ Κατάργηση παρακολούθησης %s; Επίσης, αφαιρέστε από %s Αυτό θα καταργήσει την παρακολούθηση τοπικά. + Διαγραφή ληφθέντων \ No newline at end of file diff --git a/i18n/src/main/res/values-es/strings.xml b/i18n/src/main/res/values-es/strings.xml index e53a558ee..67baa6526 100644 --- a/i18n/src/main/res/values-es/strings.xml +++ b/i18n/src/main/res/values-es/strings.xml @@ -671,7 +671,7 @@ Advertencia: Las descargas grandes pueden llevar a que las fuentes se vuelvan cada vez más lentas y en casos extremos que los servidores limiten o impidan el acceso a Tachiyomi. Toca aquí para más información. Actualizar todas Actualizaciones de la aplicación - Borrar la caché de capítulos al cerrar la aplicación + Borrar la caché de capítulos al abrir la aplicación Base de datos limpia Hay %1$d elementos en la base de datos que no están en la biblioteca No se pudo descargar el listado de extensiones @@ -880,4 +880,5 @@ ¿Quitar el rastreo de %s\? Esto eliminará el seguimiento localmente. Quitar también de %s + Borrar los ya descargados \ No newline at end of file diff --git a/i18n/src/main/res/values-fil/strings.xml b/i18n/src/main/res/values-fil/strings.xml index f503cfb2e..1d11ae73d 100644 --- a/i18n/src/main/res/values-fil/strings.xml +++ b/i18n/src/main/res/values-fil/strings.xml @@ -64,7 +64,7 @@ Dami ng kabanata Pa-alpabeto Tanggalin ang pansala - Babasahin + Hindi Nabasa Tinandaan Pansala Menu @@ -801,10 +801,10 @@ Mag-swipe ng pakanang pagkilos Itakda ang pagitan Sinadyang takdang pagkuha - Nahuling 10+ pagsusuri + Huling 10+ pagsusuri Susunod na inaasahang update Nawala\? or Nahulog\? (depending on the context, \"Nahulog\" means dropped or dropped something, and \"Nawala\" means Gone/Vanished) Nahuling 20+ at 2 buwan - Lumipas ang check period + Lumipas na panahon ng pagsuri Kunin kada buwan (kada ika-28 na araw) pagkatapos ng %d araw diff --git a/i18n/src/main/res/values-hi/strings.xml b/i18n/src/main/res/values-hi/strings.xml index e8bc982e2..182a0bbc3 100644 --- a/i18n/src/main/res/values-hi/strings.xml +++ b/i18n/src/main/res/values-hi/strings.xml @@ -314,7 +314,7 @@ सिस्टम का पालन करें सूचनाओं का प्रबंधन सुरक्षा और गोपनीयता - अनलॉक की आवश्यकता है + खोलने की आवश्यकता है निष्क्रिय होने पर लॉक करें हमेशा कभी नहीँ @@ -739,4 +739,9 @@ फिट होने के लिए चौड़े पृष्ठों को घुमाएं मैनुअल और स्वचालित बैकअप ऐप लॉक, सुरक्षित स्क्रीन + अंतराल निर्धारित करें + अनुकूलित लाने का अंतराल + मासिक प्राप्त करें (28 दिन) + देर से 10+ की जाँच + छोड़ा हुआ\? देर से 20+ और 2 महीने \ No newline at end of file diff --git a/i18n/src/main/res/values-it/strings.xml b/i18n/src/main/res/values-it/strings.xml index d93c17751..470a1a293 100644 --- a/i18n/src/main/res/values-it/strings.xml +++ b/i18n/src/main/res/values-it/strings.xml @@ -673,7 +673,7 @@ Attenzione: grossi download di massa possono rallentare le fonti e/o bloccare tachiyomi. Tocca per saperne di più. Aggiorna tutto Aggiornamenti dell\'applicazione - Cancella la cache capitoli alla chiusura dell\'app + Cancella la cache capitoli all\'avvio dell\'app %1$d voci non presenti in libreria nel database Niente da Pulire Impossibile ottenere l\'elenco estensioni @@ -882,4 +882,5 @@ Rimuovi anche da %s OK Questo rimuoverà il tracciamento locale. + Cancella scaricati \ No newline at end of file diff --git a/i18n/src/main/res/values-ms/strings.xml b/i18n/src/main/res/values-ms/strings.xml index 47b99a4aa..73677dc93 100644 --- a/i18n/src/main/res/values-ms/strings.xml +++ b/i18n/src/main/res/values-ms/strings.xml @@ -616,7 +616,7 @@ Amaran: muat turun secara pukal besar boleh menyebabkan sumber menjadi lebih perlahan dan/atau menyekat Tachiyomi. Ketik untuk ketahui selebihnya. Kemas kini semua Kemas kini aplikasi - Hapus cache bab apabila menutup aplikasi + Hapus cache bab apabila membuka aplikasi Tiada apa untuk dibersihkan %1$d entri bukan pustaka dalam pangkalan data Gagal mendapatkan senarai sambungan @@ -813,4 +813,5 @@ Ini akan membuang penjejakan secara lokal. OK Juga buang daripada %s + Padam dimuat turun \ No newline at end of file diff --git a/i18n/src/main/res/values-ne/strings.xml b/i18n/src/main/res/values-ne/strings.xml index 4b643c75a..0a3e98bbe 100644 --- a/i18n/src/main/res/values-ne/strings.xml +++ b/i18n/src/main/res/values-ne/strings.xml @@ -421,7 +421,7 @@ अध्याय %1$s मूल आकार जूम सुरु स्थिति - एप बन्दमा अध्याय क्यास खाली गर्नुहोस् + एप खोलेमा अध्याय क्यास खाली गर्नुहोस् राम्रो संगतताको लागि कृपया WebView एप अपडेट गर्नुहोस् इन्ट्री अपडेट गर्न छोड्नुहोस् नपढेको अध्याय(हरू) सँग @@ -696,7 +696,7 @@ तपाईको पुस्तकालयमा एउटै नामको इन्ट्री छ। \n \nके तपाईं अझै जारी राख्न चाहनुहुन्छ\? - पढ्ने सूची + पढिरहेको सूची पुस्तकालय पछिल्लो पटक अपडेट गरिएको: %s %s एक अप्रत्याशित त्रुटिमा पर्यो। समर्थन को लागि हामी तपाईंलाई हाम्रो Discord को #support च्यानलमा क्र्यास लगहरू साझेदारी गर्न सुझाव दिन्छौं। GitHub मा खोल्नुहोस् @@ -831,4 +831,5 @@ %s बाट पनि हटाउनुहोस् यसले लोकल रूपमा ट्र्याकिङ हटाउनेछ। ठीक छ + डाउनलोड गरिएको मेट्नुहोस् \ No newline at end of file diff --git a/i18n/src/main/res/values-pt-rBR/strings.xml b/i18n/src/main/res/values-pt-rBR/strings.xml index 02e64769d..bad001559 100644 --- a/i18n/src/main/res/values-pt-rBR/strings.xml +++ b/i18n/src/main/res/values-pt-rBR/strings.xml @@ -640,7 +640,7 @@ Aviso: grandes downloads em massa podem levar as fontes a ficarem lentas e/ou começarem a bloquear o Tachiyomi. Toque para saber mais. Atualizar tudo Atualizações do aplicativo - Limpar o cache de capítulos ao fechar o aplicativo + Limpar o cache de capítulos ao abrir o aplicativo %1$d itens que não estão na biblioteca no banco de dados Nada a ser limpo Erro ao obter a lista de extensões @@ -841,4 +841,5 @@ Remover o monitoramento do %s\? Isso irá remover o monitoramento localmente. Também remover do %s + Deletar os disponíveis offline \ No newline at end of file diff --git a/i18n/src/main/res/values-ru/strings.xml b/i18n/src/main/res/values-ru/strings.xml index 7b5fd9b9e..fe29476e0 100644 --- a/i18n/src/main/res/values-ru/strings.xml +++ b/i18n/src/main/res/values-ru/strings.xml @@ -652,7 +652,7 @@ Предупреждение: Большое количество загрузок может привести к замедлению работы источников и/или блокировке Tachiyomi. Нажмите для подробностей. Обновить все Обновления приложения - Очищать кэш глав при закрытии приложения + Очищать кэш глав при запуске приложения %1$d не библиотечных серий в базе данных Нечего очищать Не удалось получить список расширений @@ -867,4 +867,5 @@ Это удалит отслеживание локально. Также удалить из %s ОК + Удалить загруженное \ No newline at end of file diff --git a/i18n/src/main/res/values-sc/strings.xml b/i18n/src/main/res/values-sc/strings.xml index b2dfb6a9d..078babc09 100644 --- a/i18n/src/main/res/values-sc/strings.xml +++ b/i18n/src/main/res/values-sc/strings.xml @@ -567,7 +567,7 @@ Cobertedda sarvada Cobertedda Ghia pro s\'arrastamentu - Impostatziones de ordinamentu e visualizatzione pro categoria + Impostatziones de ordinamentu pro categoria Non tenes galu peruna categoria. Incumintza a iscarrigare como Tako @@ -643,7 +643,7 @@ Annullada 5% Ammustra s\'elementu - Ismànnia s\'immàgine in orizontale + Ismànnia in automàticu sas immàgines largas Grìllia cun coberteddas ebbia No incumintzadas Iscurre sas pàginas largas @@ -799,4 +799,36 @@ Iscurrimentu de capìtulu Atzione de iscurrimentu a manca Informatziones de depuratzione de còdighe + Càrcula cada + Intervallu de recùperu personalizadu + Recùpera cada mese (28 dies) + Abbandonadu\? In ritardu de 20+ dies e 2 meses + Agiornamentu imbente prevìdidu + Foras de su perìodu de publicatzione prevìdidu + Intervallos + Imposta s\'agiornamentu pro cada + Modìfica s\'intervallu + Brincadu ca non bi fiat peruna publicatzione prevìdida oe + Imposta s\'intervallu + Verìfica in ritardu de 10+ dies + Perìodu de controllu coladu + Personaliza s\'intervallu + + %d die in antis + %d dies in antis + + + %d die a pustis + %d dies a pustis + + Unu perìodu de gràtzia bassu est cussigiadu pro minimare sa pressione subra sas fontes. Prus controllos pro un\'elementu si perdent, prus longu at a èssere s\'intervallu intre sos controllos cun unu màssimu de 28 dies. + AB + Perìodu de gràtzia prevìdidu pro sa publicatzione + Bogare s\'arrastadore de %s\? + Custu at a bogare s\'arrastamentu locale. + Boga fintzas dae %s + + 1 die + %d dies + \ No newline at end of file diff --git a/i18n/src/main/res/values-tr/strings.xml b/i18n/src/main/res/values-tr/strings.xml index 958c6d482..ca73b41cd 100644 --- a/i18n/src/main/res/values-tr/strings.xml +++ b/i18n/src/main/res/values-tr/strings.xml @@ -830,4 +830,5 @@ Atlandı çünkü bugün bir yayın beklenmiyordu Bu, izlemeyi yerel olarak kaldıracak. Ayrıca şuradan da kaldır: %s + Güncelleme aralığını ayarla \ No newline at end of file diff --git a/i18n/src/main/res/values-zh-rCN/strings.xml b/i18n/src/main/res/values-zh-rCN/strings.xml index 8023d7042..33086ca35 100644 --- a/i18n/src/main/res/values-zh-rCN/strings.xml +++ b/i18n/src/main/res/values-zh-rCN/strings.xml @@ -616,7 +616,7 @@ 警告:批量下载可能导致图源变慢,甚至会使得它们屏蔽 Tachiyomi。点击了解详情。 全部更新 应用更新 - 关闭应用时清除章节缓存 + 启动时清除章节缓存 无需清理 数据库中有 %1$d 部作品未添加到书架 无法获取插件列表 @@ -797,4 +797,5 @@ 要删除 %s 的记录吗? 同时删除 %s 上的数据 将会在本地删除进度记录的关联。 + 删除已下载章节 \ No newline at end of file diff --git a/i18n/src/main/res/values-zh-rTW/strings.xml b/i18n/src/main/res/values-zh-rTW/strings.xml index 41374ee00..54d53901a 100644 --- a/i18n/src/main/res/values-zh-rTW/strings.xml +++ b/i18n/src/main/res/values-zh-rTW/strings.xml @@ -616,7 +616,7 @@ 警告:大量批次下載可能壅塞來源並 (或) 使其封鎖 Tachiyomi。輕觸以瞭解詳情。 全部更新 應用程式更新 - 結束應用程式時清除章節快取 + 啟動應用程式時清除章節快取 資料庫中有 %1$d 部作品不屬於藏書 無須清理 擴充套件清單取得失敗 @@ -631,7 +631,7 @@ 常見問題與指南 5% 顯示作品 - 縮放橫向圖片 + 自動縮放寬頁 導覽寬頁時先平移後翻頁 純封面格狀 無已讀的章節 @@ -785,4 +785,8 @@ 目錄滑動動作 每列 %d 欄 確定 + 刪除舊有下載章節 + 移除「%s」歷程平台? + 這將在本機上解除登錄該歷程平台。 + 同時移除「%s」上的資料 \ No newline at end of file From 8cc42bce5a37c256edbddd4a618787dfec9c2118 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 15:54:30 -0400 Subject: [PATCH 08/25] Tweak chapter swipe directions and icon color --- .../kanade/presentation/manga/MangaScreen.kt | 18 +++++++++--------- .../manga/components/MangaChapterListItem.kt | 13 +++++++------ .../settings/screen/SettingsLibraryScreen.kt | 4 ++-- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 2 +- .../tachiyomi/ui/manga/MangaScreenModel.kt | 4 ++-- .../library/service/LibraryPreferences.kt | 4 ++-- 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index c0c2b3f6a..7fb49250a 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -87,8 +87,8 @@ fun MangaScreen( dateRelativeTime: Int, dateFormat: DateFormat, isTabletUi: Boolean, - chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, @@ -141,8 +141,8 @@ fun MangaScreen( snackbarHostState = snackbarHostState, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, - chapterSwipeEndAction = chapterSwipeEndAction, chapterSwipeStartAction = chapterSwipeStartAction, + chapterSwipeEndAction = chapterSwipeEndAction, onBackClicked = onBackClicked, onChapterClicked = onChapterClicked, onDownloadChapter = onDownloadChapter, @@ -175,8 +175,8 @@ fun MangaScreen( state = state, snackbarHostState = snackbarHostState, dateRelativeTime = dateRelativeTime, - chapterSwipeEndAction = chapterSwipeEndAction, chapterSwipeStartAction = chapterSwipeStartAction, + chapterSwipeEndAction = chapterSwipeEndAction, dateFormat = dateFormat, onBackClicked = onBackClicked, onChapterClicked = onChapterClicked, @@ -214,8 +214,8 @@ private fun MangaScreenSmallImpl( snackbarHostState: SnackbarHostState, dateRelativeTime: Int, dateFormat: DateFormat, - chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, @@ -421,8 +421,8 @@ private fun MangaScreenSmallImpl( chapters = chapters, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, - chapterSwipeEndAction = chapterSwipeEndAction, chapterSwipeStartAction = chapterSwipeStartAction, + chapterSwipeEndAction = chapterSwipeEndAction, onChapterClicked = onChapterClicked, onDownloadChapter = onDownloadChapter, onChapterSelected = onChapterSelected, @@ -440,8 +440,8 @@ fun MangaScreenLargeImpl( snackbarHostState: SnackbarHostState, dateRelativeTime: Int, dateFormat: DateFormat, - chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, @@ -641,8 +641,8 @@ fun MangaScreenLargeImpl( chapters = chapters, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, - chapterSwipeEndAction = chapterSwipeEndAction, chapterSwipeStartAction = chapterSwipeStartAction, + chapterSwipeEndAction = chapterSwipeEndAction, onChapterClicked = onChapterClicked, onDownloadChapter = onDownloadChapter, onChapterSelected = onChapterSelected, @@ -703,8 +703,8 @@ private fun LazyListScope.sharedChapterItems( chapters: List, dateRelativeTime: Int, dateFormat: DateFormat, - chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, @@ -751,8 +751,8 @@ private fun LazyListScope.sharedChapterItems( downloadIndicatorEnabled = chapters.fastAll { !it.selected }, downloadStateProvider = { chapterItem.downloadState }, downloadProgressProvider = { chapterItem.downloadProgress }, - chapterSwipeEndAction = chapterSwipeEndAction, chapterSwipeStartAction = chapterSwipeStartAction, + chapterSwipeEndAction = chapterSwipeEndAction, onLongClick = { onChapterSelected(chapterItem, !chapterItem.selected, true, true) haptic.performHapticFeedback(HapticFeedbackType.LongPress) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 63fdfecca..1fb0abb52 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -22,6 +22,7 @@ import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text +import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect @@ -68,8 +69,8 @@ fun MangaChapterListItem( downloadIndicatorEnabled: Boolean, downloadStateProvider: () -> Download.State, downloadProgressProvider: () -> Int, - chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onLongClick: () -> Unit, onClick: () -> Unit, onDownloadClick: ((ChapterDownloadAction) -> Unit)?, @@ -226,19 +227,19 @@ private fun getSwipeAction( onSwipe: () -> Unit, ): me.saket.swipe.SwipeAction? { return when (action) { - LibraryPreferences.ChapterSwipeAction.ToggleRead -> SwipeAction( + LibraryPreferences.ChapterSwipeAction.ToggleRead -> swipeAction( icon = if (!read) Icons.Outlined.Done else Icons.Outlined.RemoveDone, background = background, isUndo = read, onSwipe = onSwipe, ) - LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> SwipeAction( + LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> swipeAction( icon = if (!bookmark) Icons.Outlined.BookmarkAdd else Icons.Outlined.BookmarkRemove, background = background, isUndo = bookmark, onSwipe = onSwipe, ) - LibraryPreferences.ChapterSwipeAction.Download -> SwipeAction( + LibraryPreferences.ChapterSwipeAction.Download -> swipeAction( icon = when (downloadState) { Download.State.NOT_DOWNLOADED, Download.State.ERROR -> Icons.Outlined.Download Download.State.QUEUE, Download.State.DOWNLOADING -> Icons.Outlined.FileDownloadOff @@ -251,7 +252,7 @@ private fun getSwipeAction( } } -private fun SwipeAction( +private fun swipeAction( onSwipe: () -> Unit, icon: ImageVector, background: Color, @@ -262,7 +263,7 @@ private fun SwipeAction( Icon( modifier = Modifier.padding(16.dp), imageVector = icon, - tint = MaterialTheme.colorScheme.onSurface, + tint = contentColorFor(background), contentDescription = null, ) }, 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 625157427..b6bd25489 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 @@ -281,7 +281,7 @@ object SettingsLibraryScreen : SearchableSettings { title = stringResource(R.string.pref_chapter_swipe), preferenceItems = listOf( Preference.PreferenceItem.ListPreference( - pref = libraryPreferences.swipeEndAction(), + pref = libraryPreferences.swipeToStartAction(), title = stringResource(R.string.pref_chapter_swipe_end), entries = mapOf( LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.disabled), @@ -291,7 +291,7 @@ object SettingsLibraryScreen : SearchableSettings { ), ), Preference.PreferenceItem.ListPreference( - pref = libraryPreferences.swipeStartAction(), + pref = libraryPreferences.swipeToEndAction(), title = stringResource(R.string.pref_chapter_swipe_start), entries = mapOf( LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.disabled), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index dce1c8f46..33244db67 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -101,8 +101,8 @@ class MangaScreen( dateRelativeTime = screenModel.relativeTime, dateFormat = screenModel.dateFormat, isTabletUi = isTabletUi(), - chapterSwipeEndAction = screenModel.chapterSwipeEndAction, chapterSwipeStartAction = screenModel.chapterSwipeStartAction, + chapterSwipeEndAction = screenModel.chapterSwipeEndAction, onBackClicked = navigator::pop, onChapterClicked = { openChapter(context, it) }, onDownloadChapter = screenModel::runChapterDownloadActions.takeIf { !successState.source.isLocalOrStub() }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 98e4338d3..5f78f9726 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -122,8 +122,8 @@ class MangaInfoScreenModel( private val filteredChapters: List? get() = successState?.processedChapters - val chapterSwipeEndAction = libraryPreferences.swipeEndAction().get() - val chapterSwipeStartAction = libraryPreferences.swipeStartAction().get() + val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get() + val chapterSwipeEndAction = libraryPreferences.swipeToStartAction().get() val relativeTime by uiPreferences.relativeTime().asState(coroutineScope) val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get())) diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index de66117ad..7aa496c54 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -134,9 +134,9 @@ class LibraryPreferences( // region Swipe Actions - fun swipeEndAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleBookmark) + fun swipeToStartAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleBookmark) - fun swipeStartAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleRead) + fun swipeToEndAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleRead) // endregion From a585d46e7aa7ed9be9f3bb55e7e5caf741ead475 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 16:02:57 -0400 Subject: [PATCH 09/25] Renovate: group Compose compiler and Kotlin version upgrades --- .github/renovate.json | 13 ------------- .github/renovate.json5 | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) delete mode 100644 .github/renovate.json create mode 100644 .github/renovate.json5 diff --git a/.github/renovate.json b/.github/renovate.json deleted file mode 100644 index 4c0f875f4..000000000 --- a/.github/renovate.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": [ - "config:base" - ], - "schedule": ["every sunday"], - "packageRules": [ - { - "managers": ["maven"], - "packageNames": ["com.google.guava:guava"], - "versionScheme": "docker" - } - ] -} diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 000000000..55dae05ca --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,22 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ], + "schedule": ["every sunday"], + "packageRules": [ + { + "managers": ["maven"], + "packageNames": ["com.google.guava:guava"], + "versionScheme": "docker" + }, + { + // Compiler plugins are tightly coupled to Kotlin version + "groupName": "Kotlin", + "matchPackagePrefixes": [ + "androidx.compose.compiler", + "org.jetbrains.kotlin", + ], + } + ] +} From f8cf3db4a456462a80ecc252e8655f6b5c66c4d4 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 17:46:48 -0400 Subject: [PATCH 10/25] Allow download ahead even if entry isn't favorited --- .../java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt | 6 +++--- i18n/src/main/res/values/strings.xml | 2 +- 2 files changed, 4 insertions(+), 4 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 422eacceb..8825864b9 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 @@ -204,6 +204,7 @@ class ReaderViewModel( } private val incognitoMode = preferences.incognitoMode().get() + private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get() init { // To save state @@ -444,9 +445,8 @@ class ReaderViewModel( } private fun downloadNextChapters() { + if (downloadAheadAmount == 0) return val manga = manga ?: return - val amount = downloadPreferences.autoDownloadWhileReading().get() - if (amount == 0 || !manga.favorite) return // Only download ahead if current + next chapter is already downloaded too to avoid jank if (getCurrentChapter()?.pageLoader?.isLocal == true) return @@ -466,7 +466,7 @@ class ReaderViewModel( } else { this } - }.take(amount) + }.take(downloadAheadAmount) downloadManager.downloadChapters( manga, diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 06c68f233..6c6ccda5b 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -470,7 +470,7 @@ Next unread chapter Next %d unread chapters - Only works on entries in library and if the current chapter plus the next one are already downloaded + Only works if the current chapter + the next one are already downloaded. Save as CBZ archive Split tall images Improves reader performance From b008223661fe74cad382b1c89d4b66f7ec0baee4 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 17:47:10 -0400 Subject: [PATCH 11/25] Minor reorganization --- .../more/settings/screen/SettingsAdvancedScreen.kt | 1 + .../more/settings/screen/SettingsMainScreen.kt | 1 + .../more/settings/screen/SettingsSearchScreen.kt | 12 +++++++----- .../more/settings/screen/{ => about}/AboutScreen.kt | 2 +- .../{ => about}/OpenSourceLibraryLicenseScreen.kt | 2 +- .../screen/{ => about}/OpenSourceLicensesScreen.kt | 2 +- .../screen/{ => advanced}/ClearDatabaseScreen.kt | 2 +- .../more/settings/screen/debug/DebugInfoScreen.kt | 2 +- .../eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt | 2 +- 9 files changed, 15 insertions(+), 11 deletions(-) rename app/src/main/java/eu/kanade/presentation/more/settings/screen/{ => about}/AboutScreen.kt (99%) rename app/src/main/java/eu/kanade/presentation/more/settings/screen/{ => about}/OpenSourceLibraryLicenseScreen.kt (98%) rename app/src/main/java/eu/kanade/presentation/more/settings/screen/{ => about}/OpenSourceLicensesScreen.kt (97%) rename app/src/main/java/eu/kanade/presentation/more/settings/screen/{ => advanced}/ClearDatabaseScreen.kt (99%) 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 c74437b85..20a91ee0d 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 @@ -28,6 +28,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.more.settings.Preference +import eu.kanade.presentation.more.settings.screen.advanced.ClearDatabaseScreen import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen import eu.kanade.presentation.util.collectAsState import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index 5b094865b..6bd98f9dd 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -44,6 +44,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.UpIcon +import eu.kanade.presentation.more.settings.screen.about.AboutScreen import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.Screen diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt index fa8392140..a5dc8a620 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSearchScreen.kt @@ -1,6 +1,5 @@ package eu.kanade.presentation.more.settings.screen -import android.content.res.Resources import androidx.compose.animation.Crossfade import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -39,12 +38,14 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow @@ -52,7 +53,6 @@ import eu.kanade.presentation.components.UpIcon import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.system.isLTR import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.screens.EmptyScreen @@ -165,6 +165,8 @@ private fun SearchResult( ) { if (searchKey.isEmpty()) return + val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr + val index = getIndex() val result by produceState?>(initialValue = null, searchKey) { value = index.asSequence() @@ -200,7 +202,7 @@ private fun SearchResult( SearchResultItem( route = settingsData.route, title = p.title, - breadcrumbs = getLocalizedBreadcrumb(path = settingsData.title, node = categoryTitle), + breadcrumbs = getLocalizedBreadcrumb(path = settingsData.title, node = categoryTitle, isLtr = isLtr), highlightKey = p.title, ) } @@ -265,11 +267,11 @@ private fun getIndex() = settingScreens ) } -private fun getLocalizedBreadcrumb(path: String, node: String?): String { +private fun getLocalizedBreadcrumb(path: String, node: String?, isLtr: Boolean): String { return if (node == null) { path } else { - if (Resources.getSystem().isLTR) { + if (isLtr) { // This locale reads left to right. "$path > $node" } else { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt similarity index 99% rename from app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt rename to app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index 655e97e85..6f42edb7a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -1,4 +1,4 @@ -package eu.kanade.presentation.more.settings.screen +package eu.kanade.presentation.more.settings.screen.about import android.content.Context import androidx.compose.animation.AnimatedVisibility diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/OpenSourceLibraryLicenseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLibraryLicenseScreen.kt similarity index 98% rename from app/src/main/java/eu/kanade/presentation/more/settings/screen/OpenSourceLibraryLicenseScreen.kt rename to app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLibraryLicenseScreen.kt index 2a35fb017..ffc7bf544 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/OpenSourceLibraryLicenseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLibraryLicenseScreen.kt @@ -1,4 +1,4 @@ -package eu.kanade.presentation.more.settings.screen +package eu.kanade.presentation.more.settings.screen.about import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/OpenSourceLicensesScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt similarity index 97% rename from app/src/main/java/eu/kanade/presentation/more/settings/screen/OpenSourceLicensesScreen.kt rename to app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt index 6ca9b5831..e5b94adb8 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/OpenSourceLicensesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt @@ -1,4 +1,4 @@ -package eu.kanade.presentation.more.settings.screen +package eu.kanade.presentation.more.settings.screen.about import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme 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/advanced/ClearDatabaseScreen.kt similarity index 99% rename from app/src/main/java/eu/kanade/presentation/more/settings/screen/ClearDatabaseScreen.kt rename to app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt index 54580d96b..89e0d951c 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/advanced/ClearDatabaseScreen.kt @@ -1,4 +1,4 @@ -package eu.kanade.presentation.more.settings.screen +package eu.kanade.presentation.more.settings.screen.advanced import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt index 4588c5fe2..8f656aa95 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt @@ -12,7 +12,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.PreferenceScaffold -import eu.kanade.presentation.more.settings.screen.AboutScreen +import eu.kanade.presentation.more.settings.screen.about.AboutScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.DeviceUtil diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt index 8a18219ad..5a6d05025 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsScreen.kt @@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow -import eu.kanade.presentation.more.settings.screen.AboutScreen import eu.kanade.presentation.more.settings.screen.SettingsAppearanceScreen import eu.kanade.presentation.more.settings.screen.SettingsBackupScreen import eu.kanade.presentation.more.settings.screen.SettingsMainScreen +import eu.kanade.presentation.more.settings.screen.about.AboutScreen import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.Screen From 16cbcecd99c24f87b087dc7d02b8ccc93f1f1251 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 17:56:15 -0400 Subject: [PATCH 12/25] Fix download ahead Fixes #9669 --- .../main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt | 3 ++- 1 file changed, 2 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 8825864b9..ac6a5a07d 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 @@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader +import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage @@ -449,7 +450,7 @@ class ReaderViewModel( val manga = manga ?: return // Only download ahead if current + next chapter is already downloaded too to avoid jank - if (getCurrentChapter()?.pageLoader?.isLocal == true) return + if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return val nextChapter = state.value.viewerChapters?.nextChapter?.chapter ?: return viewModelScope.launchIO { From 226272f686ccf4dea1cc4e81b0c305749d888231 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 8 Jul 2023 18:05:00 -0400 Subject: [PATCH 13/25] Refactor reader progress/history logic --- .../tachiyomi/ui/reader/ReaderActivity.kt | 14 ++- .../tachiyomi/ui/reader/ReaderViewModel.kt | 111 ++++++++---------- 2 files changed, 59 insertions(+), 66 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 1710814df..a7cc20707 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 @@ -235,13 +235,18 @@ class ReaderActivity : BaseActivity() { readingModeToast?.cancel() } + override fun onPause() { + viewModel.flushReadTimer() + super.onPause() + } + /** * Set menu visibility again on activity resume to apply immersive mode again if needed. * Helps with rotations. */ override fun onResume() { super.onResume() - viewModel.setReadStartTime() + viewModel.restartReadTimer() setMenuVisibility(viewModel.state.value.menuVisible, animate = false) } @@ -588,7 +593,7 @@ class ReaderActivity : BaseActivity() { * Sets the visibility of the menu according to [visible] and with an optional parameter to * [animate] the views. */ - fun setMenuVisibility(visible: Boolean, animate: Boolean = true) { + private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) { viewModel.showMenus(visible) if (visible) { windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) @@ -793,7 +798,6 @@ class ReaderActivity : BaseActivity() { * Called from the viewer whenever a [page] is marked as active. It updates the values of the * bottom menu and delegates the change to the presenter. */ - @SuppressLint("SetTextI18n") fun onPageSelected(page: ReaderPage) { viewModel.onPageSelected(page) } @@ -811,7 +815,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.launchIO { viewModel.preloadChapter(chapter) } + lifecycleScope.launchIO { viewModel.preload(chapter) } } /** @@ -898,7 +902,7 @@ class ReaderActivity : BaseActivity() { /** * Updates viewer inset depending on fullscreen reader preferences. */ - fun updateViewerInset(fullscreen: Boolean) { + private fun updateViewerInset(fullscreen: Boolean) { viewModel.state.value.viewer?.getView()?.applyInsetter { if (!fullscreen) { type(navigationBars = true, statusBars = true) { 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 ac6a5a07d..5cd95e5a6 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 @@ -57,7 +57,6 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import logcat.LogPriority import tachiyomi.core.util.lang.launchIO @@ -314,12 +313,15 @@ class ReaderViewModel( * Called when the user changed to the given [chapter] when changing pages from the viewer. * It's used only to set this chapter as active. */ - private suspend fun loadNewChapter(chapter: ReaderChapter) { + private fun loadNewChapter(chapter: ReaderChapter) { val loader = loader ?: return - logcat { "Loading ${chapter.chapter.url}" } + viewModelScope.launchIO { + logcat { "Loading ${chapter.chapter.url}" } + + flushReadTimer() + restartReadTimer() - withIOContext { try { loadChapter(loader, chapter) } catch (e: Throwable) { @@ -358,7 +360,7 @@ class ReaderViewModel( * Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so * that the user doesn't have to wait too long to continue reading. */ - private suspend fun preload(chapter: ReaderChapter) { + suspend fun preload(chapter: ReaderChapter) { if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) { return } @@ -397,9 +399,7 @@ class ReaderViewModel( fun onViewerLoaded(viewer: Viewer?) { mutableState.update { - it.copy( - viewer = viewer, - ) + it.copy(viewer = viewer) } } @@ -414,31 +414,19 @@ class ReaderViewModel( return } - val currentChapters = state.value.viewerChapters ?: return - val pages = page.chapter.pages ?: return val selectedChapter = page.chapter + val pages = selectedChapter.pages ?: return // Save last page read and mark as read if needed - saveReadingProgress() - mutableState.update { - it.copy( - currentPage = page.index + 1, - ) - } - if (!incognitoMode) { - selectedChapter.chapter.last_page_read = page.index - if (selectedChapter.pages?.lastIndex == page.index) { - selectedChapter.chapter.read = true - updateTrackChapterRead(selectedChapter) - deleteChapterIfNeeded(selectedChapter) - } + viewModelScope.launchNonCancellable { + updateChapterProgress(page.index) } - if (selectedChapter != currentChapters.currChapter) { + if (selectedChapter != getCurrentChapter()) { logcat { "Setting ${selectedChapter.chapter.url} as active" } - setReadStartTime() - viewModelScope.launch { loadNewChapter(selectedChapter) } + loadNewChapter(selectedChapter) } + val inDownloadRange = page.number.toDouble() / pages.size > 0.25 if (inDownloadRange) { downloadNextChapters() @@ -508,42 +496,54 @@ class ReaderViewModel( } /** - * Called when reader chapter is changed in reader or when activity is paused. + * Saves the chapter progress (last read page and whether it's read) + * if incognito mode isn't on. */ - private fun saveReadingProgress() { - getCurrentChapter()?.let { - viewModelScope.launchNonCancellable { - saveChapterProgress(it) - saveChapterHistory(it) + private suspend fun updateChapterProgress(pageIndex: Int) { + val readerChapter = getCurrentChapter() ?: return + + mutableState.update { + it.copy(currentPage = pageIndex + 1) + } + + if (!incognitoMode) { + readerChapter.requestedPage = pageIndex + readerChapter.chapter.last_page_read = pageIndex + + updateChapter.await( + ChapterUpdate( + id = readerChapter.chapter.id!!, + read = readerChapter.chapter.read, + bookmark = readerChapter.chapter.bookmark, + lastPageRead = readerChapter.chapter.last_page_read.toLong(), + ), + ) + + if (readerChapter.pages?.lastIndex == pageIndex) { + readerChapter.chapter.read = true + updateTrackChapterRead(readerChapter) + deleteChapterIfNeeded(readerChapter) } } } - /** - * Saves this [readerChapter] progress (last read page and whether it's read) - * if incognito mode isn't on. - */ - private suspend fun saveChapterProgress(readerChapter: ReaderChapter) { - if (incognitoMode) return + fun restartReadTimer() { + chapterReadStartTime = Date().time + } - val chapter = readerChapter.chapter - readerChapter.requestedPage = chapter.last_page_read - updateChapter.await( - ChapterUpdate( - id = chapter.id!!, - read = chapter.read, - bookmark = chapter.bookmark, - lastPageRead = chapter.last_page_read.toLong(), - ), - ) + fun flushReadTimer() { + viewModelScope.launchNonCancellable { + updateHistory() + } } /** - * Saves this [readerChapter] last read history if incognito mode isn't on. + * Saves the chapter last read history if incognito mode isn't on. */ - private suspend fun saveChapterHistory(readerChapter: ReaderChapter) { + private suspend fun updateHistory() { if (incognitoMode) return + val readerChapter = getCurrentChapter() ?: return val chapterId = readerChapter.chapter.id!! val endTime = Date() val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0 @@ -552,17 +552,6 @@ class ReaderViewModel( chapterReadStartTime = null } - fun setReadStartTime() { - chapterReadStartTime = Date().time - } - - /** - * Called from the activity to preload the given [chapter]. - */ - suspend fun preloadChapter(chapter: ReaderChapter) { - preload(chapter) - } - /** * Called from the activity to load and set the next chapter as active. */ From 7c62453280e775809031744518d4a22201237f5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 8 Jul 2023 22:08:37 -0400 Subject: [PATCH 14/25] Update aboutlib.version to v10.8.1 (#9685) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f42bd035c..f013780ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "10.8.0" +aboutlib_version = "10.8.1" okhttp_version = "5.0.0-alpha.11" shizuku_version = "12.2.0" sqlite = "2.3.1" From 1e3d9a00f2366919430a8077ad79808c8514d793 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 9 Jul 2023 09:54:05 -0400 Subject: [PATCH 15/25] Handle chapter read status in correct order Fixes #9687 --- .../eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 5cd95e5a6..57c6d61df 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 @@ -510,6 +510,12 @@ class ReaderViewModel( readerChapter.requestedPage = pageIndex readerChapter.chapter.last_page_read = pageIndex + if (readerChapter.pages?.lastIndex == pageIndex) { + readerChapter.chapter.read = true + updateTrackChapterRead(readerChapter) + deleteChapterIfNeeded(readerChapter) + } + updateChapter.await( ChapterUpdate( id = readerChapter.chapter.id!!, @@ -518,12 +524,6 @@ class ReaderViewModel( lastPageRead = readerChapter.chapter.last_page_read.toLong(), ), ) - - if (readerChapter.pages?.lastIndex == pageIndex) { - readerChapter.chapter.read = true - updateTrackChapterRead(readerChapter) - deleteChapterIfNeeded(readerChapter) - } } } From 0759936226ca3fbc09be17d8bc779ffa1beb676d Mon Sep 17 00:00:00 2001 From: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> Date: Mon, 10 Jul 2023 01:08:58 +0700 Subject: [PATCH 16/25] Remove scrollable animation workaround (#9690) Reverts ba93060e591fccf3c85995b50f496bc937ae8ae4 Related https://android-review.googlesource.com/c/platform/frameworks/support/+/2239762 --- .../presentation/browse/GlobalSearchScreen.kt | 2 +- .../browse/MigrateSearchScreen.kt | 2 +- .../browse/components/BrowseSourceList.kt | 2 +- .../presentation/category/CategoryScreen.kt | 2 +- .../kanade/presentation/manga/MangaScreen.kt | 2 +- .../settings/screen/SettingsMainScreen.kt | 2 +- .../settings/screen/debug/WorkerInfoScreen.kt | 2 +- .../settings/widget/TriStateListDialog.kt | 2 +- .../more/stats/StatsScreenContent.kt | 2 +- .../source/browse/SourceFilterDialog.kt | 2 +- .../presentation/core/components/LazyGrid.kt | 4 -- .../presentation/core/components/LazyList.kt | 38 +----------- .../core/components/WheelPicker.kt | 1 + .../presentation/core/util/Scrollable.kt | 60 ------------------- 14 files changed, 12 insertions(+), 111 deletions(-) delete mode 100644 presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt diff --git a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt index b9a8f636e..790053063 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -19,7 +20,6 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchState import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.manga.model.Manga -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt index 4b7b70c45..76a78506a 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSearchScreen.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.State import eu.kanade.presentation.browse.components.GlobalSearchCardRow @@ -14,7 +15,6 @@ import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchState import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.manga.model.Manga -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold @Composable diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceList.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceList.kt index 5d7680c05..63be2a55d 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceList.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceList.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.browse.components import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -12,7 +13,6 @@ import eu.kanade.presentation.library.components.MangaListItem import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaCover -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.util.plus @Composable diff --git a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt index b33325deb..f7d699164 100644 --- a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt @@ -3,6 +3,7 @@ package eu.kanade.presentation.category import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState @@ -16,7 +17,6 @@ import eu.kanade.presentation.components.AppBar import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.category.CategoryScreenState import tachiyomi.domain.category.model.Category -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.components.material.topSmallPaddingValues diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 7fb49250a..2a1c9882a 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState @@ -69,7 +70,6 @@ import tachiyomi.domain.chapter.service.missingChaptersCount import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.model.StubSource -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.TwoPanelBox import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt index 6bd98f9dd..513acc39b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsMainScreen.kt @@ -4,6 +4,7 @@ import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape @@ -49,7 +50,6 @@ import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.presentation.util.LocalBackPress import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt index 8b16084c6..12393080d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -39,7 +40,6 @@ import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.util.plus diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt index 6983e206f..82ababc88 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons @@ -28,7 +29,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrolledToStart diff --git a/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenContent.kt b/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenContent.kt index 27a855d34..6134b8f57 100644 --- a/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/stats/StatsScreenContent.kt @@ -3,6 +3,7 @@ package eu.kanade.presentation.more.stats import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.CollectionsBookmark @@ -19,7 +20,6 @@ import eu.kanade.presentation.more.stats.components.StatsSection import eu.kanade.presentation.more.stats.data.StatsData import eu.kanade.presentation.util.toDurationString import eu.kanade.tachiyomi.R -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.material.padding import java.util.Locale import kotlin.time.DurationUnit diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt index 95b3c4af2..20eb63a80 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme @@ -24,7 +25,6 @@ import tachiyomi.domain.manga.model.TriStateFilter import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.HeadingItem -import tachiyomi.presentation.core.components.LazyColumn import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TextItem import tachiyomi.presentation.core.components.material.Button diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt index 085caf59f..f0061e11a 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyGrid.kt @@ -1,6 +1,5 @@ package tachiyomi.presentation.core.components -import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.grid.GridCells @@ -14,7 +13,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import tachiyomi.presentation.core.util.flingBehaviorIgnoringMotionScale @Composable fun FastScrollLazyVerticalGrid( @@ -31,7 +29,6 @@ fun FastScrollLazyVerticalGrid( verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), userScrollEnabled: Boolean = true, content: LazyGridScope.() -> Unit, ) { @@ -54,7 +51,6 @@ fun FastScrollLazyVerticalGrid( reverseLayout = reverseLayout, verticalArrangement = verticalArrangement, horizontalArrangement = horizontalArrangement, - flingBehavior = flingBehavior, userScrollEnabled = userScrollEnabled, content = content, ) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt index afce11140..e5d27e0e1 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/LazyList.kt @@ -1,9 +1,9 @@ package tachiyomi.presentation.core.components -import androidx.compose.foundation.gestures.FlingBehavior import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState @@ -15,38 +15,6 @@ import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.dp import tachiyomi.presentation.core.util.drawVerticalScrollbar -import tachiyomi.presentation.core.util.flingBehaviorIgnoringMotionScale - -/** - * LazyColumn with fling animation fix - * - * @see flingBehaviorIgnoringMotionScale - */ -@Composable -fun LazyColumn( - modifier: Modifier = Modifier, - state: LazyListState = rememberLazyListState(), - contentPadding: PaddingValues = PaddingValues(0.dp), - reverseLayout: Boolean = false, - verticalArrangement: Arrangement.Vertical = - if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, - horizontalAlignment: Alignment.Horizontal = Alignment.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), - userScrollEnabled: Boolean = true, - content: LazyListScope.() -> Unit, -) { - androidx.compose.foundation.lazy.LazyColumn( - modifier = modifier, - state = state, - contentPadding = contentPadding, - reverseLayout = reverseLayout, - verticalArrangement = verticalArrangement, - horizontalAlignment = horizontalAlignment, - flingBehavior = flingBehavior, - userScrollEnabled = userScrollEnabled, - content = content, - ) -} /** * LazyColumn with scrollbar. @@ -60,7 +28,6 @@ fun ScrollbarLazyColumn( verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit, ) { @@ -81,7 +48,6 @@ fun ScrollbarLazyColumn( reverseLayout = reverseLayout, verticalArrangement = verticalArrangement, horizontalAlignment = horizontalAlignment, - flingBehavior = flingBehavior, userScrollEnabled = userScrollEnabled, content = content, ) @@ -99,7 +65,6 @@ fun FastScrollLazyColumn( verticalArrangement: Arrangement.Vertical = if (!reverseLayout) Arrangement.Top else Arrangement.Bottom, horizontalAlignment: Alignment.Horizontal = Alignment.Start, - flingBehavior: FlingBehavior = flingBehaviorIgnoringMotionScale(), userScrollEnabled: Boolean = true, content: LazyListScope.() -> Unit, ) { @@ -115,7 +80,6 @@ fun FastScrollLazyColumn( reverseLayout = reverseLayout, verticalArrangement = verticalArrangement, horizontalAlignment = horizontalAlignment, - flingBehavior = flingBehavior, userScrollEnabled = userScrollEnabled, content = content, ) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt index e1d2fd320..6d24aa545 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListItemInfo import androidx.compose.foundation.lazy.LazyListState diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt deleted file mode 100644 index 96d9166b1..000000000 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollable.kt +++ /dev/null @@ -1,60 +0,0 @@ -package tachiyomi.presentation.core.util - -import androidx.compose.animation.core.AnimationState -import androidx.compose.animation.core.DecayAnimationSpec -import androidx.compose.animation.core.animateDecay -import androidx.compose.animation.rememberSplineBasedDecay -import androidx.compose.foundation.gestures.FlingBehavior -import androidx.compose.foundation.gestures.ScrollScope -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.MotionDurationScale -import kotlinx.coroutines.withContext -import kotlin.math.abs - -/** - * FlingBehavior that always uses the default motion scale. - * - * This makes the scrolling animation works like View's lists - * when "Remove animation" settings is on. - */ -@Composable -fun flingBehaviorIgnoringMotionScale(): FlingBehavior { - val flingSpec = rememberSplineBasedDecay() - return remember(flingSpec) { - DefaultFlingBehavior(flingSpec) - } -} - -private val DefaultMotionDurationScale = object : MotionDurationScale { - // Use default motion scale factor - override val scaleFactor: Float = 1f -} - -private class DefaultFlingBehavior( - private val flingDecay: DecayAnimationSpec, -) : FlingBehavior { - override suspend fun ScrollScope.performFling(initialVelocity: Float): Float { - // come up with the better threshold, but we need it since spline curve gives us NaNs - return if (abs(initialVelocity) > 1f) { - var velocityLeft = initialVelocity - var lastValue = 0f - withContext(DefaultMotionDurationScale) { - AnimationState( - initialValue = 0f, - initialVelocity = initialVelocity, - ).animateDecay(flingDecay) { - val delta = value - lastValue - val consumed = scrollBy(delta) - lastValue = value - velocityLeft = this.velocity - // avoid rounding errors and stop if anything is unconsumed - if (abs(delta - consumed) > 0.5f) this.cancelAnimation() - } - } - velocityLeft - } else { - initialVelocity - } - } -} From 6063efd101f6a79e4404fffbd9a63a1d1c72de78 Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 9 Jul 2023 22:43:56 -0400 Subject: [PATCH 17/25] Bump dependencies --- gradle/compose.versions.toml | 4 ++-- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 99769e9b6..1c5c86a92 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.4.8" -compose-bom = "2023.06.00-alpha01" -accompanist = "0.31.4-beta" +compose-bom = "2023.07.00-alpha01" +accompanist = "0.31.5-beta" [libraries] activity = "androidx.activity:activity-compose:1.7.2" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f013780ca..37e1d8cff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -58,7 +58,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013 photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" -compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.3" +compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.4" compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0" swipe = "me.saket.swipe:swipe:1.2.0" From d0f52ea93de86ff136ecd92895da45d0134fd3c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 10 Jul 2023 11:16:34 -0400 Subject: [PATCH 18/25] Update aboutlib.version to v10.8.2 (#9689) Update dependency com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin to v10.8.2 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 37e1d8cff..8d4794280 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "10.8.1" +aboutlib_version = "10.8.2" okhttp_version = "5.0.0-alpha.11" shizuku_version = "12.2.0" sqlite = "2.3.1" From a577f5534f31086174b1cc851d8b489d69f557e8 Mon Sep 17 00:00:00 2001 From: KaiserBh <41852205+KaiserBh@users.noreply.github.com> Date: Tue, 11 Jul 2023 05:52:57 +1000 Subject: [PATCH 19/25] Database changes to support library syncing (#9683) * feat: added migrations. * feat: create triggers, account for new installs. * feat: update mappers to include the new field. * feat: update backupManga and backupChapter. Include the new fields to be backed up as well. * feat: add sql query to fetch all manga with `last_favorited_at` field. * feat: version bump. * chore: revert and refactor. * chore: forgot to lower case the field name. * chore: added getAllManga query as well renamed `fetchMangaWithLastFavorite` to `getMangasWithFavoriteTimestamp` * chore: oops that's not meant to be there. * feat: back fill and set last_modified_at to not null. * chore: remove redundant triggers. * fix: build error, accidentally removed insert. * fix: build error, accidentally removed insert. * refactor: review pointer, make fields not null. --- app/build.gradle.kts | 5 +- .../data/backup/models/BackupChapter.kt | 5 +- .../data/backup/models/BackupManga.kt | 6 +++ .../tachiyomi/data/database/models/Chapter.kt | 3 ++ .../data/database/models/ChapterImpl.kt | 2 + .../tachiyomi/data/chapter/ChapterMapper.kt | 5 +- .../java/tachiyomi/data/manga/MangaMapper.kt | 12 +++-- .../sqldelight/tachiyomi/data/chapters.sq | 14 +++++- .../main/sqldelight/tachiyomi/data/mangas.sq | 34 +++++++++++-- .../tachiyomi/data/mangas_categories.sq | 14 +++++- .../sqldelight/tachiyomi/migrations/25.sqm | 49 +++++++++++++++++++ .../tachiyomi/domain/chapter/model/Chapter.kt | 2 + .../tachiyomi/domain/manga/model/Manga.kt | 4 ++ 13 files changed, 139 insertions(+), 16 deletions(-) create mode 100644 data/src/main/sqldelight/tachiyomi/migrations/25.sqm diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d178945dd..e08346259 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,9 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi" - - versionCode = 103 + + versionCode = 104 + versionName = "0.14.6" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt index a70121a8c..ed42a75ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt @@ -20,6 +20,7 @@ data class BackupChapter( // chapterNumber is called number is 1.x @ProtoNumber(9) var chapterNumber: Float = 0F, @ProtoNumber(10) var sourceOrder: Long = 0, + @ProtoNumber(11) var lastModifiedAt: Long = 0, ) { fun toChapterImpl(): Chapter { return Chapter.create().copy( @@ -33,11 +34,12 @@ data class BackupChapter( dateFetch = this@BackupChapter.dateFetch, dateUpload = this@BackupChapter.dateUpload, sourceOrder = this@BackupChapter.sourceOrder, + lastModifiedAt = this@BackupChapter.lastModifiedAt, ) } } -val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long -> +val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long -> BackupChapter( url = url, name = name, @@ -49,5 +51,6 @@ val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlat dateFetch = dateFetch, dateUpload = dateUpload, sourceOrder = source_order, + lastModifiedAt = lastModifiedAt, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index a38d45197..cbca81f49 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -39,6 +39,8 @@ data class BackupManga( @ProtoNumber(103) var viewer_flags: Int? = null, @ProtoNumber(104) var history: List = emptyList(), @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, + @ProtoNumber(106) var lastModifiedAt: Long = 0, + @ProtoNumber(107) var favoriteModifiedAt: Long? = 0, ) { fun getMangaImpl(): Manga { return Manga.create().copy( @@ -56,6 +58,8 @@ data class BackupManga( viewerFlags = (this@BackupManga.viewer_flags ?: this@BackupManga.viewer).toLong(), chapterFlags = this@BackupManga.chapterFlags.toLong(), updateStrategy = this@BackupManga.updateStrategy, + lastModifiedAt = this@BackupManga.lastModifiedAt, + favoriteModifiedAt = this@BackupManga.favoriteModifiedAt, ) } @@ -89,6 +93,8 @@ data class BackupManga( viewer_flags = manga.viewerFlags.toInt(), chapterFlags = manga.chapterFlags.toInt(), updateStrategy = manga.updateStrategy, + lastModifiedAt = manga.lastModifiedAt, + favoriteModifiedAt = manga.favoriteModifiedAt, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt index 8ca265d6b..be1d72b08 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt @@ -19,6 +19,8 @@ interface Chapter : SChapter, Serializable { var date_fetch: Long var source_order: Int + + var last_modified: Long } fun Chapter.toDomainChapter(): DomainChapter? { @@ -36,5 +38,6 @@ fun Chapter.toDomainChapter(): DomainChapter? { dateUpload = date_upload, chapterNumber = chapter_number, scanlator = scanlator, + lastModifiedAt = last_modified, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt index a1a2d3f55..58ba41dec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt @@ -26,6 +26,8 @@ class ChapterImpl : Chapter { override var source_order: Int = 0 + override var last_modified: Long = 0 + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt index 7b1888205..b91ccd7b4 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterMapper.kt @@ -2,8 +2,8 @@ package tachiyomi.data.chapter import tachiyomi.domain.chapter.model.Chapter -val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long) -> Chapter = - { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload -> +val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long, Long) -> Chapter = + { id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt -> Chapter( id = id, mangaId = mangaId, @@ -17,5 +17,6 @@ val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, dateUpload = dateUpload, chapterNumber = chapterNumber, scanlator = scanlator, + lastModifiedAt = lastModifiedAt, ) } diff --git a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt index 0c9191bf2..b021ff0c3 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt @@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga -val mangaMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long) -> Manga = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval -> +val mangaMapper: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Manga = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt -> Manga( id = id, source = source, @@ -27,11 +27,13 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List?, thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, + lastModifiedAt = lastModifiedAt, + favoriteModifiedAt = favoriteModifiedAt, ) } -val libraryManga: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga = - { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category -> +val libraryManga: (Long, Long, String, String?, String?, String?, List?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga = + { id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category -> LibraryManga( manga = mangaMapper( id, @@ -54,6 +56,8 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List?, dateAdded, updateStrategy, calculateInterval, + lastModifiedAt, + favoriteModifiedAt, ), category = category, totalChapters = totalCount, diff --git a/data/src/main/sqldelight/tachiyomi/data/chapters.sq b/data/src/main/sqldelight/tachiyomi/data/chapters.sq index 165916241..d5babe203 100644 --- a/data/src/main/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/main/sqldelight/tachiyomi/data/chapters.sq @@ -11,6 +11,7 @@ CREATE TABLE chapters( source_order INTEGER NOT NULL, date_fetch INTEGER AS Long NOT NULL, date_upload INTEGER AS Long NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); @@ -18,6 +19,15 @@ CREATE TABLE chapters( CREATE INDEX chapters_manga_id_index ON chapters(manga_id); CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0; +CREATE TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON chapters +FOR EACH ROW +BEGIN + UPDATE chapters + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getChapterById: SELECT * FROM chapters @@ -50,8 +60,8 @@ DELETE FROM chapters WHERE _id IN :chapterIds; insert: -INSERT INTO chapters(manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload) -VALUES (:mangaId,:url,:name,:scanlator,:read,:bookmark,:lastPageRead,:chapterNumber,:sourceOrder,:dateFetch,:dateUpload); +INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at) +VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now')); update: UPDATE chapters diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index b5661e061..1ed66298f 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -22,12 +22,31 @@ CREATE TABLE mangas( cover_last_modified INTEGER AS Long NOT NULL, date_added INTEGER AS Long NOT NULL, update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0, - calculate_interval INTEGER DEFAULT 0 NOT NULL + calculate_interval INTEGER DEFAULT 0 NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, + favorite_modified_at INTEGER AS Long ); CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1; CREATE INDEX mangas_url_index ON mangas(url); +CREATE TRIGGER update_favorite_modified_at_mangas +AFTER UPDATE OF favorite ON mangas +BEGIN + UPDATE mangas + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +CREATE TRIGGER update_last_modified_at_mangas +AFTER UPDATE ON mangas +FOR EACH ROW +BEGIN + UPDATE mangas + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + getMangaById: SELECT * FROM mangas @@ -45,6 +64,15 @@ SELECT * FROM mangas WHERE favorite = 1; +getAllManga: +SELECT * +FROM mangas; + +getMangasWithFavoriteTimestamp: +SELECT * +FROM mangas +WHERE favorite_modified_at IS NOT NULL; + getSourceIdWithFavoriteCount: SELECT source, @@ -81,8 +109,8 @@ DELETE FROM mangas WHERE favorite = 0 AND source IN :sourceIds; insert: -INSERT INTO mangas(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added,update_strategy,calculate_interval) -VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval); +INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at) +VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now')); update: UPDATE mangas SET diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq index a97c9d3ca..c10387a6a 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq @@ -2,15 +2,25 @@ CREATE TABLE mangas_categories( _id INTEGER NOT NULL PRIMARY KEY, manga_id INTEGER NOT NULL, category_id INTEGER NOT NULL, + last_modified_at INTEGER AS Long NOT NULL, FOREIGN KEY(category_id) REFERENCES categories (_id) ON DELETE CASCADE, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON mangas_categories +FOR EACH ROW +BEGIN + UPDATE mangas_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + insert: -INSERT INTO mangas_categories(manga_id, category_id) -VALUES (:mangaId, :categoryId); +INSERT INTO mangas_categories(manga_id, category_id, last_modified_at) +VALUES (:mangaId, :categoryId, strftime('%s', 'now')); deleteMangaCategoryByMangaId: DELETE FROM mangas_categories diff --git a/data/src/main/sqldelight/tachiyomi/migrations/25.sqm b/data/src/main/sqldelight/tachiyomi/migrations/25.sqm new file mode 100644 index 000000000..b4d98546a --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/migrations/25.sqm @@ -0,0 +1,49 @@ +ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; +ALTER TABLE mangas ADD COLUMN favorite_modified_at INTEGER AS Long; +ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; +ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; + +UPDATE mangas SET last_modified_at = strftime('%s', 'now'); +UPDATE mangas SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1; +UPDATE mangas_categories SET last_modified_at = strftime('%s', 'now'); +UPDATE chapters SET last_modified_at = strftime('%s', 'now'); + +-- Create triggers +DROP TRIGGER IF EXISTS update_last_modified_at_mangas; +CREATE TRIGGER update_last_modified_at_mangas +AFTER UPDATE ON mangas +FOR EACH ROW +BEGIN + UPDATE mangas + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas; +CREATE TRIGGER update_last_favorited_at_mangas +AFTER UPDATE OF favorite ON mangas +BEGIN + UPDATE mangas + SET favorite_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_chapters; +CREATE TRIGGER update_last_modified_at_chapters +AFTER UPDATE ON chapters +FOR EACH ROW +BEGIN + UPDATE chapters + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; + +DROP TRIGGER IF EXISTS update_last_modified_at_mangas_categories; +CREATE TRIGGER update_last_modified_at_mangas_categories +AFTER UPDATE ON mangas_categories +FOR EACH ROW +BEGIN + UPDATE mangas_categories + SET last_modified_at = strftime('%s', 'now') + WHERE _id = new._id; +END; \ No newline at end of file diff --git a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt index e11ca6564..9adee3f1b 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt @@ -13,6 +13,7 @@ data class Chapter( val dateUpload: Long, val chapterNumber: Float, val scanlator: String?, + val lastModifiedAt: Long, ) { val isRecognizedNumber: Boolean get() = chapterNumber >= 0f @@ -31,6 +32,7 @@ data class Chapter( dateUpload = -1, chapterNumber = -1f, scanlator = null, + lastModifiedAt = 0, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index df7885a92..6d53afa6a 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -24,6 +24,8 @@ data class Manga( val thumbnailUrl: String?, val updateStrategy: UpdateStrategy, val initialized: Boolean, + val lastModifiedAt: Long, + val favoriteModifiedAt: Long?, ) : Serializable { val sorting: Long @@ -109,6 +111,8 @@ data class Manga( thumbnailUrl = null, updateStrategy = UpdateStrategy.ALWAYS_UPDATE, initialized = false, + lastModifiedAt = 0L, + favoriteModifiedAt = 0L, ) } } From 9a817e49bed34e11e6b24b48d738d14583440fae Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 10 Jul 2023 16:44:48 -0400 Subject: [PATCH 20/25] Set proper defaults for new table columns --- app/build.gradle.kts | 3 +-- .../eu/kanade/tachiyomi/data/backup/models/BackupManga.kt | 2 +- data/src/main/sqldelight/tachiyomi/migrations/25.sqm | 6 +++--- domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e08346259..74477e52f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,9 +22,8 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi" - + versionCode = 104 - versionName = "0.14.6" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index cbca81f49..a3d0f4493 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -40,7 +40,7 @@ data class BackupManga( @ProtoNumber(104) var history: List = emptyList(), @ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE, @ProtoNumber(106) var lastModifiedAt: Long = 0, - @ProtoNumber(107) var favoriteModifiedAt: Long? = 0, + @ProtoNumber(107) var favoriteModifiedAt: Long? = null, ) { fun getMangaImpl(): Manga { return Manga.create().copy( diff --git a/data/src/main/sqldelight/tachiyomi/migrations/25.sqm b/data/src/main/sqldelight/tachiyomi/migrations/25.sqm index b4d98546a..0cd1c4c07 100644 --- a/data/src/main/sqldelight/tachiyomi/migrations/25.sqm +++ b/data/src/main/sqldelight/tachiyomi/migrations/25.sqm @@ -1,7 +1,7 @@ -ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; +ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; ALTER TABLE mangas ADD COLUMN favorite_modified_at INTEGER AS Long; -ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; -ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL; +ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; +ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0; UPDATE mangas SET last_modified_at = strftime('%s', 'now'); UPDATE mangas SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1; diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 6d53afa6a..22e953b58 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -112,7 +112,7 @@ data class Manga( updateStrategy = UpdateStrategy.ALWAYS_UPDATE, initialized = false, lastModifiedAt = 0L, - favoriteModifiedAt = 0L, + favoriteModifiedAt = null, ) } } From efabe801be56476bf9ee536747f39ab8d486ca12 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 10 Jul 2023 17:13:58 -0400 Subject: [PATCH 21/25] Refactor chapter tracking logic Could probably call this if we ever make it update tracking on manually marking chapters as read. --- .../java/eu/kanade/domain/DomainModule.kt | 2 + .../domain/track/interactor/TrackChapter.kt | 56 +++++++++++++++ .../tachiyomi/ui/reader/ReaderViewModel.kt | 71 +++---------------- 3 files changed, 69 insertions(+), 60 deletions(-) create mode 100644 app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index f3aa29464..58faf2914 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -16,6 +16,7 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSourcePin +import eu.kanade.domain.track.interactor.TrackChapter import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.history.HistoryRepositoryImpl @@ -109,6 +110,7 @@ class DomainModule : InjektModule { addFactory { GetApplicationRelease(get(), get()) } addSingletonFactory { TrackRepositoryImpl(get()) } + addFactory { TrackChapter(get(), get(), get(), get()) } addFactory { DeleteTrack(get()) } addFactory { GetTracksPerManga(get()) } addFactory { GetTracks(get()) } diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt new file mode 100644 index 000000000..57a49006a --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -0,0 +1,56 @@ +package eu.kanade.domain.track.interactor + +import android.content.Context +import eu.kanade.domain.track.model.toDbTrack +import eu.kanade.domain.track.service.DelayedTrackingUpdateJob +import eu.kanade.domain.track.store.DelayedTrackingStore +import eu.kanade.tachiyomi.data.track.TrackManager +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import logcat.LogPriority +import tachiyomi.core.util.lang.launchNonCancellable +import tachiyomi.core.util.system.logcat +import tachiyomi.domain.track.interactor.GetTracks +import tachiyomi.domain.track.interactor.InsertTrack + +class TrackChapter( + private val getTracks: GetTracks, + private val trackManager: TrackManager, + private val insertTrack: InsertTrack, + private val delayedTrackingStore: DelayedTrackingStore, +) { + + suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) = coroutineScope { + launchNonCancellable { + val tracks = getTracks.await(mangaId) + + if (tracks.isEmpty()) return@launchNonCancellable + + tracks.mapNotNull { track -> + val service = trackManager.getService(track.syncId) + if (service != null && service.isLogged && chapterNumber > track.lastChapterRead) { + val updatedTrack = track.copy(lastChapterRead = chapterNumber) + + async { + runCatching { + try { + service.update(updatedTrack.toDbTrack(), true) + insertTrack.await(updatedTrack) + } catch (e: Exception) { + delayedTrackingStore.addItem(updatedTrack) + DelayedTrackingUpdateJob.setupTask(context) + throw e + } + } + } + } else { + null + } + } + .awaitAll() + .mapNotNull { it.exceptionOrNull() } + .forEach { logcat(LogPriority.INFO, it) } + } + } +} 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 57c6d61df..284005d33 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 @@ -10,10 +10,8 @@ import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.manga.interactor.SetMangaViewerFlags import eu.kanade.domain.manga.model.orientationType import eu.kanade.domain.manga.model.readingModeType -import eu.kanade.domain.track.model.toDbTrack -import eu.kanade.domain.track.service.DelayedTrackingUpdateJob +import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.service.TrackPreferences -import eu.kanade.domain.track.store.DelayedTrackingStore import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadProvider @@ -21,7 +19,6 @@ import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.saver.Image import eu.kanade.tachiyomi.data.saver.ImageSaver import eu.kanade.tachiyomi.data.saver.Location -import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader @@ -42,11 +39,8 @@ import eu.kanade.tachiyomi.util.lang.byteSize import eu.kanade.tachiyomi.util.lang.takeBytes import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.cacheImageDir -import eu.kanade.tachiyomi.util.system.isOnline import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -75,8 +69,6 @@ import tachiyomi.domain.history.model.HistoryUpdate import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager -import tachiyomi.domain.track.interactor.GetTracks -import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -96,12 +88,10 @@ class ReaderViewModel( private val basePreferences: BasePreferences = Injekt.get(), private val downloadPreferences: DownloadPreferences = Injekt.get(), private val trackPreferences: TrackPreferences = Injekt.get(), - private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(), + private val trackChapter: TrackChapter = Injekt.get(), private val getManga: GetManga = Injekt.get(), private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val getNextChapters: GetNextChapters = Injekt.get(), - private val getTracks: GetTracks = Injekt.get(), - private val insertTrack: InsertTrack = Injekt.get(), private val upsertHistory: UpsertHistory = Injekt.get(), private val updateChapter: UpdateChapter = Injekt.get(), private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(), @@ -197,12 +187,6 @@ class ReaderViewModel( .map(::ReaderChapter) } - private var hasTrackers: Boolean = false - private val checkTrackers: (Manga) -> Unit = { manga -> - val tracks = runBlocking { getTracks.await(manga.id) } - hasTrackers = tracks.isNotEmpty() - } - private val incognitoMode = preferences.incognitoMode().get() private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get() @@ -258,8 +242,6 @@ class ReaderViewModel( mutableState.update { it.copy(manga = manga) } if (chapterId == -1L) chapterId = initialChapterId - checkTrackers(manga) - val context = Injekt.get() val source = sourceManager.getOrStub(manga.source) loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source) @@ -419,7 +401,7 @@ class ReaderViewModel( // Save last page read and mark as read if needed viewModelScope.launchNonCancellable { - updateChapterProgress(page.index) + updateChapterProgress(selectedChapter, page.index) } if (selectedChapter != getCurrentChapter()) { @@ -499,9 +481,7 @@ class ReaderViewModel( * Saves the chapter progress (last read page and whether it's read) * if incognito mode isn't on. */ - private suspend fun updateChapterProgress(pageIndex: Int) { - val readerChapter = getCurrentChapter() ?: return - + private suspend fun updateChapterProgress(readerChapter: ReaderChapter, pageIndex: Int) { mutableState.update { it.copy(currentPage = pageIndex + 1) } @@ -532,18 +512,19 @@ class ReaderViewModel( } fun flushReadTimer() { - viewModelScope.launchNonCancellable { - updateHistory() + getCurrentChapter()?.let { + viewModelScope.launchNonCancellable { + updateHistory(it) + } } } /** * Saves the chapter last read history if incognito mode isn't on. */ - private suspend fun updateHistory() { + private suspend fun updateHistory(readerChapter: ReaderChapter) { if (incognitoMode) return - val readerChapter = getCurrentChapter() ?: return val chapterId = readerChapter.chapter.id!! val endTime = Date() val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0 @@ -829,44 +810,14 @@ class ReaderViewModel( * will run in a background thread and errors are ignored. */ private fun updateTrackChapterRead(readerChapter: ReaderChapter) { - if (incognitoMode || !hasTrackers) return + if (incognitoMode) return if (!trackPreferences.autoUpdateTrack().get()) return val manga = manga ?: return - val chapterRead = readerChapter.chapter.chapter_number.toDouble() - - val trackManager = Injekt.get() val context = Injekt.get() viewModelScope.launchNonCancellable { - getTracks.await(manga.id) - .mapNotNull { track -> - val service = trackManager.getService(track.syncId) - if (service != null && service.isLogged && chapterRead > track.lastChapterRead) { - val updatedTrack = track.copy(lastChapterRead = chapterRead) - - // We want these to execute even if the presenter is destroyed and leaks - // for a while. The view can still be garbage collected. - async { - runCatching { - try { - if (!context.isOnline()) error("Couldn't update tracker as device is offline") - service.update(updatedTrack.toDbTrack(), true) - insertTrack.await(updatedTrack) - } catch (e: Exception) { - delayedTrackingStore.addItem(updatedTrack) - DelayedTrackingUpdateJob.setupTask(context) - throw e - } - } - } - } else { - null - } - } - .awaitAll() - .mapNotNull { it.exceptionOrNull() } - .forEach { logcat(LogPriority.INFO, it) } + trackChapter.await(context, manga.id, readerChapter.chapter.chapter_number.toDouble()) } } From 87bdee59908c73f340c892e43b522727e07b33d2 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 10 Jul 2023 17:25:52 -0400 Subject: [PATCH 22/25] Move SettingsItems composables to presentation-core --- .../eu/kanade/domain/manga/model/Manga.kt | 18 +-- .../presentation/components/SettingsItems.kt | 128 ------------------ .../library/LibrarySettingsDialog.kt | 6 +- .../manga/ChapterSettingsDialog.kt | 28 ++-- .../java/eu/kanade/tachiyomi/Migrations.kt | 11 +- .../source/browse/SourceFilterDialog.kt | 22 +-- .../ui/library/LibraryScreenModel.kt | 34 ++--- .../ui/library/LibrarySettingsScreenModel.kt | 4 +- .../tachiyomi/ui/manga/MangaScreenModel.kt | 26 ++-- .../tachiyomi/core/preference/TriState.kt | 16 +++ .../library/service/LibraryPreferences.kt | 24 ++-- .../tachiyomi/domain/manga/model/Manga.kt | 17 +-- .../tachiyomi/domain/manga/model/TriState.kt | 9 ++ .../domain/manga/model/TriStateFilter.kt | 22 --- presentation-core/build.gradle.kts | 2 + .../core/components/SettingsItems.kt | 121 ++++++++++++++++- 16 files changed, 238 insertions(+), 250 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt create mode 100644 core/src/main/java/tachiyomi/core/preference/TriState.kt create mode 100644 domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt delete mode 100644 domain/src/main/java/tachiyomi/domain/manga/model/TriStateFilter.kt diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 94c1353f9..ef8efa359 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -7,9 +7,9 @@ import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus +import tachiyomi.core.preference.TriState import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga -import tachiyomi.domain.manga.model.TriStateFilter import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -20,19 +20,19 @@ val Manga.readingModeType: Long val Manga.orientationType: Long get() = viewerFlags and OrientationType.MASK.toLong() -val Manga.downloadedFilter: TriStateFilter +val Manga.downloadedFilter: TriState get() { - if (forceDownloaded()) return TriStateFilter.ENABLED_IS + if (forceDownloaded()) return TriState.ENABLED_IS return when (downloadedFilterRaw) { - Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS - Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT - else -> TriStateFilter.DISABLED + Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS + Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT + else -> TriState.DISABLED } } fun Manga.chaptersFiltered(): Boolean { - return unreadFilter != TriStateFilter.DISABLED || - downloadedFilter != TriStateFilter.DISABLED || - bookmarkedFilter != TriStateFilter.DISABLED + return unreadFilter != TriState.DISABLED || + downloadedFilter != TriState.DISABLED || + bookmarkedFilter != TriState.DISABLED } fun Manga.forceDownloaded(): Boolean { return favorite && Injekt.get().downloadedOnly().get() diff --git a/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt b/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt deleted file mode 100644 index 57549e990..000000000 --- a/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt +++ /dev/null @@ -1,128 +0,0 @@ -package eu.kanade.presentation.components - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.ContentAlpha -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.CheckBox -import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank -import androidx.compose.material.icons.rounded.DisabledByDefault -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExposedDropdownMenuBox -import androidx.compose.material3.ExposedDropdownMenuDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import tachiyomi.domain.manga.model.TriStateFilter -import tachiyomi.presentation.core.components.SettingsItemsPaddings - -@Composable -fun TriStateItem( - label: String, - state: TriStateFilter, - enabled: Boolean = true, - onClick: ((TriStateFilter) -> Unit)?, -) { - Row( - modifier = Modifier - .clickable( - enabled = enabled && onClick != null, - onClick = { - when (state) { - TriStateFilter.DISABLED -> onClick?.invoke(TriStateFilter.ENABLED_IS) - TriStateFilter.ENABLED_IS -> onClick?.invoke(TriStateFilter.ENABLED_NOT) - TriStateFilter.ENABLED_NOT -> onClick?.invoke(TriStateFilter.DISABLED) - } - }, - ) - .fillMaxWidth() - .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled - - Icon( - imageVector = when (state) { - TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank - TriStateFilter.ENABLED_IS -> Icons.Rounded.CheckBox - TriStateFilter.ENABLED_NOT -> Icons.Rounded.DisabledByDefault - }, - contentDescription = null, - tint = if (!enabled || state == TriStateFilter.DISABLED) { - MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha) - } else { - when (onClick) { - null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled) - else -> MaterialTheme.colorScheme.primary - } - }, - ) - Text( - text = label, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha), - style = MaterialTheme.typography.bodyMedium, - ) - } -} - -@Composable -fun SelectItem( - label: String, - options: Array, - selectedIndex: Int, - onSelect: (Int) -> Unit, -) { - var expanded by remember { mutableStateOf(false) } - - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = !expanded }, - ) { - OutlinedTextField( - modifier = Modifier - .menuAnchor() - .fillMaxWidth() - .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), - label = { Text(text = label) }, - value = options[selectedIndex].toString(), - onValueChange = {}, - readOnly = true, - singleLine = true, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon( - expanded = expanded, - ) - }, - colors = ExposedDropdownMenuDefaults.textFieldColors(), - ) - - ExposedDropdownMenu( - modifier = Modifier.exposedDropdownSize(matchTextFieldWidth = true), - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - options.forEachIndexed { index, text -> - DropdownMenuItem( - text = { Text(text.toString()) }, - onClick = { - onSelect(index) - expanded = false - }, - ) - } - } - } -} diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 8e2531544..d91f8fc04 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -14,21 +14,21 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings -import eu.kanade.presentation.components.TriStateItem import eu.kanade.presentation.util.collectAsState import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel +import tachiyomi.core.preference.TriState import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.model.sort import tachiyomi.domain.library.service.LibraryPreferences -import tachiyomi.domain.manga.model.TriStateFilter import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.RadioItem import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SortItem +import tachiyomi.presentation.core.components.TriStateItem @Composable fun LibrarySettingsDialog( @@ -74,7 +74,7 @@ private fun ColumnScope.FilterPage( TriStateItem( label = stringResource(R.string.label_downloaded), state = if (downloadedOnly) { - TriStateFilter.ENABLED_IS + TriState.ENABLED_IS } else { filterDownloaded }, diff --git a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt index 665906e71..8b9ef54a3 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt @@ -27,20 +27,20 @@ import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.domain.manga.model.forceDownloaded import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings -import eu.kanade.presentation.components.TriStateItem import eu.kanade.tachiyomi.R +import tachiyomi.core.preference.TriState import tachiyomi.domain.manga.model.Manga -import tachiyomi.domain.manga.model.TriStateFilter import tachiyomi.presentation.core.components.RadioItem import tachiyomi.presentation.core.components.SortItem +import tachiyomi.presentation.core.components.TriStateItem @Composable fun ChapterSettingsDialog( onDismissRequest: () -> Unit, manga: Manga? = null, - onDownloadFilterChanged: (TriStateFilter) -> Unit, - onUnreadFilterChanged: (TriStateFilter) -> Unit, - onBookmarkedFilterChanged: (TriStateFilter) -> Unit, + onDownloadFilterChanged: (TriState) -> Unit, + onUnreadFilterChanged: (TriState) -> Unit, + onBookmarkedFilterChanged: (TriState) -> Unit, onSortModeChanged: (Long) -> Unit, onDisplayModeChanged: (Long) -> Unit, onSetAsDefault: (applyToExistingManga: Boolean) -> Unit, @@ -78,11 +78,11 @@ fun ChapterSettingsDialog( when (page) { 0 -> { FilterPage( - downloadFilter = manga?.downloadedFilter ?: TriStateFilter.DISABLED, + downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED, onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { manga?.forceDownloaded() == true }, - unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED, + unreadFilter = manga?.unreadFilter ?: TriState.DISABLED, onUnreadFilterChanged = onUnreadFilterChanged, - bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED, + bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED, onBookmarkedFilterChanged = onBookmarkedFilterChanged, ) } @@ -106,12 +106,12 @@ fun ChapterSettingsDialog( @Composable private fun ColumnScope.FilterPage( - downloadFilter: TriStateFilter, - onDownloadFilterChanged: ((TriStateFilter) -> Unit)?, - unreadFilter: TriStateFilter, - onUnreadFilterChanged: (TriStateFilter) -> Unit, - bookmarkedFilter: TriStateFilter, - onBookmarkedFilterChanged: (TriStateFilter) -> Unit, + downloadFilter: TriState, + onDownloadFilterChanged: ((TriState) -> Unit)?, + unreadFilter: TriState, + onUnreadFilterChanged: (TriState) -> Unit, + bookmarkedFilter: TriState, + onBookmarkedFilterChanged: (TriState) -> Unit, ) { TriStateItem( label = stringResource(R.string.label_downloaded), diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index cc9bd506e..c5a66afab 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.util.system.isReleaseBuildType import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.workManager import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.preference.TriState import tachiyomi.core.preference.getEnum import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED -import tachiyomi.domain.manga.model.TriStateFilter -import uy.kohesive.injekt.api.get import java.io.File object Migrations { @@ -350,12 +349,12 @@ object Migrations { remove(key) val newValue = when (pref.get()) { - 1 -> TriStateFilter.ENABLED_IS - 2 -> TriStateFilter.ENABLED_NOT - else -> TriStateFilter.DISABLED + 1 -> TriState.ENABLED_IS + 2 -> TriState.ENABLED_NOT + else -> TriState.DISABLED } - preferenceStore.getEnum("${key}_v2", TriStateFilter.DISABLED).set(newValue) + preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt index 20eb63a80..149194d93 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt @@ -16,17 +16,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.presentation.components.AdaptiveSheet -import eu.kanade.presentation.components.SelectItem -import eu.kanade.presentation.components.TriStateItem import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList -import tachiyomi.domain.manga.model.TriStateFilter +import tachiyomi.core.preference.TriState import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.HeadingItem +import tachiyomi.presentation.core.components.SelectItem import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TextItem +import tachiyomi.presentation.core.components.TriStateItem import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.Divider @@ -164,19 +164,19 @@ private fun FilterItem(filter: Filter<*>, onUpdate: () -> Unit) { } } -private fun Int.toTriStateFilter(): TriStateFilter { +private fun Int.toTriStateFilter(): TriState { return when (this) { - Filter.TriState.STATE_IGNORE -> TriStateFilter.DISABLED - Filter.TriState.STATE_INCLUDE -> TriStateFilter.ENABLED_IS - Filter.TriState.STATE_EXCLUDE -> TriStateFilter.ENABLED_NOT + Filter.TriState.STATE_IGNORE -> TriState.DISABLED + Filter.TriState.STATE_INCLUDE -> TriState.ENABLED_IS + Filter.TriState.STATE_EXCLUDE -> TriState.ENABLED_NOT else -> throw IllegalStateException("Unknown TriState state: $this") } } -private fun TriStateFilter.toTriStateInt(): Int { +private fun TriState.toTriStateInt(): Int { return when (this) { - TriStateFilter.DISABLED -> Filter.TriState.STATE_IGNORE - TriStateFilter.ENABLED_IS -> Filter.TriState.STATE_INCLUDE - TriStateFilter.ENABLED_NOT -> Filter.TriState.STATE_EXCLUDE + TriState.DISABLED -> Filter.TriState.STATE_IGNORE + TriState.ENABLED_IS -> Filter.TriState.STATE_INCLUDE + TriState.ENABLED_NOT -> Filter.TriState.STATE_EXCLUDE } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index fa982d322..5649285b2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import tachiyomi.core.preference.CheckboxState +import tachiyomi.core.preference.TriState import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.withIOContext @@ -57,7 +58,6 @@ import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.interactor.GetLibraryManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaUpdate -import tachiyomi.domain.manga.model.TriStateFilter import tachiyomi.domain.manga.model.applyFilter import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.GetTracksPerManga @@ -153,7 +153,7 @@ class LibraryScreenModel( prefs.filterBookmarked, prefs.filterCompleted, ) + trackFilter.values - ).any { it != TriStateFilter.DISABLED } + ).any { it != TriState.DISABLED } } .distinctUntilChanged() .onEach { @@ -169,12 +169,12 @@ class LibraryScreenModel( */ private suspend fun LibraryMap.applyFilters( trackMap: Map>, - loggedInTrackServices: Map, + loggedInTrackServices: Map, ): LibraryMap { val prefs = getLibraryItemPreferencesFlow().first() val downloadedOnly = prefs.globalFilterDownloaded val filterDownloaded = - if (downloadedOnly) TriStateFilter.ENABLED_IS else prefs.filterDownloaded + if (downloadedOnly) TriState.ENABLED_IS else prefs.filterDownloaded val filterUnread = prefs.filterUnread val filterStarted = prefs.filterStarted val filterBookmarked = prefs.filterBookmarked @@ -182,8 +182,8 @@ class LibraryScreenModel( val isNotLoggedInAnyTrack = loggedInTrackServices.isEmpty() - val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriStateFilter.ENABLED_NOT) it.key else null } - val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriStateFilter.ENABLED_IS) it.key else null } + val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null } + val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null } val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty() val filterFnDownloaded: (LibraryItem) -> Boolean = { @@ -308,11 +308,11 @@ class LibraryScreenModel( localBadge = it[1] as Boolean, languageBadge = it[2] as Boolean, globalFilterDownloaded = it[3] as Boolean, - filterDownloaded = it[4] as TriStateFilter, - filterUnread = it[5] as TriStateFilter, - filterStarted = it[6] as TriStateFilter, - filterBookmarked = it[7] as TriStateFilter, - filterCompleted = it[8] as TriStateFilter, + filterDownloaded = it[4] as TriState, + filterUnread = it[5] as TriState, + filterStarted = it[6] as TriState, + filterBookmarked = it[7] as TriState, + filterCompleted = it[8] as TriState, ) }, ) @@ -365,7 +365,7 @@ class LibraryScreenModel( * * @return map of track id with the filter value */ - private fun getTrackingFilterFlow(): Flow> { + private fun getTrackingFilterFlow(): Flow> { val loggedServices = trackManager.services.filter { it.isLogged } return if (loggedServices.isNotEmpty()) { val prefFlows = loggedServices @@ -670,11 +670,11 @@ class LibraryScreenModel( val languageBadge: Boolean, val globalFilterDownloaded: Boolean, - val filterDownloaded: TriStateFilter, - val filterUnread: TriStateFilter, - val filterStarted: TriStateFilter, - val filterBookmarked: TriStateFilter, - val filterCompleted: TriStateFilter, + val filterDownloaded: TriState, + val filterUnread: TriState, + val filterStarted: TriState, + val filterBookmarked: TriState, + val filterCompleted: TriState, ) @Immutable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt index 78badbede..12db1e289 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt @@ -6,6 +6,7 @@ import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.util.preference.toggle import tachiyomi.core.preference.Preference +import tachiyomi.core.preference.TriState import tachiyomi.core.preference.getAndSet import tachiyomi.core.util.lang.launchIO import tachiyomi.domain.category.interactor.SetDisplayMode @@ -14,7 +15,6 @@ import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.service.LibraryPreferences -import tachiyomi.domain.manga.model.TriStateFilter import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -33,7 +33,7 @@ class LibrarySettingsScreenModel( preference(libraryPreferences).toggle() } - fun toggleFilter(preference: (LibraryPreferences) -> Preference) { + fun toggleFilter(preference: (LibraryPreferences) -> Preference) { preference(libraryPreferences).getAndSet { it.next() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 5f78f9726..ca8bf5c63 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -46,6 +46,7 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import logcat.LogPriority import tachiyomi.core.preference.CheckboxState +import tachiyomi.core.preference.TriState import tachiyomi.core.preference.mapAsCheckboxState import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchNonCancellable @@ -67,7 +68,6 @@ import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga import tachiyomi.domain.manga.interactor.GetMangaWithChapters import tachiyomi.domain.manga.interactor.SetMangaChapterFlags import tachiyomi.domain.manga.model.Manga -import tachiyomi.domain.manga.model.TriStateFilter import tachiyomi.domain.manga.model.applyFilter import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.GetTracks @@ -743,13 +743,13 @@ class MangaInfoScreenModel( * Sets the read filter and requests an UI update. * @param state whether to display only unread chapters or all chapters. */ - fun setUnreadFilter(state: TriStateFilter) { + fun setUnreadFilter(state: TriState) { val manga = successState?.manga ?: return val flag = when (state) { - TriStateFilter.DISABLED -> Manga.SHOW_ALL - TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_UNREAD - TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_READ + TriState.DISABLED -> Manga.SHOW_ALL + TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_UNREAD + TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_READ } coroutineScope.launchNonCancellable { setMangaChapterFlags.awaitSetUnreadFilter(manga, flag) @@ -760,13 +760,13 @@ class MangaInfoScreenModel( * Sets the download filter and requests an UI update. * @param state whether to display only downloaded chapters or all chapters. */ - fun setDownloadedFilter(state: TriStateFilter) { + fun setDownloadedFilter(state: TriState) { val manga = successState?.manga ?: return val flag = when (state) { - TriStateFilter.DISABLED -> Manga.SHOW_ALL - TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_DOWNLOADED - TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED + TriState.DISABLED -> Manga.SHOW_ALL + TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_DOWNLOADED + TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED } coroutineScope.launchNonCancellable { @@ -778,13 +778,13 @@ class MangaInfoScreenModel( * Sets the bookmark filter and requests an UI update. * @param state whether to display only bookmarked chapters or all chapters. */ - fun setBookmarkedFilter(state: TriStateFilter) { + fun setBookmarkedFilter(state: TriState) { val manga = successState?.manga ?: return val flag = when (state) { - TriStateFilter.DISABLED -> Manga.SHOW_ALL - TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_BOOKMARKED - TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED + TriState.DISABLED -> Manga.SHOW_ALL + TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_BOOKMARKED + TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED } coroutineScope.launchNonCancellable { diff --git a/core/src/main/java/tachiyomi/core/preference/TriState.kt b/core/src/main/java/tachiyomi/core/preference/TriState.kt new file mode 100644 index 000000000..68b9173ce --- /dev/null +++ b/core/src/main/java/tachiyomi/core/preference/TriState.kt @@ -0,0 +1,16 @@ +package tachiyomi.core.preference + +enum class TriState { + DISABLED, // Disable filter + ENABLED_IS, // Enabled with "is" filter + ENABLED_NOT, // Enabled with "not" filter + ; + + fun next(): TriState { + return when (this) { + DISABLED -> ENABLED_IS + ENABLED_IS -> ENABLED_NOT + ENABLED_NOT -> DISABLED + } + } +} diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt index 7aa496c54..efec8755b 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -1,11 +1,11 @@ package tachiyomi.domain.library.service import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.preference.TriState import tachiyomi.core.preference.getEnum import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.manga.model.Manga -import tachiyomi.domain.manga.model.TriStateFilter class LibraryPreferences( private val preferenceStore: PreferenceStore, @@ -49,27 +49,27 @@ class LibraryPreferences( // region Filter - fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriStateFilter.DISABLED) + fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriState.DISABLED) - fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriStateFilter.DISABLED) + fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriState.DISABLED) - fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriStateFilter.DISABLED) + fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriState.DISABLED) - fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriStateFilter.DISABLED) + fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriState.DISABLED) - fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriStateFilter.DISABLED) + fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriState.DISABLED) - fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriStateFilter.DISABLED) + fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriState.DISABLED) - fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriStateFilter.DISABLED) + fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriState.DISABLED) - fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriStateFilter.DISABLED) + fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriState.DISABLED) - fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriStateFilter.DISABLED) + fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriState.DISABLED) - fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriStateFilter.DISABLED) + fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriState.DISABLED) - fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriStateFilter.DISABLED) + fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriState.DISABLED) // endregion diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 22e953b58..41db5f827 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -1,6 +1,7 @@ package tachiyomi.domain.manga.model import eu.kanade.tachiyomi.source.model.UpdateStrategy +import tachiyomi.core.preference.TriState import java.io.Serializable data class Manga( @@ -43,18 +44,18 @@ data class Manga( val bookmarkedFilterRaw: Long get() = chapterFlags and CHAPTER_BOOKMARKED_MASK - val unreadFilter: TriStateFilter + val unreadFilter: TriState get() = when (unreadFilterRaw) { - CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS - CHAPTER_SHOW_READ -> TriStateFilter.ENABLED_NOT - else -> TriStateFilter.DISABLED + CHAPTER_SHOW_UNREAD -> TriState.ENABLED_IS + CHAPTER_SHOW_READ -> TriState.ENABLED_NOT + else -> TriState.DISABLED } - val bookmarkedFilter: TriStateFilter + val bookmarkedFilter: TriState get() = when (bookmarkedFilterRaw) { - CHAPTER_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS - CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT - else -> TriStateFilter.DISABLED + CHAPTER_SHOW_BOOKMARKED -> TriState.ENABLED_IS + CHAPTER_SHOW_NOT_BOOKMARKED -> TriState.ENABLED_NOT + else -> TriState.DISABLED } fun sortDescending(): Boolean { diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt b/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt new file mode 100644 index 000000000..75a21f959 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt @@ -0,0 +1,9 @@ +package tachiyomi.domain.manga.model + +import tachiyomi.core.preference.TriState + +inline fun applyFilter(filter: TriState, predicate: () -> Boolean): Boolean = when (filter) { + TriState.DISABLED -> true + TriState.ENABLED_IS -> predicate() + TriState.ENABLED_NOT -> !predicate() +} diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/TriStateFilter.kt b/domain/src/main/java/tachiyomi/domain/manga/model/TriStateFilter.kt deleted file mode 100644 index 8a8631683..000000000 --- a/domain/src/main/java/tachiyomi/domain/manga/model/TriStateFilter.kt +++ /dev/null @@ -1,22 +0,0 @@ -package tachiyomi.domain.manga.model - -enum class TriStateFilter { - DISABLED, // Disable filter - ENABLED_IS, // Enabled with "is" filter - ENABLED_NOT, // Enabled with "not" filter - ; - - fun next(): TriStateFilter { - return when (this) { - DISABLED -> ENABLED_IS - ENABLED_IS -> ENABLED_NOT - ENABLED_NOT -> DISABLED - } - } -} - -inline fun applyFilter(filter: TriStateFilter, predicate: () -> Boolean): Boolean = when (filter) { - TriStateFilter.DISABLED -> true - TriStateFilter.ENABLED_IS -> predicate() - TriStateFilter.ENABLED_NOT -> !predicate() -} diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index 53025fdaa..9c87d7465 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -21,6 +21,8 @@ android { } dependencies { + implementation(project(":core")) + // Compose implementation(platform(compose.bom)) implementation(compose.activity) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt index fae6c8382..d0442ae54 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt @@ -10,10 +10,17 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.material.ContentAlpha import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.material.icons.filled.ArrowUpward +import androidx.compose.material.icons.rounded.CheckBox +import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank +import androidx.compose.material.icons.rounded.DisabledByDefault import androidx.compose.material3.Checkbox +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField @@ -21,11 +28,16 @@ import androidx.compose.material3.RadioButton import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import tachiyomi.core.preference.TriState import tachiyomi.presentation.core.theme.header object SettingsItemsPaddings { @@ -175,22 +187,99 @@ fun SliderItem( } @Composable -private fun BaseSettingsItem( +fun SelectItem( label: String, - widget: @Composable RowScope.() -> Unit, - onClick: () -> Unit, + options: Array, + selectedIndex: Int, + onSelect: (Int) -> Unit, +) { + var expanded by remember { mutableStateOf(false) } + + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = !expanded }, + ) { + OutlinedTextField( + modifier = Modifier + .menuAnchor() + .fillMaxWidth() + .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + label = { Text(text = label) }, + value = options[selectedIndex].toString(), + onValueChange = {}, + readOnly = true, + singleLine = true, + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon( + expanded = expanded, + ) + }, + colors = ExposedDropdownMenuDefaults.textFieldColors(), + ) + + ExposedDropdownMenu( + modifier = Modifier.exposedDropdownSize(matchTextFieldWidth = true), + expanded = expanded, + onDismissRequest = { expanded = false }, + ) { + options.forEachIndexed { index, text -> + DropdownMenuItem( + text = { Text(text.toString()) }, + onClick = { + onSelect(index) + expanded = false + }, + ) + } + } + } +} + +@Composable +fun TriStateItem( + label: String, + state: TriState, + enabled: Boolean = true, + onClick: ((TriState) -> Unit)?, ) { Row( modifier = Modifier - .clickable(onClick = onClick) + .clickable( + enabled = enabled && onClick != null, + onClick = { + when (state) { + TriState.DISABLED -> onClick?.invoke(TriState.ENABLED_IS) + TriState.ENABLED_IS -> onClick?.invoke(TriState.ENABLED_NOT) + TriState.ENABLED_NOT -> onClick?.invoke(TriState.DISABLED) + } + }, + ) .fillMaxWidth() .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), ) { - widget(this) + val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled + + Icon( + imageVector = when (state) { + TriState.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank + TriState.ENABLED_IS -> Icons.Rounded.CheckBox + TriState.ENABLED_NOT -> Icons.Rounded.DisabledByDefault + }, + contentDescription = null, + tint = if (!enabled || state == TriState.DISABLED) { + MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha) + } else { + when (onClick) { + null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled) + else -> MaterialTheme.colorScheme.primary + } + }, + ) Text( text = label, + color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha), style = MaterialTheme.typography.bodyMedium, ) } @@ -212,3 +301,25 @@ fun TextItem( singleLine = true, ) } + +@Composable +private fun BaseSettingsItem( + label: String, + widget: @Composable RowScope.() -> Unit, + onClick: () -> Unit, +) { + Row( + modifier = Modifier + .clickable(onClick = onClick) + .fillMaxWidth() + .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + widget(this) + Text( + text = label, + style = MaterialTheme.typography.bodyMedium, + ) + } +} From 710ebfb7a5e3b57a59103a50da462be1259f0d3b Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 10 Jul 2023 18:42:35 -0400 Subject: [PATCH 23/25] Initial migration of general reader settings to Compose --- .../presentation/components/TabbedDialog.kt | 3 +- .../reader/settings/ColorFilterPage.kt | 158 +++++++++++++++++ .../reader/settings/GeneralSettingsPage.kt | 96 ++++++++++ .../reader/settings/ReaderSettingsDialog.kt | 66 +++++++ .../reader/settings/ReadingModePage.kt | 10 ++ .../tachiyomi/ui/reader/ReaderActivity.kt | 30 ++-- .../tachiyomi/ui/reader/ReaderViewModel.kt | 6 +- .../reader/setting/ReaderColorFilterDialog.kt | 164 ------------------ .../reader/setting/ReaderGeneralSettings.kt | 53 ------ .../setting/ReaderSettingsScreenModel.kt | 16 ++ .../ui/reader/setting/ReaderSettingsSheet.kt | 1 - .../res/drawable/ic_brightness_5_24dp.xml | 9 - app/src/main/res/layout/reader_activity.xml | 12 +- .../res/layout/reader_general_settings.xml | 87 ---------- app/src/main/res/values/arrays.xml | 14 -- i18n/src/main/res/values/strings.xml | 2 +- 16 files changed, 373 insertions(+), 354 deletions(-) create mode 100644 app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt create mode 100644 app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt create mode 100644 app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt create mode 100644 app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterDialog.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt delete mode 100644 app/src/main/res/drawable/ic_brightness_5_24dp.xml delete mode 100644 app/src/main/res/layout/reader_general_settings.xml diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt index e13638ac2..6e494f275 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert @@ -42,13 +43,13 @@ fun TabbedDialog( onDismissRequest: () -> Unit, tabTitles: List, tabOverflowMenuContent: (@Composable ColumnScope.(() -> Unit) -> Unit)? = null, + pagerState: PagerState = rememberPagerState { tabTitles.size }, content: @Composable (Int) -> Unit, ) { AdaptiveSheet( onDismissRequest = onDismissRequest, ) { val scope = rememberCoroutineScope() - val pagerState = rememberPagerState { tabTitles.size } Column { Row { diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt new file mode 100644 index 000000000..06bbb37b1 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt @@ -0,0 +1,158 @@ +package eu.kanade.presentation.reader.settings + +import android.os.Build +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.res.stringResource +import androidx.core.graphics.alpha +import androidx.core.graphics.blue +import androidx.core.graphics.green +import androidx.core.graphics.red +import eu.kanade.presentation.util.collectAsState +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences +import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel +import tachiyomi.core.preference.getAndSet +import tachiyomi.presentation.core.components.CheckboxItem +import tachiyomi.presentation.core.components.SelectItem +import tachiyomi.presentation.core.components.SliderItem + +@Composable +internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) { + val colorFilterModes = buildList { + addAll( + listOf( + R.string.label_default, + R.string.filter_mode_multiply, + R.string.filter_mode_screen, + ), + ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + addAll( + listOf( + R.string.filter_mode_overlay, + R.string.filter_mode_lighten, + R.string.filter_mode_darken, + ), + ) + } + }.map { stringResource(it) } + + val customBrightness by screenModel.preferences.customBrightness().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_custom_brightness), + checked = customBrightness, + onClick = { + screenModel.togglePreference(ReaderPreferences::customBrightness) + }, + ) + + /** + * Sets the brightness of the screen. Range is [-75, 100]. + * From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness. + * From 1 to 100 it sets that value as brightness. + * 0 sets system brightness and hides the overlay. + */ + if (customBrightness) { + val customBrightnessValue by screenModel.preferences.customBrightnessValue().collectAsState() + SliderItem( + label = stringResource(R.string.pref_custom_brightness), + min = -75, + max = 100, + value = customBrightnessValue, + valueText = customBrightnessValue.toString(), + onChange = { screenModel.preferences.customBrightnessValue().set(it) }, + ) + } + + val colorFilter by screenModel.preferences.colorFilter().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_custom_color_filter), + checked = colorFilter, + onClick = { + screenModel.togglePreference(ReaderPreferences::colorFilter) + }, + ) + if (colorFilter) { + val colorFilterValue by screenModel.preferences.colorFilterValue().collectAsState() + SliderItem( + label = stringResource(R.string.color_filter_r_value), + max = 255, + value = colorFilterValue.red, + valueText = colorFilterValue.red.toString(), + onChange = { newRValue -> + screenModel.preferences.colorFilterValue().getAndSet { + getColorValue(it, newRValue, RED_MASK, 16) + } + }, + ) + SliderItem( + label = stringResource(R.string.color_filter_g_value), + max = 255, + value = colorFilterValue.green, + valueText = colorFilterValue.green.toString(), + onChange = { newGValue -> + screenModel.preferences.colorFilterValue().getAndSet { + getColorValue(it, newGValue, GREEN_MASK, 8) + } + }, + ) + SliderItem( + label = stringResource(R.string.color_filter_b_value), + max = 255, + value = colorFilterValue.blue, + valueText = colorFilterValue.blue.toString(), + onChange = { newBValue -> + screenModel.preferences.colorFilterValue().getAndSet { + getColorValue(it, newBValue, BLUE_MASK, 0) + } + }, + ) + SliderItem( + label = stringResource(R.string.color_filter_a_value), + max = 255, + value = colorFilterValue.alpha, + valueText = colorFilterValue.alpha.toString(), + onChange = { newAValue -> + screenModel.preferences.colorFilterValue().getAndSet { + getColorValue(it, newAValue, ALPHA_MASK, 24) + } + }, + ) + + val colorFilterMode by screenModel.preferences.colorFilterMode().collectAsState() + SelectItem( + label = stringResource(R.string.pref_color_filter_mode), + options = colorFilterModes.toTypedArray(), + selectedIndex = colorFilterMode, + ) { + screenModel.preferences.colorFilterMode().set(it) + } + } + + val grayscale by screenModel.preferences.grayscale().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_grayscale), + checked = grayscale, + onClick = { + screenModel.togglePreference(ReaderPreferences::grayscale) + }, + ) + val invertedColors by screenModel.preferences.invertedColors().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_inverted_colors), + checked = invertedColors, + onClick = { + screenModel.togglePreference(ReaderPreferences::invertedColors) + }, + ) +} + +private fun getColorValue(currentColor: Int, color: Int, mask: Long, bitShift: Int): Int { + return (color shl bitShift) or (currentColor and mask.inv().toInt()) +} +private const val ALPHA_MASK: Long = 0xFF000000 +private const val RED_MASK: Long = 0x00FF0000 +private const val GREEN_MASK: Long = 0x0000FF00 +private const val BLUE_MASK: Long = 0x000000FF diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt new file mode 100644 index 000000000..43eea7648 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt @@ -0,0 +1,96 @@ +package eu.kanade.presentation.reader.settings + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.res.stringResource +import eu.kanade.presentation.util.collectAsState +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences +import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel +import tachiyomi.presentation.core.components.CheckboxItem +import tachiyomi.presentation.core.components.HeadingItem +import tachiyomi.presentation.core.components.RadioItem + +@Composable +internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { + // TODO: show this in a nicer way + HeadingItem(stringResource(R.string.pref_reader_theme)) + val readerTheme by screenModel.preferences.readerTheme().collectAsState() + listOf( + R.string.black_background to 1, + R.string.gray_background to 2, + R.string.white_background to 0, + R.string.automatic_background to 3, + ).map { (titleRes, theme) -> + RadioItem( + label = stringResource(titleRes), + selected = readerTheme == theme, + onClick = { screenModel.preferences.readerTheme().set(theme) }, + ) + } + + val showPageNumber by screenModel.preferences.showPageNumber().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_show_page_number), + checked = showPageNumber, + onClick = { + screenModel.togglePreference(ReaderPreferences::showPageNumber) + }, + ) + + val fullscreen by screenModel.preferences.fullscreen().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_fullscreen), + checked = fullscreen, + onClick = { + screenModel.togglePreference(ReaderPreferences::fullscreen) + }, + ) + + // TODO: hide if there's no cutout + val cutoutShort by screenModel.preferences.cutoutShort().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_cutout_short), + checked = cutoutShort, + onClick = { + screenModel.togglePreference(ReaderPreferences::cutoutShort) + }, + ) + + val keepScreenOn by screenModel.preferences.keepScreenOn().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_keep_screen_on), + checked = keepScreenOn, + onClick = { + screenModel.togglePreference(ReaderPreferences::keepScreenOn) + }, + ) + + val readWithLongTap by screenModel.preferences.readWithLongTap().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_read_with_long_tap), + checked = readWithLongTap, + onClick = { + screenModel.togglePreference(ReaderPreferences::readWithLongTap) + }, + ) + + val alwaysShowChapterTransition by screenModel.preferences.alwaysShowChapterTransition().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_always_show_chapter_transition), + checked = alwaysShowChapterTransition, + onClick = { + screenModel.togglePreference(ReaderPreferences::alwaysShowChapterTransition) + }, + ) + + val pageTransitions by screenModel.preferences.pageTransitions().collectAsState() + CheckboxItem( + label = stringResource(R.string.pref_page_transitions), + checked = pageTransitions, + onClick = { + screenModel.togglePreference(ReaderPreferences::pageTransitions) + }, + ) +} diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt new file mode 100644 index 000000000..44f1bc27f --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReaderSettingsDialog.kt @@ -0,0 +1,66 @@ +package eu.kanade.presentation.reader.settings + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.window.DialogWindowProvider +import eu.kanade.presentation.components.TabbedDialog +import eu.kanade.presentation.components.TabbedDialogPaddings +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel + +@Composable +fun ReaderSettingsDialog( + onDismissRequest: () -> Unit, + onShowMenus: () -> Unit, + onHideMenus: () -> Unit, + screenModel: ReaderSettingsScreenModel, +) { + // TODO: undimming doesn't seem to work + val window = (LocalView.current.parent as? DialogWindowProvider)?.window + + val tabTitles = listOf( + stringResource(R.string.pref_category_reading_mode), + stringResource(R.string.pref_category_general), + stringResource(R.string.custom_filter), + ) + val pagerState = rememberPagerState { tabTitles.size } + + LaunchedEffect(pagerState.currentPage) { + if (pagerState.currentPage == 2) { + window?.setDimAmount(0f) + onHideMenus() + } else { + window?.setDimAmount(0.75f) + onShowMenus() + } + } + + TabbedDialog( + onDismissRequest = { + onDismissRequest() + onShowMenus() + }, + tabTitles = tabTitles, + pagerState = pagerState, + ) { page -> + Column( + modifier = Modifier + .padding(vertical = TabbedDialogPaddings.Vertical) + .verticalScroll(rememberScrollState()), + ) { + when (page) { + 0 -> ReadingModePage(screenModel) + 1 -> GeneralPage(screenModel) + 2 -> ColorFilterPage(screenModel) + } + } + } +} diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt new file mode 100644 index 000000000..0cf0ed29c --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -0,0 +1,10 @@ +package eu.kanade.presentation.reader.settings + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.runtime.Composable +import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel + +@Composable +internal fun ColumnScope.ReadingModePage(screenModel: ReaderSettingsScreenModel) { + // TODO +} 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 a7cc20707..a8acff615 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 @@ -31,6 +31,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -51,6 +52,7 @@ import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.manga.model.orientationType import eu.kanade.presentation.reader.ChapterNavigator import eu.kanade.presentation.reader.PageIndicatorText +import eu.kanade.presentation.reader.settings.ReaderSettingsDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications @@ -65,8 +67,8 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.setting.OrientationType -import eu.kanade.tachiyomi.ui.reader.setting.ReaderColorFilterDialog import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences +import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator @@ -389,6 +391,8 @@ class ReaderActivity : BaseActivity() { binding.dialogRoot.setComposeContent { val state by viewModel.state.collectAsState() + val settingsScreenModel = remember { ReaderSettingsScreenModel() } + val onDismissRequest = viewModel::closeDialog when (state.dialog) { is ReaderViewModel.Dialog.Loading -> { @@ -406,14 +410,12 @@ class ReaderActivity : BaseActivity() { }, ) } - is ReaderViewModel.Dialog.ColorFilter -> { - setMenuVisibility(false) - ReaderColorFilterDialog( - onDismissRequest = { - onDismissRequest() - setMenuVisibility(true) - }, - readerPreferences = viewModel.readerPreferences, + is ReaderViewModel.Dialog.Settings -> { + ReaderSettingsDialog( + onDismissRequest = onDismissRequest, + onShowMenus = { setMenuVisibility(true) }, + onHideMenus = { setMenuVisibility(false) }, + screenModel = settingsScreenModel, ) } is ReaderViewModel.Dialog.PageActions -> { @@ -546,7 +548,7 @@ class ReaderActivity : BaseActivity() { } // Settings sheet - with(binding.actionSettings) { + with(binding.actionSettingsLegacy) { setTooltip(R.string.action_settings) var readerSettingSheet: ReaderSettingsSheet? = null @@ -556,13 +558,11 @@ class ReaderActivity : BaseActivity() { readerSettingSheet = ReaderSettingsSheet(this@ReaderActivity).apply { show() } } } - - // Color filter sheet - with(binding.actionColorSettings) { - setTooltip(R.string.custom_filter) + with(binding.actionSettings) { + setTooltip(R.string.action_settings) setOnClickListener { - viewModel.openColorFilterDialog() + viewModel.openSettingsDialog() } } } 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 284005d33..7a7a313a6 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 @@ -685,8 +685,8 @@ class ReaderViewModel( mutableState.update { it.copy(dialog = Dialog.PageActions(page)) } } - fun openColorFilterDialog() { - mutableState.update { it.copy(dialog = Dialog.ColorFilter) } + fun openSettingsDialog() { + mutableState.update { it.copy(dialog = Dialog.Settings) } } fun closeDialog() { @@ -863,7 +863,7 @@ class ReaderViewModel( sealed class Dialog { object Loading : Dialog() - object ColorFilter : Dialog() + object Settings : Dialog() data class PageActions(val page: ReaderPage) : Dialog() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterDialog.kt deleted file mode 100644 index db7a901d4..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterDialog.kt +++ /dev/null @@ -1,164 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader.setting - -import android.os.Build -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.LocalView -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.compose.ui.window.DialogWindowProvider -import androidx.core.graphics.alpha -import androidx.core.graphics.blue -import androidx.core.graphics.green -import androidx.core.graphics.red -import eu.kanade.presentation.components.AdaptiveSheet -import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight -import eu.kanade.presentation.more.settings.Preference -import eu.kanade.presentation.more.settings.PreferenceScreen -import eu.kanade.presentation.util.collectAsState -import eu.kanade.tachiyomi.R -import tachiyomi.core.preference.getAndSet - -@Composable -fun ReaderColorFilterDialog( - onDismissRequest: () -> Unit, - readerPreferences: ReaderPreferences, -) { - val colorFilterModes = buildList { - addAll( - listOf( - R.string.label_default, - R.string.filter_mode_multiply, - R.string.filter_mode_screen, - ), - ) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - addAll( - listOf( - R.string.filter_mode_overlay, - R.string.filter_mode_lighten, - R.string.filter_mode_darken, - ), - ) - } - }.map { stringResource(it) } - - val customBrightness by readerPreferences.customBrightness().collectAsState() - val customBrightnessValue by readerPreferences.customBrightnessValue().collectAsState() - val colorFilter by readerPreferences.colorFilter().collectAsState() - val colorFilterValue by readerPreferences.colorFilterValue().collectAsState() - val colorFilterMode by readerPreferences.colorFilterMode().collectAsState() - - AdaptiveSheet( - onDismissRequest = onDismissRequest, - ) { - (LocalView.current.parent as? DialogWindowProvider)?.window?.setDimAmount(0f) - - CompositionLocalProvider( - LocalPreferenceMinHeight provides 48.dp, - ) { - PreferenceScreen( - items = listOfNotNull( - Preference.PreferenceItem.SwitchPreference( - pref = readerPreferences.customBrightness(), - title = stringResource(R.string.pref_custom_brightness), - ), - /** - * Sets the brightness of the screen. Range is [-75, 100]. - * From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness. - * From 1 to 100 it sets that value as brightness. - * 0 sets system brightness and hides the overlay. - */ - Preference.PreferenceItem.SliderPreference( - value = customBrightnessValue, - title = stringResource(R.string.pref_custom_brightness), - min = -75, - max = 100, - onValueChanged = { - readerPreferences.customBrightnessValue().set(it) - true - }, - ).takeIf { customBrightness }, - - Preference.PreferenceItem.SwitchPreference( - pref = readerPreferences.colorFilter(), - title = stringResource(R.string.pref_custom_color_filter), - ), - Preference.PreferenceItem.SliderPreference( - value = colorFilterValue.red, - title = stringResource(R.string.color_filter_r_value), - max = 255, - onValueChanged = { newRValue -> - readerPreferences.colorFilterValue().getAndSet { - getColorValue(it, newRValue, RED_MASK, 16) - } - true - }, - ).takeIf { colorFilter }, - Preference.PreferenceItem.SliderPreference( - value = colorFilterValue.green, - title = stringResource(R.string.color_filter_g_value), - max = 255, - onValueChanged = { newRValue -> - readerPreferences.colorFilterValue().getAndSet { - getColorValue(it, newRValue, GREEN_MASK, 8) - } - true - }, - ).takeIf { colorFilter }, - Preference.PreferenceItem.SliderPreference( - value = colorFilterValue.blue, - title = stringResource(R.string.color_filter_b_value), - max = 255, - onValueChanged = { newRValue -> - readerPreferences.colorFilterValue().getAndSet { - getColorValue(it, newRValue, BLUE_MASK, 0) - } - true - }, - ).takeIf { colorFilter }, - Preference.PreferenceItem.SliderPreference( - value = colorFilterValue.alpha, - title = stringResource(R.string.color_filter_a_value), - max = 255, - onValueChanged = { newRValue -> - readerPreferences.colorFilterValue().getAndSet { - getColorValue(it, newRValue, ALPHA_MASK, 24) - } - true - }, - ).takeIf { colorFilter }, - Preference.PreferenceItem.BasicListPreference( - value = colorFilterMode.toString(), - title = stringResource(R.string.pref_color_filter_mode), - entries = colorFilterModes - .mapIndexed { index, mode -> index.toString() to mode } - .toMap(), - onValueChanged = { newValue -> - readerPreferences.colorFilterMode().set(newValue.toInt()) - true - }, - ).takeIf { colorFilter }, - - Preference.PreferenceItem.SwitchPreference( - pref = readerPreferences.grayscale(), - title = stringResource(R.string.pref_grayscale), - ), - Preference.PreferenceItem.SwitchPreference( - pref = readerPreferences.invertedColors(), - title = stringResource(R.string.pref_inverted_colors), - ), - ), - ) - } - } -} - -private fun getColorValue(currentColor: Int, color: Int, mask: Long, bitShift: Int): Int { - return (color shl bitShift) or (currentColor and mask.inv().toInt()) -} -private const val ALPHA_MASK: Long = 0xFF000000 -private const val RED_MASK: Long = 0x00FF0000 -private const val GREEN_MASK: Long = 0x0000FF00 -private const val BLUE_MASK: Long = 0x000000FF diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt deleted file mode 100644 index 50beac220..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt +++ /dev/null @@ -1,53 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader.setting - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import androidx.core.view.isVisible -import androidx.core.widget.NestedScrollView -import androidx.lifecycle.lifecycleScope -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.ReaderGeneralSettingsBinding -import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.util.preference.bindToPreference -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.injectLazy - -/** - * Sheet to show reader and viewer preferences. - */ -class ReaderGeneralSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - NestedScrollView(context, attrs) { - - private val readerPreferences: ReaderPreferences by injectLazy() - - private val binding = ReaderGeneralSettingsBinding.inflate(LayoutInflater.from(context), this, false) - - init { - addView(binding.root) - - initGeneralPreferences() - } - - /** - * Init general reader preferences. - */ - private fun initGeneralPreferences() { - binding.backgroundColor.bindToIntPreference(readerPreferences.readerTheme(), R.array.reader_themes_values) - binding.showPageNumber.bindToPreference(readerPreferences.showPageNumber()) - binding.fullscreen.bindToPreference(readerPreferences.fullscreen()) - readerPreferences.fullscreen().changes() - .onEach { - // If the preference is explicitly disabled, that means the setting was configured since there is a cutout - binding.cutoutShort.isVisible = it && ((context as ReaderActivity).hasCutout || !readerPreferences.cutoutShort().get()) - binding.cutoutShort.bindToPreference(readerPreferences.cutoutShort()) - } - .launchIn((context as ReaderActivity).lifecycleScope) - - binding.keepscreen.bindToPreference(readerPreferences.keepScreenOn()) - binding.longTap.bindToPreference(readerPreferences.readWithLongTap()) - binding.alwaysShowChapterTransition.bindToPreference(readerPreferences.alwaysShowChapterTransition()) - binding.pageTransitions.bindToPreference(readerPreferences.pageTransitions()) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt new file mode 100644 index 000000000..6a9ad2936 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.ui.reader.setting + +import cafe.adriel.voyager.core.model.ScreenModel +import eu.kanade.tachiyomi.util.preference.toggle +import tachiyomi.core.preference.Preference +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class ReaderSettingsScreenModel( + val preferences: ReaderPreferences = Injekt.get(), +) : ScreenModel { + + fun togglePreference(preference: (ReaderPreferences) -> Preference) { + preference(preferences).toggle() + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt index ff432320f..1d511255e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt @@ -16,7 +16,6 @@ class ReaderSettingsSheet( private val tabs = listOf( ReaderReadingModeSettings(activity) to R.string.pref_category_reading_mode, - ReaderGeneralSettings(activity) to R.string.pref_category_general, ) private lateinit var binding: CommonTabbedSheetBinding diff --git a/app/src/main/res/drawable/ic_brightness_5_24dp.xml b/app/src/main/res/drawable/ic_brightness_5_24dp.xml deleted file mode 100644 index ae338ca19..000000000 --- a/app/src/main/res/drawable/ic_brightness_5_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index cb1a71f78..afd40f4c9 100644 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -119,29 +119,29 @@ app:tint="?attr/colorOnSurface" /> diff --git a/app/src/main/res/layout/reader_general_settings.xml b/app/src/main/res/layout/reader_general_settings.xml deleted file mode 100644 index 1d9fe94a3..000000000 --- a/app/src/main/res/layout/reader_general_settings.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 941c8d872..77161bfad 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -9,20 +9,6 @@ @string/vertical_plus_viewer - - @string/black_background - @string/gray_background - @string/white_background - @string/automatic_background - - - - 1 - 2 - 0 - 3 - - @string/scale_type_fit_screen @string/scale_type_stretch diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 6c6ccda5b..00c91a7b0 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -376,7 +376,7 @@ Vertical Both Actions - Show on long tap + Show actions on long tap Save pages into separate folders Creates folders according to entries\' title Background color From 813d7e49cd8f8811d38c16781b706d866584f96a Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 10 Jul 2023 18:55:20 -0400 Subject: [PATCH 24/25] Remove unused tabbed sheet layouts/classes --- .../setting/ReaderReadingModeSettings.kt | 146 ------------------ .../ui/reader/setting/ReaderSettingsSheet.kt | 144 +++++++++++++---- .../widget/sheet/BaseBottomSheetDialog.kt | 55 ------- .../widget/sheet/BottomSheetViewPager.kt | 56 ------- .../main/res/layout/common_tabbed_sheet.xml | 50 ------ app/src/main/res/values-sw720dp/dimens.xml | 2 - app/src/main/res/values/dimens.xml | 2 - 7 files changed, 113 insertions(+), 342 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BottomSheetViewPager.kt delete mode 100644 app/src/main/res/layout/common_tabbed_sheet.xml diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt deleted file mode 100644 index 23a8590c6..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt +++ /dev/null @@ -1,146 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader.setting - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import androidx.core.view.isVisible -import androidx.core.widget.NestedScrollView -import androidx.lifecycle.lifecycleScope -import eu.kanade.domain.manga.model.orientationType -import eu.kanade.domain.manga.model.readingModeType -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.ReaderReadingModeSettingsBinding -import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer -import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer -import eu.kanade.tachiyomi.util.preference.bindToPreference -import eu.kanade.tachiyomi.util.system.isReleaseBuildType -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import uy.kohesive.injekt.injectLazy - -/** - * Sheet to show reader and viewer preferences. - */ -class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - NestedScrollView(context, attrs) { - - private val readerPreferences: ReaderPreferences by injectLazy() - - private val binding = ReaderReadingModeSettingsBinding.inflate(LayoutInflater.from(context), this, false) - - init { - addView(binding.root) - - initGeneralPreferences() - - when ((context as ReaderActivity).viewModel.state.value.viewer) { - is PagerViewer -> initPagerPreferences() - is WebtoonViewer -> initWebtoonPreferences() - } - } - - /** - * Init general reader preferences. - */ - private fun initGeneralPreferences() { - binding.viewer.onItemSelectedListener = { position -> - val readingModeType = ReadingModeType.fromSpinner(position) - (context as ReaderActivity).viewModel.setMangaReadingMode(readingModeType.flagValue) - - val mangaViewer = (context as ReaderActivity).viewModel.getMangaReadingMode() - if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) { - initWebtoonPreferences() - } else { - initPagerPreferences() - } - } - binding.viewer.setSelection((context as ReaderActivity).viewModel.manga?.readingModeType?.let { ReadingModeType.fromPreference(it.toInt()).prefValue } ?: ReadingModeType.DEFAULT.prefValue) - - binding.rotationMode.onItemSelectedListener = { position -> - val rotationType = OrientationType.fromSpinner(position) - (context as ReaderActivity).viewModel.setMangaOrientationType(rotationType.flagValue) - } - binding.rotationMode.setSelection((context as ReaderActivity).viewModel.manga?.orientationType?.let { OrientationType.fromPreference(it.toInt()).prefValue } ?: OrientationType.DEFAULT.prefValue) - } - - /** - * Init the preferences for the pager reader. - */ - private fun initPagerPreferences() { - binding.webtoonPrefsGroup.root.isVisible = false - binding.pagerPrefsGroup.root.isVisible = true - - binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), ReaderPreferences.TappingInvertMode::class.java) - binding.pagerPrefsGroup.navigatePan.bindToPreference(readerPreferences.navigateToPan()) - - binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager()) - readerPreferences.navigationModePager().changes() - .onEach { - val isTappingEnabled = it != 5 - binding.pagerPrefsGroup.tappingInverted.isVisible = isTappingEnabled - binding.pagerPrefsGroup.navigatePan.isVisible = isTappingEnabled - } - .launchIn((context as ReaderActivity).lifecycleScope) - // Makes so that landscape zoom gets hidden away when image scale type is not fit screen - binding.pagerPrefsGroup.scaleType.bindToPreference(readerPreferences.imageScaleType(), 1) - readerPreferences.imageScaleType().changes() - .onEach { binding.pagerPrefsGroup.landscapeZoom.isVisible = it == 1 } - .launchIn((context as ReaderActivity).lifecycleScope) - binding.pagerPrefsGroup.landscapeZoom.bindToPreference(readerPreferences.landscapeZoom()) - - binding.pagerPrefsGroup.zoomStart.bindToPreference(readerPreferences.zoomStart(), 1) - binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders()) - - binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged()) - readerPreferences.dualPageSplitPaged().changes() - .onEach { - binding.pagerPrefsGroup.dualPageInvert.isVisible = it - if (it) { - binding.pagerPrefsGroup.dualPageRotateToFit.isChecked = false - } - } - .launchIn((context as ReaderActivity).lifecycleScope) - binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged()) - - binding.pagerPrefsGroup.dualPageRotateToFit.bindToPreference(readerPreferences.dualPageRotateToFit()) - readerPreferences.dualPageRotateToFit().changes() - .onEach { - binding.pagerPrefsGroup.dualPageRotateToFitInvert.isVisible = it - if (it) { - binding.pagerPrefsGroup.dualPageSplit.isChecked = false - } - } - .launchIn((context as ReaderActivity).lifecycleScope) - binding.pagerPrefsGroup.dualPageRotateToFitInvert.bindToPreference(readerPreferences.dualPageRotateToFitInvert()) - } - - /** - * Init the preferences for the webtoon reader. - */ - private fun initWebtoonPreferences() { - binding.pagerPrefsGroup.root.isVisible = false - binding.webtoonPrefsGroup.root.isVisible = true - - binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), ReaderPreferences.TappingInvertMode::class.java) - - binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon()) - readerPreferences.navigationModeWebtoon().changes() - .onEach { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 } - .launchIn((context as ReaderActivity).lifecycleScope) - binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(readerPreferences.cropBordersWebtoon()) - binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(readerPreferences.webtoonSidePadding(), R.array.webtoon_side_padding_values) - - binding.webtoonPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitWebtoon()) - // Makes it so that dual page invert gets hidden away when dual page split is turned off - readerPreferences.dualPageSplitWebtoon().changes() - .onEach { binding.webtoonPrefsGroup.dualPageInvert.isVisible = it } - .launchIn((context as ReaderActivity).lifecycleScope) - binding.webtoonPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertWebtoon()) - - binding.webtoonPrefsGroup.longStripSplit.isVisible = !isReleaseBuildType - binding.webtoonPrefsGroup.longStripSplit.bindToPreference(readerPreferences.longStripSplitWebtoon()) - - binding.webtoonPrefsGroup.doubleTapZoom.bindToPreference(readerPreferences.webtoonDoubleTapZoomEnabled()) - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt index 1d511255e..8fde6830a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt @@ -1,54 +1,136 @@ package eu.kanade.tachiyomi.ui.reader.setting import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope +import com.google.android.material.bottomsheet.BottomSheetDialog +import eu.kanade.domain.manga.model.orientationType +import eu.kanade.domain.manga.model.readingModeType import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.databinding.CommonTabbedSheetBinding +import eu.kanade.tachiyomi.databinding.ReaderReadingModeSettingsBinding import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.widget.ViewPagerAdapter -import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog +import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer +import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer +import eu.kanade.tachiyomi.util.preference.bindToPreference +import eu.kanade.tachiyomi.util.system.isReleaseBuildType +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import uy.kohesive.injekt.injectLazy class ReaderSettingsSheet( private val activity: ReaderActivity, -) : BaseBottomSheetDialog(activity) { +) : BottomSheetDialog(activity) { - private val tabs = listOf( - ReaderReadingModeSettings(activity) to R.string.pref_category_reading_mode, - ) + private val readerPreferences: ReaderPreferences by injectLazy() - private lateinit var binding: CommonTabbedSheetBinding - - override fun createView(inflater: LayoutInflater): View { - binding = CommonTabbedSheetBinding.inflate(activity.layoutInflater) - - val adapter = Adapter() - binding.pager.adapter = adapter - binding.tabs.setupWithViewPager(binding.pager) - - return binding.root - } + private lateinit var binding: ReaderReadingModeSettingsBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - behavior.isFitToContents = false - behavior.halfExpandedRatio = 0.25f + binding = ReaderReadingModeSettingsBinding.inflate(activity.layoutInflater) + setContentView(binding.root) + + initGeneralPreferences() + + when (activity.viewModel.state.value.viewer) { + is PagerViewer -> initPagerPreferences() + is WebtoonViewer -> initWebtoonPreferences() + } } - private inner class Adapter : ViewPagerAdapter() { + private fun initGeneralPreferences() { + binding.viewer.onItemSelectedListener = { position -> + val readingModeType = ReadingModeType.fromSpinner(position) + activity.viewModel.setMangaReadingMode(readingModeType.flagValue) - override fun createView(container: ViewGroup, position: Int): View { - return tabs[position].first + val mangaViewer = activity.viewModel.getMangaReadingMode() + if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) { + initWebtoonPreferences() + } else { + initPagerPreferences() + } } + binding.viewer.setSelection(activity.viewModel.manga?.readingModeType?.let { ReadingModeType.fromPreference(it.toInt()).prefValue } ?: ReadingModeType.DEFAULT.prefValue) - override fun getCount(): Int { - return tabs.size + binding.rotationMode.onItemSelectedListener = { position -> + val rotationType = OrientationType.fromSpinner(position) + activity.viewModel.setMangaOrientationType(rotationType.flagValue) } + binding.rotationMode.setSelection(activity.viewModel.manga?.orientationType?.let { OrientationType.fromPreference(it.toInt()).prefValue } ?: OrientationType.DEFAULT.prefValue) + } - override fun getPageTitle(position: Int): CharSequence { - return activity.resources!!.getString(tabs[position].second) - } + private fun initPagerPreferences() { + binding.webtoonPrefsGroup.root.isVisible = false + binding.pagerPrefsGroup.root.isVisible = true + + binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), ReaderPreferences.TappingInvertMode::class.java) + binding.pagerPrefsGroup.navigatePan.bindToPreference(readerPreferences.navigateToPan()) + + binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager()) + readerPreferences.navigationModePager().changes() + .onEach { + val isTappingEnabled = it != 5 + binding.pagerPrefsGroup.tappingInverted.isVisible = isTappingEnabled + binding.pagerPrefsGroup.navigatePan.isVisible = isTappingEnabled + } + .launchIn(activity.lifecycleScope) + // Makes so that landscape zoom gets hidden away when image scale type is not fit screen + binding.pagerPrefsGroup.scaleType.bindToPreference(readerPreferences.imageScaleType(), 1) + readerPreferences.imageScaleType().changes() + .onEach { binding.pagerPrefsGroup.landscapeZoom.isVisible = it == 1 } + .launchIn(activity.lifecycleScope) + binding.pagerPrefsGroup.landscapeZoom.bindToPreference(readerPreferences.landscapeZoom()) + + binding.pagerPrefsGroup.zoomStart.bindToPreference(readerPreferences.zoomStart(), 1) + binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders()) + + binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged()) + readerPreferences.dualPageSplitPaged().changes() + .onEach { + binding.pagerPrefsGroup.dualPageInvert.isVisible = it + if (it) { + binding.pagerPrefsGroup.dualPageRotateToFit.isChecked = false + } + } + .launchIn(activity.lifecycleScope) + binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged()) + + binding.pagerPrefsGroup.dualPageRotateToFit.bindToPreference(readerPreferences.dualPageRotateToFit()) + readerPreferences.dualPageRotateToFit().changes() + .onEach { + binding.pagerPrefsGroup.dualPageRotateToFitInvert.isVisible = it + if (it) { + binding.pagerPrefsGroup.dualPageSplit.isChecked = false + } + } + .launchIn(activity.lifecycleScope) + binding.pagerPrefsGroup.dualPageRotateToFitInvert.bindToPreference(readerPreferences.dualPageRotateToFitInvert()) + } + + private fun initWebtoonPreferences() { + binding.pagerPrefsGroup.root.isVisible = false + binding.webtoonPrefsGroup.root.isVisible = true + + binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), ReaderPreferences.TappingInvertMode::class.java) + + binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon()) + readerPreferences.navigationModeWebtoon().changes() + .onEach { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 } + .launchIn(activity.lifecycleScope) + binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(readerPreferences.cropBordersWebtoon()) + binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(readerPreferences.webtoonSidePadding(), R.array.webtoon_side_padding_values) + + binding.webtoonPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitWebtoon()) + // Makes it so that dual page invert gets hidden away when dual page split is turned off + readerPreferences.dualPageSplitWebtoon().changes() + .onEach { binding.webtoonPrefsGroup.dualPageInvert.isVisible = it } + .launchIn(activity.lifecycleScope) + binding.webtoonPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertWebtoon()) + + binding.webtoonPrefsGroup.longStripSplit.isVisible = !isReleaseBuildType + binding.webtoonPrefsGroup.longStripSplit.bindToPreference(readerPreferences.longStripSplitWebtoon()) + + binding.webtoonPrefsGroup.doubleTapZoom.bindToPreference(readerPreferences.webtoonDoubleTapZoomEnabled()) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt deleted file mode 100644 index d19623525..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BaseBottomSheetDialog.kt +++ /dev/null @@ -1,55 +0,0 @@ -package eu.kanade.tachiyomi.widget.sheet - -import android.content.Context -import android.os.Build -import android.os.Bundle -import android.util.DisplayMetrics -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.getElevation -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.system.displayCompat -import eu.kanade.tachiyomi.util.system.isNightMode -import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat - -abstract class BaseBottomSheetDialog(context: Context) : BottomSheetDialog(context) { - - abstract fun createView(inflater: LayoutInflater): View - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val rootView = createView(layoutInflater) - setContentView(rootView) - - // Enforce max width for tablets - val width = context.resources.getDimensionPixelSize(R.dimen.bottom_sheet_width) - if (width > 0) { - behavior.maxWidth = width - } - - // Set peek height to 50% display height - context.displayCompat?.let { - val metrics = DisplayMetrics() - it.getRealMetrics(metrics) - behavior.peekHeight = metrics.heightPixels / 2 - } - - // Set navbar color to transparent for edge-to-edge bottom sheet if we can use light navigation bar - // TODO Replace deprecated systemUiVisibility when material-components uses new API to modify status bar icons - @Suppress("DEPRECATION") - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - window?.setNavigationBarTransparentCompat(context, behavior.getElevation()) - val bottomSheet = rootView.parent as ViewGroup - var flags = bottomSheet.systemUiVisibility - flags = if (context.isNightMode()) { - flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv() - } else { - flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - } - bottomSheet.systemUiVisibility = flags - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BottomSheetViewPager.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BottomSheetViewPager.kt deleted file mode 100644 index 91b710757..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/BottomSheetViewPager.kt +++ /dev/null @@ -1,56 +0,0 @@ -package eu.kanade.tachiyomi.widget.sheet -import android.content.Context -import android.util.AttributeSet -import android.view.View -import androidx.viewpager.widget.ViewPager -import java.lang.reflect.Field - -/** - * From https://github.com/kafumi/android-bottomsheet-viewpager - */ -class BottomSheetViewPager @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, -) : ViewPager(context, attrs) { - - private val positionField: Field = LayoutParams::class.java.getDeclaredField("position").also { - it.isAccessible = true - } - - override fun getChildAt(index: Int): View { - val currentView = getCurrentView() ?: return super.getChildAt(index) - return if (index == 0) { - currentView - } else { - var view = super.getChildAt(index) - if (view == currentView) { - view = super.getChildAt(0) - } - return view - } - } - - private fun getCurrentView(): View? { - for (i in 0 until childCount) { - val child = super.getChildAt(i) - val lp = child.layoutParams as? LayoutParams - if (lp != null) { - val position = positionField.getInt(lp) - if (!lp.isDecor && currentItem == position) { - return child - } - } - } - return null - } - - init { - addOnPageChangeListener( - object : SimpleOnPageChangeListener() { - override fun onPageSelected(position: Int) { - requestLayout() - } - }, - ) - } -} diff --git a/app/src/main/res/layout/common_tabbed_sheet.xml b/app/src/main/res/layout/common_tabbed_sheet.xml deleted file mode 100644 index 9b6aae965..000000000 --- a/app/src/main/res/layout/common_tabbed_sheet.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/values-sw720dp/dimens.xml b/app/src/main/res/values-sw720dp/dimens.xml index cebce1c6a..5cb983dc4 100644 --- a/app/src/main/res/values-sw720dp/dimens.xml +++ b/app/src/main/res/values-sw720dp/dimens.xml @@ -1,5 +1,3 @@ - 480dp - 24dp diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 65275808c..f6f722017 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,6 +1,4 @@ - 0dp - 8dp 16dp From 3aa6e7ae0e83a93e5c17b7b1f6afd149fbffd67f Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 10 Jul 2023 22:23:05 -0400 Subject: [PATCH 25/25] Fix swipe action preference labels --- .../more/settings/screen/SettingsLibraryScreen.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b6bd25489..755d788f3 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 @@ -282,7 +282,7 @@ object SettingsLibraryScreen : SearchableSettings { preferenceItems = listOf( Preference.PreferenceItem.ListPreference( pref = libraryPreferences.swipeToStartAction(), - title = stringResource(R.string.pref_chapter_swipe_end), + title = stringResource(R.string.pref_chapter_swipe_start), entries = mapOf( LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.disabled), LibraryPreferences.ChapterSwipeAction.ToggleBookmark to stringResource(R.string.action_bookmark), @@ -292,7 +292,7 @@ object SettingsLibraryScreen : SearchableSettings { ), Preference.PreferenceItem.ListPreference( pref = libraryPreferences.swipeToEndAction(), - title = stringResource(R.string.pref_chapter_swipe_start), + title = stringResource(R.string.pref_chapter_swipe_end), entries = mapOf( LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.disabled), LibraryPreferences.ChapterSwipeAction.ToggleBookmark to stringResource(R.string.action_bookmark),