diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fb2dfc5c7..6fe50aa6e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -193,11 +193,6 @@
android:name=".ui.qrcode.QrCodeActivity"
android:enableOnBackInvokedCallback="true"
android:launchMode="singleTask" />
-
-
-
-
>
@@ -741,8 +741,8 @@ interface BookDao {
WHERE type & ${BookType.text} > 0 AND type & ${BookType.local} = 0
AND ((SELECT COALESCE(SUM(groupId), 0) FROM book_groups WHERE groupId > 0) & `group`) = 0
AND (SELECT show FROM book_groups WHERE groupId = ${BookGroup.IdNetNone}) != 1
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfRootPreview(): Flow>
@@ -757,8 +757,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE type & ${BookType.local} > 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfLocalPreview(): Flow>
@@ -773,8 +773,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE type & ${BookType.audio} > 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfAudioPreview(): Flow>
@@ -790,8 +790,8 @@ interface BookDao {
FROM books
WHERE type & ${BookType.audio} = 0 AND type & ${BookType.local} = 0
AND ((SELECT COALESCE(SUM(groupId), 0) FROM book_groups WHERE groupId > 0) & `group`) = 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfNetNoGroupPreview(): Flow>
@@ -807,8 +807,8 @@ interface BookDao {
FROM books
WHERE type & ${BookType.local} > 0
AND ((SELECT COALESCE(SUM(groupId), 0) FROM book_groups WHERE groupId > 0) & `group`) = 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfLocalNoGroupPreview(): Flow>
@@ -823,8 +823,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE type & ${BookType.image} > 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfMangaPreview(): Flow>
@@ -839,8 +839,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE type & ${BookType.text} > 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfTextPreview(): Flow>
@@ -855,8 +855,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE type & ${BookType.updateError} > 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfUpdateErrorPreview(): Flow>
@@ -871,8 +871,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE durChapterIndex = 0 AND durChapterPos = 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfUnreadPreview(): Flow>
@@ -887,8 +887,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE totalChapterNum > 0 AND durChapterIndex > 0 AND durChapterIndex < totalChapterNum - 1
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfReadingPreview(): Flow>
@@ -903,8 +903,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE totalChapterNum > 0 AND durChapterIndex >= totalChapterNum - 1
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfReadFinishedPreview(): Flow>
@@ -919,8 +919,8 @@ interface BookDao {
ifnull(customIntro, intro) as intro, kind
FROM books
WHERE (`group` & :groupId) > 0
- ORDER BY CASE WHEN (coverUrl IS NOT NULL OR customCoverUrl IS NOT NULL) THEN 0 ELSE 1 END, durChapterTime DESC
- LIMIT 4
+ ORDER BY durChapterTime DESC
+ LIMIT 10
"""
)
fun flowBookShelfPreviewByUserGroup(groupId: Long): Flow>
diff --git a/app/src/main/java/io/legado/app/di/appModule.kt b/app/src/main/java/io/legado/app/di/appModule.kt
index 7dba6810c..b9834e025 100644
--- a/app/src/main/java/io/legado/app/di/appModule.kt
+++ b/app/src/main/java/io/legado/app/di/appModule.kt
@@ -14,23 +14,23 @@ import io.legado.app.data.repository.BookRepository
import io.legado.app.data.repository.BookSourceCallbackRepository
import io.legado.app.data.repository.CacheBookDownloadRepository
import io.legado.app.data.repository.DatabaseMaintenanceRepository
-import io.legado.app.data.repository.DirectLinkUploadRepository
import io.legado.app.data.repository.DictRuleRepository
+import io.legado.app.data.repository.DirectLinkUploadRepository
import io.legado.app.data.repository.ExploreRepository
import io.legado.app.data.repository.ExploreRepositoryImpl
import io.legado.app.data.repository.LocalBookRepository
import io.legado.app.data.repository.ReadRecordRepository
import io.legado.app.data.repository.RemoteBookRepository
import io.legado.app.data.repository.RssRepository
+import io.legado.app.data.repository.SearchContentRepository
import io.legado.app.data.repository.SearchRepository
import io.legado.app.data.repository.SearchRepositoryImpl
-import io.legado.app.data.repository.SearchContentRepository
import io.legado.app.data.repository.SettingsRepository
import io.legado.app.data.repository.UploadRepository
import io.legado.app.data.repository.WebDavBackupRepository
import io.legado.app.data.repository.WebDavReadingProgressRepository
-import io.legado.app.domain.gateway.BookCacheCleanupGateway
import io.legado.app.domain.gateway.AppStartupGateway
+import io.legado.app.domain.gateway.BookCacheCleanupGateway
import io.legado.app.domain.gateway.BookCacheDownloadGateway
import io.legado.app.domain.gateway.BookSearchGateway
import io.legado.app.domain.gateway.BookSourceCallbackGateway
@@ -47,14 +47,13 @@ import io.legado.app.domain.usecase.ClearBookCacheUseCase
import io.legado.app.domain.usecase.DeleteBooksUseCase
import io.legado.app.domain.usecase.GetReadingProgressUseCase
import io.legado.app.domain.usecase.RemoveBookGroupAssignmentUseCase
-import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
import io.legado.app.domain.usecase.ResolveBookShelfStateUseCase
-import io.legado.app.domain.usecase.readRecord.GetReadRecordOverviewUseCase
import io.legado.app.domain.usecase.SearchBooksUseCase
import io.legado.app.domain.usecase.ShrinkDatabaseUseCase
import io.legado.app.domain.usecase.UpdateBooksGroupUseCase
import io.legado.app.domain.usecase.UploadReadingProgressUseCase
import io.legado.app.domain.usecase.WebDavBackupUseCase
+import io.legado.app.domain.usecase.readRecord.GetReadRecordOverviewUseCase
import io.legado.app.help.coil.CoverFetcher
import io.legado.app.help.coil.CoverInterceptor
import io.legado.app.help.http.okHttpClient
@@ -64,7 +63,6 @@ import io.legado.app.ui.book.cache.manage.BookCacheManageViewModel
import io.legado.app.ui.book.changecover.ChangeCoverViewModel
import io.legado.app.ui.book.changesource.ChangeBookSourceComposeViewModel
import io.legado.app.ui.book.changesource.ChangeBookSourceViewModel
-import io.legado.app.ui.book.manage.BookshelfManageScreenViewModel
import io.legado.app.ui.book.explore.ExploreShowViewModel
import io.legado.app.ui.book.group.GroupViewModel
import io.legado.app.ui.book.import.local.ImportBookViewModel
@@ -72,10 +70,11 @@ import io.legado.app.ui.book.import.remote.RemoteBookViewModel
import io.legado.app.ui.book.import.remote.ServerConfigViewModel
import io.legado.app.ui.book.import.remote.ServersViewModel
import io.legado.app.ui.book.info.BookInfoViewModel
+import io.legado.app.ui.book.manage.BookshelfManageScreenViewModel
import io.legado.app.ui.book.manga.ReadMangaViewModel
import io.legado.app.ui.book.read.ReadBookViewModel
-import io.legado.app.ui.book.readRecord.ReadRecordViewModel
import io.legado.app.ui.book.readRecord.ReadRecordOverviewViewModel
+import io.legado.app.ui.book.readRecord.ReadRecordViewModel
import io.legado.app.ui.book.search.SearchViewModel
import io.legado.app.ui.book.searchContent.SearchContentViewModel
import io.legado.app.ui.book.toc.TocViewModel
@@ -97,10 +96,13 @@ import io.legado.app.ui.main.rss.RssViewModel
import io.legado.app.ui.replace.ReplaceEditRoute
import io.legado.app.ui.replace.ReplaceRuleViewModel
import io.legado.app.ui.replace.edit.ReplaceEditViewModel
-import io.legado.app.ui.rss.source.manage.RssSourceViewModel
import io.legado.app.ui.rss.article.RssArticlesViewModel
import io.legado.app.ui.rss.article.RssSortViewModel
+import io.legado.app.ui.rss.favorites.RssFavoritesViewModel
import io.legado.app.ui.rss.read.ReadRssViewModel
+import io.legado.app.ui.rss.source.manage.RssSourceViewModel
+import io.legado.app.ui.rss.subscription.RuleSubViewModel
+import io.legado.app.ui.widget.components.explore.ExploreKindUiUseCase
import org.koin.core.module.dsl.singleOf
import org.koin.core.module.dsl.viewModel
import org.koin.core.module.dsl.viewModelOf
@@ -179,6 +181,8 @@ val appModule = module {
viewModelOf(::RssSortViewModel)
viewModelOf(::RssArticlesViewModel)
viewModelOf(::ReadRssViewModel)
+ viewModelOf(::RssFavoritesViewModel)
+ viewModelOf(::RuleSubViewModel)
viewModelOf(::ReadRecordViewModel)
viewModelOf(::ReadRecordOverviewViewModel)
viewModelOf(::ExploreShowViewModel)
diff --git a/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt b/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt
index 0faf4ac07..760baa44f 100644
--- a/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt
+++ b/app/src/main/java/io/legado/app/ui/config/bookshelfConfig/BookshelfConfig.kt
@@ -269,12 +269,17 @@ object BookshelfConfig {
/**
* 书架卡片背景颜色
*/
- private val _bookshelfCardColor = prefStateDelegate(PreferKey.bookshelfCardColor, 0) {
- postEvent(EventBus.NOTIFY_MAIN, false)
- }
+ private val _bookshelfCardColor = prefStateDelegate(PreferKey.bookshelfCardColor, 0)
var bookshelfCardColor by _bookshelfCardColor
val bookshelfCardColorState: State get() = _bookshelfCardColor.state
+ /**
+ * 书架卡片背景颜色 (夜间)
+ */
+ private val _bookshelfCardColorDark = prefStateDelegate(PreferKey.bookshelfCardColorDark, 0)
+ var bookshelfCardColorDark by _bookshelfCardColorDark
+ val bookshelfCardColorDarkState: State get() = _bookshelfCardColorDark.state
+
/**
* 文件夹在列表模式下的样式: 0: 默认, 2: 横排封面
*/
diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
index b20d34050..3c7bee3b6 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
@@ -15,18 +15,12 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleOut
import androidx.compose.animation.togetherWith
-import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
-import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.navigation3.runtime.NavKey
-import androidx.navigation3.runtime.entryProvider
import androidx.navigation3.runtime.rememberNavBackStack
-import androidx.navigation3.ui.LocalNavAnimatedContentScope
import androidx.navigation3.ui.NavDisplay
import io.legado.app.BuildConfig
import io.legado.app.R
@@ -43,49 +37,18 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.service.WebService
import io.legado.app.ui.about.CrashLogsDialog
import io.legado.app.ui.about.UpdateDialog
-import io.legado.app.ui.book.cache.manage.BookCacheManageRouteScreen
-import io.legado.app.ui.book.explore.ExploreShowScreen
-import io.legado.app.ui.book.info.BookInfoRouteScreen
-import io.legado.app.ui.book.info.BookInfoViewModel
-import io.legado.app.ui.book.import.local.ImportBookScreen
-import io.legado.app.ui.book.import.remote.RemoteBookScreen
-import io.legado.app.ui.book.search.SearchIntent
-import io.legado.app.ui.book.search.SearchScreen
-import io.legado.app.ui.book.search.SearchViewModel
-import io.legado.app.ui.book.readRecord.ReadRecordOverviewScreen
-import io.legado.app.ui.book.readRecord.ReadRecordScreen
-import io.legado.app.ui.book.readRecord.ReadRecordViewModel
-import io.legado.app.ui.book.source.manage.BookSourceActivity
-import io.legado.app.ui.book.manage.BookshelfManageRouteScreen
import io.legado.app.ui.book.read.ReadBookActivity
-import io.legado.app.ui.config.ConfigNavScreen
-import io.legado.app.ui.config.ConfigTag
-import io.legado.app.ui.config.backupConfig.BackupConfigScreen
-import io.legado.app.ui.config.coverConfig.CoverConfigScreen
-import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigScreen
import io.legado.app.ui.config.themeConfig.ThemeConfig
-import io.legado.app.ui.config.otherConfig.OtherConfigScreen
-import io.legado.app.ui.config.customTheme.CustomThemeScreen
-import io.legado.app.ui.config.readConfig.ReadConfigScreen
-import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
-import io.legado.app.ui.config.themeManage.ThemeManageScreen
-import io.legado.app.ui.rss.article.MainRouteRssSort
-import io.legado.app.ui.rss.article.RssSortRouteScreen
-import io.legado.app.ui.rss.read.MainRouteRssRead
-import io.legado.app.ui.rss.read.RssReadRouteScreen
import io.legado.app.ui.welcome.WelcomeActivity
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.ui.widget.dialog.VariableDialog
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.startActivity
-import io.legado.app.utils.startActivityForBook
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import kotlinx.serialization.Serializable
-import org.koin.androidx.compose.koinViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@@ -96,81 +59,19 @@ import kotlin.coroutines.suspendCoroutine
open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
companion object {
- const val EXTRA_START_ROUTE = "startRoute"
- private const val ROUTE_MAIN = "main"
- private const val ROUTE_SETTINGS = "settings"
- private const val ROUTE_SETTINGS_OTHER = "settings/other"
- private const val ROUTE_SETTINGS_READ = "settings/read"
- private const val ROUTE_SETTINGS_COVER = "settings/cover"
- private const val ROUTE_SETTINGS_THEME = "settings/theme"
- private const val ROUTE_SETTINGS_BACKUP = "settings/backup"
- private const val ROUTE_SETTINGS_CUSTOM_THEME = "settings/custom_theme"
- private const val ROUTE_SETTINGS_DOWNLOAD_CACHE = "settings/download_cache"
- private const val ROUTE_IMPORT_LOCAL = "import/local"
- private const val ROUTE_IMPORT_REMOTE = "import/remote"
- private const val ROUTE_CACHE = "cache"
- private const val ROUTE_BOOK_CACHE_MANAGE = "book/cache/manage"
- private const val ROUTE_SEARCH = "search"
- private const val ROUTE_BOOK_INFO = "book/info"
- private const val ROUTE_EXPLORE_SHOW = "explore/show"
- private const val ROUTE_RSS_SORT = "rss/sort"
- private const val ROUTE_RSS_READ = "rss/read"
- private const val ROUTE_READ_RECORD = "read_record"
- private const val ROUTE_READ_RECORD_OVERVIEW = "read_record_overview"
- private const val EXTRA_CACHE_GROUP_ID = "extra_cache_group_id"
- private const val EXTRA_SEARCH_KEY = "extra_search_key"
- private const val EXTRA_SEARCH_SCOPE = "extra_search_scope"
- private const val EXTRA_BOOK_NAME = "name"
- private const val EXTRA_BOOK_AUTHOR = "author"
- private const val EXTRA_BOOK_URL = "bookUrl"
- private const val EXTRA_EXPLORE_NAME = "exploreName"
- private const val EXTRA_SOURCE_URL = "sourceUrl"
- private const val EXTRA_EXPLORE_URL = "exploreUrl"
+ fun createLauncherIntent(context: Context): Intent =
+ MainIntent.createLauncherIntent(context)
- private const val EXTRA_RSS_SOURCE_URL = "extra_rss_source_url"
- private const val EXTRA_RSS_SORT_URL = "extra_rss_sort_url"
- private const val EXTRA_RSS_KEY = "extra_rss_key"
-
- private const val EXTRA_RSS_READ_TITLE = "extra_rss_read_title"
- private const val EXTRA_RSS_READ_ORIGIN = "extra_rss_read_origin"
- private const val EXTRA_RSS_READ_LINK = "extra_rss_read_link"
- private const val EXTRA_RSS_READ_OPEN_URL = "extra_rss_read_open_url"
-
- fun createLauncherIntent(context: Context): Intent {
- val launcherComponent =
- context.packageManager.getLaunchIntentForPackage(context.packageName)?.component
- return if (launcherComponent != null) {
- Intent().setComponent(launcherComponent)
- } else {
- Intent(context, MainActivity::class.java)
- }
- }
-
- fun createHomeIntent(context: Context): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_MAIN)
- }
- }
-
- fun createIntent(context: Context, configTag: String? = null): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, routeForConfigTag(configTag))
- }
- }
+ fun createHomeIntent(context: Context): Intent = MainIntent.createHomeIntent(context)
+ fun createIntent(context: Context, configTag: String? = null): Intent =
+ MainIntent.createIntent(context, configTag)
fun createRssSortIntent(
context: Context,
sourceUrl: String,
sortUrl: String? = null,
key: String? = null
- ): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_RSS_SORT)
- putExtra(EXTRA_RSS_SOURCE_URL, sourceUrl)
- putExtra(EXTRA_RSS_SORT_URL, sortUrl)
- putExtra(EXTRA_RSS_KEY, key)
- }
- }
+ ): Intent = MainIntent.createRssSortIntent(context, sourceUrl, sortUrl, key)
fun createRssReadIntent(
context: Context,
@@ -178,165 +79,42 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
origin: String,
link: String? = null,
openUrl: String? = null
- ): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_RSS_READ)
- putExtra(EXTRA_RSS_READ_TITLE, title)
- putExtra(EXTRA_RSS_READ_ORIGIN, origin)
- putExtra(EXTRA_RSS_READ_LINK, link)
- putExtra(EXTRA_RSS_READ_OPEN_URL, openUrl)
- }
- }
+ ): Intent = MainIntent.createRssReadIntent(context, title, origin, link, openUrl)
- fun createBookshelfManageScreenIntent(
- context: Context,
- groupId: Long = -1L
- ): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_CACHE)
- putExtra(EXTRA_CACHE_GROUP_ID, groupId)
- }
- }
+ fun createBookshelfManageScreenIntent(context: Context, groupId: Long = -1L): Intent =
+ MainIntent.createBookshelfManageScreenIntent(context, groupId)
- fun createCacheIntent(
- context: Context,
- groupId: Long = -1L
- ): Intent = createBookshelfManageScreenIntent(context, groupId)
+ fun createCacheIntent(context: Context, groupId: Long = -1L): Intent =
+ MainIntent.createCacheIntent(context, groupId)
- fun createBookCacheManageIntent(context: Context): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_BOOK_CACHE_MANAGE)
- }
- }
+ fun createBookCacheManageIntent(context: Context): Intent =
+ MainIntent.createBookCacheManageIntent(context)
fun createSearchIntent(
context: Context,
key: String? = null,
scopeRaw: String? = null
- ): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_SEARCH)
- putExtra(EXTRA_SEARCH_KEY, key)
- putExtra(EXTRA_SEARCH_SCOPE, scopeRaw)
- }
- }
+ ): Intent = MainIntent.createSearchIntent(context, key, scopeRaw)
fun createBookInfoIntent(
context: Context,
name: String? = null,
author: String? = null,
bookUrl: String
- ): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_BOOK_INFO)
- putExtra(EXTRA_BOOK_NAME, name)
- putExtra(EXTRA_BOOK_AUTHOR, author)
- putExtra(EXTRA_BOOK_URL, bookUrl)
- }
- }
+ ): Intent = MainIntent.createBookInfoIntent(context, name, author, bookUrl)
fun createExploreShowIntent(
context: Context,
exploreName: String? = null,
sourceUrl: String,
exploreUrl: String? = null
- ): Intent {
- return createLauncherIntent(context).apply {
- putExtra(EXTRA_START_ROUTE, ROUTE_EXPLORE_SHOW)
- putExtra(EXTRA_EXPLORE_NAME, exploreName)
- putExtra(EXTRA_SOURCE_URL, sourceUrl)
- putExtra(EXTRA_EXPLORE_URL, exploreUrl)
- }
- }
-
- private fun routeForConfigTag(configTag: String?): String {
- return when (configTag) {
- ConfigTag.OTHER_CONFIG -> ROUTE_SETTINGS_OTHER
- ConfigTag.READ_CONFIG -> ROUTE_SETTINGS_READ
- ConfigTag.COVER_CONFIG -> ROUTE_SETTINGS_COVER
- ConfigTag.THEME_CONFIG -> ROUTE_SETTINGS_THEME
- ConfigTag.BACKUP_CONFIG -> ROUTE_SETTINGS_BACKUP
- ConfigTag.DOWNLOAD_CACHE_CONFIG -> ROUTE_SETTINGS_DOWNLOAD_CACHE
- else -> ROUTE_SETTINGS
- }
- }
+ ): Intent = MainIntent.createExploreShowIntent(context, exploreName, sourceUrl, exploreUrl)
}
private val viewModel by viewModel()
private val routeEvents = MutableSharedFlow(extraBufferCapacity = 1)
private var bookInfoVariableSetter: ((String, String?) -> Unit)? = null
- @Serializable
- private sealed interface MainRoute : NavKey
-
- @Serializable
- private data object MainRouteHome : MainRoute
-
- @Serializable
- private data object MainRouteSettings : MainRoute
-
- @Serializable
- private data object MainRouteSettingsOther : MainRoute
-
- @Serializable
- private data object MainRouteSettingsRead : MainRoute
-
- @Serializable
- private data object MainRouteSettingsCover : MainRoute
-
- @Serializable
- private data object MainRouteSettingsTheme : MainRoute
-
- @Serializable
- private data object MainRouteSettingsBackup : MainRoute
-
- @Serializable
- private data object MainRouteSettingsCustomTheme : MainRoute
-
- @Serializable
- private data object MainRouteSettingsThemeManage : MainRoute
-
- @Serializable
- private data object MainRouteSettingsDownloadCache : MainRoute
-
- @Serializable
- private data object MainRouteImportLocal : MainRoute
-
- @Serializable
- private data object MainRouteImportRemote : MainRoute
-
- @Serializable
- private data object MainRouteReadRecord : MainRoute
-
- @Serializable
- private data object MainRouteReadRecordOverview : MainRoute
-
- @Serializable
- private data class MainRouteCache(val groupId: Long) : MainRoute
-
- @Serializable
- private data object MainRouteBookCacheManage : MainRoute
-
- @Serializable
- private data class MainRouteSearch(
- val key: String?,
- val scopeRaw: String? = null
- ) : MainRoute
-
- @Serializable
- private data class MainRouteBookInfo(
- val name: String?,
- val author: String?,
- val bookUrl: String,
- ) : MainRoute
-
- @Serializable
- private data class MainRouteExploreShow(
- val title: String?,
- val sourceUrl: String,
- val exploreUrl: String?,
- ) : MainRoute
-
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
@@ -367,7 +145,7 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
- routeEvents.tryEmit(resolveStartRoute(intent))
+ routeEvents.tryEmit(MainNavigator.resolveStartRoute(intent))
}
@OptIn(ExperimentalSharedTransitionApi::class)
@@ -385,11 +163,11 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
else -> false
}
- val backStack = rememberNavBackStack(resolveStartRoute(intent))
+ val backStack = rememberNavBackStack(MainNavigator.resolveStartRoute(intent))
LaunchedEffect(backStack) {
routeEvents.collect { route ->
- navigateToRoute(backStack, route)
+ MainNavigator.navigateToRoute(backStack, route)
}
}
@@ -397,556 +175,69 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
NavDisplay(
backStack = backStack,
transitionSpec = {
- (slideIntoContainer(
- towards = AnimatedContentTransitionScope.SlideDirection.Start,
- animationSpec = tween(
- durationMillis = 480,
- easing = FastOutSlowInEasing
- ),
- initialOffset = { fullWidth -> fullWidth }
- ) + fadeIn(
- animationSpec = tween(
- durationMillis = 360,
- easing = LinearOutSlowInEasing
- )
- )) togetherWith (slideOutOfContainer(
- towards = AnimatedContentTransitionScope.SlideDirection.Start,
- animationSpec = tween(
- durationMillis = 480,
- easing = FastOutSlowInEasing
- ),
- targetOffset = { fullWidth -> fullWidth / 4 }
- ) + fadeOut(
- animationSpec = tween(
- durationMillis = 360,
- easing = LinearOutSlowInEasing
- )
- ))
- },
- popTransitionSpec = {
- (slideIntoContainer(
- towards = AnimatedContentTransitionScope.SlideDirection.Start,
- animationSpec = tween(
- durationMillis = 480,
- easing = FastOutSlowInEasing
- ),
- initialOffset = { fullWidth -> -fullWidth / 4 }
- ) + fadeIn(
- animationSpec = tween(
- durationMillis = 360,
- easing = LinearOutSlowInEasing
- )
- )) togetherWith (scaleOut(
- targetScale = 0.8f,
- animationSpec = tween(
- durationMillis = 480,
- easing = FastOutSlowInEasing
- )
- ) + fadeOut(
- animationSpec = tween(durationMillis = 360)
- ))
- },
- predictivePopTransitionSpec = { _ ->
- (slideIntoContainer(
- towards = AnimatedContentTransitionScope.SlideDirection.Start,
- animationSpec = tween(
- easing = FastOutSlowInEasing
- ),
- initialOffset = { fullWidth -> -fullWidth / 4 }
- ) + fadeIn(
- animationSpec = tween(
- easing = LinearOutSlowInEasing
- )
- )) togetherWith (scaleOut(
- targetScale = 0.8f,
- animationSpec = tween(
- easing = FastOutSlowInEasing
- )
- ) + fadeOut(
- animationSpec = tween()
- ))
- },
- onBack = {
- if (backStack.size > 1) {
- backStack.removeLastOrNull()
- } else {
- finish()
- }
- },
- entryProvider = entryProvider {
- entry {
- MainScreen(
- useRail = useRail,
- onOpenSettings = {
- navigateToRoute(backStack, MainRouteSettings)
- },
- onNavigateToSearch = { key ->
- navigateToRoute(
- backStack,
- MainRouteSearch(
- key = key?.trim()?.takeIf { it.isNotEmpty() }
- )
- )
- },
- onNavigateToRemoteImport = {
- navigateToRoute(backStack, MainRouteImportRemote)
- },
- onNavigateToLocalImport = {
- navigateToRoute(backStack, MainRouteImportLocal)
- },
- onNavigateToCache = { groupId ->
- navigateToRoute(backStack, MainRouteCache(groupId))
- },
- onNavigateToBookCacheManage = {
- navigateToRoute(backStack, MainRouteBookCacheManage)
- },
- onNavigateToBookInfo = { name, author, bookUrl ->
- navigateToRoute(
- backStack,
- MainRouteBookInfo(
- name = name,
- author = author,
- bookUrl = bookUrl
- )
- )
- },
- onNavigateToExploreShow = { title, sourceUrl, exploreUrl ->
- navigateToRoute(
- backStack,
- MainRouteExploreShow(
- title = title,
- sourceUrl = sourceUrl,
- exploreUrl = exploreUrl
- )
- )
- },
- onNavigateToRssSort = { sourceUrl, sortUrl, key ->
- navigateToRoute(
- backStack,
- MainRouteRssSort(
- sourceUrl = sourceUrl,
- sortUrl = sortUrl,
- key = key
- )
- )
- },
- onNavigateToRssRead = { title, origin, link, openUrl ->
- navigateToRoute(
- backStack,
- MainRouteRssRead(
- title = title,
- origin = origin,
- link = link,
- openUrl = openUrl
- )
- )
- },
- onNavigateToReadRecord = {
- navigateToRoute(backStack, MainRouteReadRecord)
- },
- sharedTransitionScope = this@SharedTransitionLayout,
- animatedVisibilityScope = LocalNavAnimatedContentScope.current,
- )
- }
-
- entry {
- ConfigNavScreen(
- onBackClick = { navigateBack(backStack) },
- onNavigateToOther = { backStack.add(MainRouteSettingsOther) },
- onNavigateToRead = { backStack.add(MainRouteSettingsRead) },
- onNavigateToCover = { backStack.add(MainRouteSettingsCover) },
- onNavigateToTheme = { backStack.add(MainRouteSettingsTheme) },
- onNavigateToBackup = { backStack.add(MainRouteSettingsBackup) },
- onNavigateToDownloadCache = { backStack.add(MainRouteSettingsDownloadCache) }
- )
- }
-
- entry {
- OtherConfigScreen(onBackClick = { navigateBack(backStack) })
- }
-
- entry {
- ReadConfigScreen(onBackClick = { navigateBack(backStack) })
- }
-
- entry {
- CoverConfigScreen(onBackClick = { navigateBack(backStack) })
- }
-
- entry {
- ThemeConfigScreen(
- onBackClick = { navigateBack(backStack) },
- onNavigateToCustomTheme = { backStack.add(MainRouteSettingsCustomTheme) },
- onNavigateToThemeManage = { backStack.add(MainRouteSettingsThemeManage) }
- )
- }
-
- entry {
- BackupConfigScreen(onBackClick = { navigateBack(backStack) })
- }
-
- entry {
- DownloadCacheConfigScreen(onBackClick = { navigateBack(backStack) })
- }
-
- entry {
- CustomThemeScreen(
- onBackClick = { navigateBack(backStack) }
- )
- }
-
- entry {
- ThemeManageScreen(onBackClick = { navigateBack(backStack) })
- }
-
- entry {
- ImportBookScreen(
- onBackClick = { navigateBack(backStack) }
- )
- }
-
- entry {
- RemoteBookScreen(
- onBackClick = { navigateBack(backStack) }
- )
- }
-
- entry { route ->
- BookshelfManageRouteScreen(
- groupId = route.groupId,
- onBackClick = { navigateBack(backStack) },
- onOpenBookInfo = { name, author, bookUrl ->
- navigateToRoute(
- backStack,
- MainRouteBookInfo(
- name = name,
- author = author,
- bookUrl = bookUrl
- )
- )
- }
- )
- }
-
- entry {
- BookCacheManageRouteScreen(
- onBackClick = { navigateBack(backStack) }
- )
- }
-
- entry { route ->
- val searchViewModel = koinViewModel()
-
- LaunchedEffect(route.key, route.scopeRaw, searchViewModel) {
- searchViewModel.onIntent(
- SearchIntent.Initialize(
- key = route.key,
- scopeRaw = route.scopeRaw
- )
+ (slideIntoContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(durationMillis = 480, easing = FastOutSlowInEasing),
+ initialOffset = { fullWidth -> fullWidth }
+ ) + fadeIn(
+ animationSpec = tween(
+ durationMillis = 360,
+ easing = LinearOutSlowInEasing
)
- }
-
- SearchScreen(
- viewModel = searchViewModel,
- onBack = {
- searchViewModel.onIntent(SearchIntent.ClearSearchResults)
- navigateBack(backStack)
- },
- onOpenBookInfo = { name, author, bookUrl ->
- navigateToRoute(
- backStack,
- MainRouteBookInfo(
- name = name,
- author = author,
- bookUrl = bookUrl
- )
- )
- },
- onOpenSourceManage = {
- this@MainActivity.startActivity()
- },
- sharedTransitionScope = this@SharedTransitionLayout,
- animatedVisibilityScope = LocalNavAnimatedContentScope.current,
- )
- }
-
- entry { route ->
- RssSortRouteScreen(
- sourceUrl = route.sourceUrl,
- initialSortUrl = route.sortUrl,
- onBackClick = { navigateBack(backStack) },
- onOpenRead = { title, origin, link, openUrl ->
- if (link?.contains("@js:") == true) {
- navigateToRoute(
- backStack,
- MainRouteRssSort(
- sourceUrl = origin,
- sortUrl = link
- )
- )
- } else {
- navigateToRoute(
- backStack,
- MainRouteRssRead(
- title = title,
- origin = origin,
- link = link,
- openUrl = openUrl
- )
- )
- }
- }
- )
- }
-
- entry { route ->
- RssReadRouteScreen(
- title = route.title,
- origin = route.origin,
- link = route.link,
- openUrl = route.openUrl,
- onBackClick = { navigateBack(backStack) }
- )
- }
-
- entry {
- ReadRecordScreen(
- onBackClick = { navigateBack(backStack) },
- onBookClick = { name, author ->
- lifecycleScope.launch {
- val book = withContext(IO) {
- io.legado.app.data.appDb.bookDao.getBook(name, author)
- }
- if (book != null) this@MainActivity.startActivityForBook(book)
- else {
- navigateToRoute(backStack, MainRouteSearch(key = name))
- }
- }
- },
- onSummaryClick = {
- navigateToRoute(backStack, MainRouteReadRecordOverview)
- }
- )
- }
-
- entry {
- ReadRecordOverviewScreen(
- onBackClick = { navigateBack(backStack) },
- onBookClick = { name, author ->
- lifecycleScope.launch {
- val book = withContext(IO) {
- io.legado.app.data.appDb.bookDao.getBook(name, author)
- }
- if (book != null) this@MainActivity.startActivityForBook(book)
- else {
- navigateToRoute(backStack, MainRouteSearch(key = name))
- }
- }
- }
- )
- }
-
- entry(
- metadata = NavDisplay.transitionSpec {
- val from = initialState.key
- val fromStr = from.toString()
- if (from is MainRouteHome || from is MainRouteExploreShow || from is MainRouteSearch ||
- fromStr.startsWith("MainRouteHome") || fromStr.startsWith("MainRouteExploreShow") || fromStr.startsWith("MainRouteSearch")) {
- fadeIn(animationSpec = tween(300)) togetherWith
- fadeOut(animationSpec = tween(300))
- } else null
- } + NavDisplay.popTransitionSpec {
- val to = targetState.key
- val toStr = to.toString()
- if (to is MainRouteHome || to is MainRouteExploreShow || to is MainRouteSearch ||
- toStr.startsWith("MainRouteHome") || toStr.startsWith("MainRouteExploreShow") || toStr.startsWith("MainRouteSearch")) {
- fadeIn(animationSpec = tween(300)) togetherWith
- fadeOut(animationSpec = tween(300))
- } else null
- } + NavDisplay.predictivePopTransitionSpec { _ ->
- val to = targetState.key
- val toStr = to.toString()
- if (to is MainRouteHome || to is MainRouteExploreShow || to is MainRouteSearch ||
- toStr.startsWith("MainRouteHome") || toStr.startsWith("MainRouteExploreShow") || toStr.startsWith("MainRouteSearch")) {
- fadeIn(animationSpec = tween(300)) togetherWith
- fadeOut(animationSpec = tween(300))
- } else null
- }
- ) { route ->
- val bookInfoViewModel = koinViewModel()
- BookInfoRouteScreen(
- bookUrl = route.bookUrl,
- viewModel = bookInfoViewModel,
- onBack = { navigateBack(backStack) },
- onFinish = { _, _ -> navigateBack(backStack) },
- onOpenSearch = { keyword ->
- navigateToRoute(backStack, MainRouteSearch(key = keyword))
- },
- sharedTransitionScope = this@SharedTransitionLayout,
- animatedVisibilityScope = LocalNavAnimatedContentScope.current,
- sharedCoverKey = bookCoverSharedElementKey(route.bookUrl),
- onRegisterVariableSetter = { setter ->
- bookInfoVariableSetter = setter
- }
- )
- }
-
- entry { route ->
- ExploreShowScreen(
- title = route.title ?: "探索",
- sourceUrl = route.sourceUrl,
- exploreUrl = route.exploreUrl,
- onBack = { navigateBack(backStack) },
- onBookClick = { book ->
- navigateToRoute(
- backStack,
- MainRouteBookInfo(
- name = book.name,
- author = book.author,
- bookUrl = book.bookUrl
- )
- )
- },
- sharedTransitionScope = this@SharedTransitionLayout,
- animatedVisibilityScope = LocalNavAnimatedContentScope.current,
- )
- }
- }
+ )) togetherWith (slideOutOfContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(durationMillis = 480, easing = FastOutSlowInEasing),
+ targetOffset = { fullWidth -> fullWidth / 4 }
+ ) + fadeOut(
+ animationSpec = tween(
+ durationMillis = 360,
+ easing = LinearOutSlowInEasing
+ )
+ ))
+ },
+ popTransitionSpec = {
+ (slideIntoContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(durationMillis = 480, easing = FastOutSlowInEasing),
+ initialOffset = { fullWidth -> -fullWidth / 4 }
+ ) + fadeIn(
+ animationSpec = tween(
+ durationMillis = 360,
+ easing = LinearOutSlowInEasing
+ )
+ )) togetherWith (scaleOut(
+ targetScale = 0.8f,
+ animationSpec = tween(durationMillis = 480, easing = FastOutSlowInEasing)
+ ) + fadeOut(animationSpec = tween(durationMillis = 360)))
+ },
+ predictivePopTransitionSpec = { _ ->
+ (slideIntoContainer(
+ towards = AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = tween(easing = FastOutSlowInEasing),
+ initialOffset = { fullWidth -> -fullWidth / 4 }
+ ) + fadeIn(animationSpec = tween(easing = LinearOutSlowInEasing))) togetherWith (scaleOut(
+ targetScale = 0.8f,
+ animationSpec = tween(easing = FastOutSlowInEasing)
+ ) + fadeOut(animationSpec = tween()))
+ },
+ onBack = { MainNavigator.navigateBack(this@MainActivity, backStack) },
+ entryProvider = mainEntryProvider(
+ backStack = backStack,
+ useRail = useRail,
+ sharedTransitionScope = this@SharedTransitionLayout,
+ onNavigateToRoute = { route ->
+ MainNavigator.navigateToRoute(
+ backStack,
+ route
+ )
+ },
+ onNavigateBack = { MainNavigator.navigateBack(this@MainActivity, backStack) },
+ onRegisterVariableSetter = { setter -> bookInfoVariableSetter = setter }
+ )
)
}
}
- private fun navigateToRoute(backStack: MutableList, route: NavKey) {
- val currentRoute = backStack.lastOrNull()
- if (currentRoute == route) return
-
- when (route) {
- MainRouteHome -> {
- backStack.clear()
- backStack.add(MainRouteHome)
- }
-
- MainRouteSettings -> {
- if (currentRoute == MainRouteHome) {
- backStack.add(MainRouteSettings)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(MainRouteSettings)
- }
- }
-
- MainRouteSettingsOther,
- MainRouteSettingsRead,
- MainRouteSettingsCover,
- MainRouteSettingsTheme,
- MainRouteSettingsBackup,
- MainRouteSettingsCustomTheme,
- MainRouteSettingsThemeManage,
- MainRouteSettingsDownloadCache -> {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(MainRouteSettings)
- backStack.add(route)
- }
-
- MainRouteImportLocal,
- MainRouteImportRemote,
- is MainRouteCache,
- MainRouteBookCacheManage -> {
- if (currentRoute == MainRouteHome) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- is MainRouteSearch -> {
- if (currentRoute == MainRouteHome) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- is MainRouteBookInfo -> {
- if (
- currentRoute == MainRouteHome ||
- currentRoute is MainRouteSearch ||
- currentRoute is MainRouteExploreShow
- ) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- is MainRouteExploreShow -> {
- if (currentRoute == MainRouteHome) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- is MainRouteRssSort -> {
- if (currentRoute == MainRouteHome) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- is MainRouteRssRead -> {
- if (currentRoute == MainRouteHome || currentRoute is MainRouteRssSort) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- MainRouteReadRecord -> {
- if (currentRoute == MainRouteHome) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
-
- MainRouteReadRecordOverview -> {
- if (currentRoute == MainRouteHome || currentRoute == MainRouteReadRecord) {
- backStack.add(route)
- } else {
- backStack.clear()
- backStack.add(MainRouteHome)
- backStack.add(route)
- }
- }
- }
- }
-
- private fun navigateBack(backStack: MutableList) {
- if (backStack.size > 1) {
- backStack.removeLastOrNull()
- } else {
- finish()
- }
- }
-
private fun checkStartupRoute(): Boolean {
return when {
LocalConfig.isFirstOpenApp -> {
@@ -1057,96 +348,6 @@ open class MainActivity : BaseComposeActivity(), VariableDialog.Callback {
}
}
- private fun resolveStartRoute(intent: Intent?): NavKey {
- val route = intent?.getStringExtra(EXTRA_START_ROUTE)
- resolveRssStartRoute(route, intent)?.let { return it }
- return resolveStartRoute(route)
- }
-
- private fun resolveRssStartRoute(route: String?, intent: Intent?): NavKey? {
- return when (route) {
- ROUTE_RSS_SORT -> {
- val sourceUrl = intent?.getStringExtra(EXTRA_RSS_SOURCE_URL)
- if (sourceUrl.isNullOrBlank()) {
- null
- } else {
- MainRouteRssSort(
- sourceUrl = sourceUrl,
- sortUrl = intent.getStringExtra(EXTRA_RSS_SORT_URL),
- key = intent.getStringExtra(EXTRA_RSS_KEY)
- )
- }
- }
-
- ROUTE_RSS_READ -> {
- val origin = intent?.getStringExtra(EXTRA_RSS_READ_ORIGIN)
- if (origin.isNullOrBlank()) {
- null
- } else {
- MainRouteRssRead(
- title = intent.getStringExtra(EXTRA_RSS_READ_TITLE),
- origin = origin,
- link = intent.getStringExtra(EXTRA_RSS_READ_LINK),
- openUrl = intent.getStringExtra(EXTRA_RSS_READ_OPEN_URL)
- )
- }
- }
-
- else -> null
- }
- }
-
- private fun resolveStartRoute(route: String?): MainRoute {
- return when (route) {
- "main" -> MainRouteHome
- "settings" -> MainRouteSettings
- "settings/other" -> MainRouteSettingsOther
- "settings/read" -> MainRouteSettingsRead
- "settings/cover" -> MainRouteSettingsCover
- "settings/theme" -> MainRouteSettingsTheme
- "settings/backup" -> MainRouteSettingsBackup
- "settings/custom_theme" -> MainRouteSettingsCustomTheme
- "settings/download_cache" -> MainRouteSettingsDownloadCache
- "import/local" -> MainRouteImportLocal
- "import/remote" -> MainRouteImportRemote
- ROUTE_MAIN -> MainRouteHome
- ROUTE_SETTINGS -> MainRouteSettings
- ROUTE_SETTINGS_OTHER -> MainRouteSettingsOther
- ROUTE_SETTINGS_READ -> MainRouteSettingsRead
- ROUTE_SETTINGS_COVER -> MainRouteSettingsCover
- ROUTE_SETTINGS_THEME -> MainRouteSettingsTheme
- ROUTE_SETTINGS_BACKUP -> MainRouteSettingsBackup
- ROUTE_SETTINGS_DOWNLOAD_CACHE -> MainRouteSettingsDownloadCache
- ROUTE_IMPORT_LOCAL -> MainRouteImportLocal
- ROUTE_IMPORT_REMOTE -> MainRouteImportRemote
- ROUTE_CACHE -> MainRouteCache(intent?.getLongExtra(EXTRA_CACHE_GROUP_ID, -1L) ?: -1L)
- ROUTE_BOOK_CACHE_MANAGE -> MainRouteBookCacheManage
- ROUTE_SEARCH -> MainRouteSearch(
- key = intent?.getStringExtra(EXTRA_SEARCH_KEY),
- scopeRaw = intent?.getStringExtra(EXTRA_SEARCH_SCOPE)
- )
- ROUTE_BOOK_INFO -> intent?.getStringExtra(EXTRA_BOOK_URL)
- ?.takeIf { it.isNotBlank() }
- ?.let { bookUrl ->
- MainRouteBookInfo(
- name = intent.getStringExtra(EXTRA_BOOK_NAME),
- author = intent.getStringExtra(EXTRA_BOOK_AUTHOR),
- bookUrl = bookUrl
- )
- } ?: MainRouteHome
- ROUTE_EXPLORE_SHOW -> intent?.getStringExtra(EXTRA_SOURCE_URL)
- ?.takeIf { it.isNotBlank() }
- ?.let { sourceUrl ->
- MainRouteExploreShow(
- title = intent.getStringExtra(EXTRA_EXPLORE_NAME),
- sourceUrl = sourceUrl,
- exploreUrl = intent.getStringExtra(EXTRA_EXPLORE_URL)
- )
- } ?: MainRouteHome
- else -> MainRouteHome
- }
- }
-
override fun setVariable(key: String, variable: String?) {
bookInfoVariableSetter?.invoke(key, variable)
}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainIntent.kt b/app/src/main/java/io/legado/app/ui/main/MainIntent.kt
new file mode 100644
index 000000000..24e769d93
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/main/MainIntent.kt
@@ -0,0 +1,152 @@
+package io.legado.app.ui.main
+
+import android.content.Context
+import android.content.Intent
+import io.legado.app.ui.config.ConfigTag
+
+object MainIntent {
+ const val EXTRA_START_ROUTE = "startRoute"
+ const val EXTRA_CACHE_GROUP_ID = "extra_cache_group_id"
+ const val EXTRA_SEARCH_KEY = "extra_search_key"
+ const val EXTRA_SEARCH_SCOPE = "extra_search_scope"
+ const val EXTRA_BOOK_NAME = "name"
+ const val EXTRA_BOOK_AUTHOR = "author"
+ const val EXTRA_BOOK_URL = "bookUrl"
+ const val EXTRA_EXPLORE_NAME = "exploreName"
+ const val EXTRA_SOURCE_URL = "sourceUrl"
+ const val EXTRA_EXPLORE_URL = "exploreUrl"
+
+ const val EXTRA_RSS_SOURCE_URL = "extra_rss_source_url"
+ const val EXTRA_RSS_SORT_URL = "extra_rss_sort_url"
+ const val EXTRA_RSS_KEY = "extra_rss_key"
+
+ const val EXTRA_RSS_READ_TITLE = "extra_rss_read_title"
+ const val EXTRA_RSS_READ_ORIGIN = "extra_rss_read_origin"
+ const val EXTRA_RSS_READ_LINK = "extra_rss_read_link"
+ const val EXTRA_RSS_READ_OPEN_URL = "extra_rss_read_open_url"
+
+ fun createLauncherIntent(context: Context): Intent {
+ val launcherComponent =
+ context.packageManager.getLaunchIntentForPackage(context.packageName)?.component
+ return if (launcherComponent != null) {
+ Intent().setComponent(launcherComponent)
+ } else {
+ Intent(context, MainActivity::class.java)
+ }
+ }
+
+ fun createHomeIntent(context: Context): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_MAIN)
+ }
+ }
+
+ fun createIntent(context: Context, configTag: String? = null): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, routeForConfigTag(configTag))
+ }
+ }
+
+ fun createRssSortIntent(
+ context: Context,
+ sourceUrl: String,
+ sortUrl: String? = null,
+ key: String? = null
+ ): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_RSS_SORT)
+ putExtra(EXTRA_RSS_SOURCE_URL, sourceUrl)
+ putExtra(EXTRA_RSS_SORT_URL, sortUrl)
+ putExtra(EXTRA_RSS_KEY, key)
+ }
+ }
+
+ fun createRssReadIntent(
+ context: Context,
+ title: String? = null,
+ origin: String,
+ link: String? = null,
+ openUrl: String? = null
+ ): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_RSS_READ)
+ putExtra(EXTRA_RSS_READ_TITLE, title)
+ putExtra(EXTRA_RSS_READ_ORIGIN, origin)
+ putExtra(EXTRA_RSS_READ_LINK, link)
+ putExtra(EXTRA_RSS_READ_OPEN_URL, openUrl)
+ }
+ }
+
+ fun createBookshelfManageScreenIntent(
+ context: Context,
+ groupId: Long = -1L
+ ): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_CACHE)
+ putExtra(EXTRA_CACHE_GROUP_ID, groupId)
+ }
+ }
+
+ fun createCacheIntent(
+ context: Context,
+ groupId: Long = -1L
+ ): Intent = createBookshelfManageScreenIntent(context, groupId)
+
+ fun createBookCacheManageIntent(context: Context): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_BOOK_CACHE_MANAGE)
+ }
+ }
+
+ fun createSearchIntent(
+ context: Context,
+ key: String? = null,
+ scopeRaw: String? = null
+ ): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_SEARCH)
+ putExtra(EXTRA_SEARCH_KEY, key)
+ putExtra(EXTRA_SEARCH_SCOPE, scopeRaw)
+ }
+ }
+
+ fun createBookInfoIntent(
+ context: Context,
+ name: String? = null,
+ author: String? = null,
+ bookUrl: String
+ ): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_BOOK_INFO)
+ putExtra(EXTRA_BOOK_NAME, name)
+ putExtra(EXTRA_BOOK_AUTHOR, author)
+ putExtra(EXTRA_BOOK_URL, bookUrl)
+ }
+ }
+
+ fun createExploreShowIntent(
+ context: Context,
+ exploreName: String? = null,
+ sourceUrl: String,
+ exploreUrl: String? = null
+ ): Intent {
+ return createLauncherIntent(context).apply {
+ putExtra(EXTRA_START_ROUTE, MainRouteConst.ROUTE_EXPLORE_SHOW)
+ putExtra(EXTRA_EXPLORE_NAME, exploreName)
+ putExtra(EXTRA_SOURCE_URL, sourceUrl)
+ putExtra(EXTRA_EXPLORE_URL, exploreUrl)
+ }
+ }
+
+ private fun routeForConfigTag(configTag: String?): String {
+ return when (configTag) {
+ ConfigTag.OTHER_CONFIG -> MainRouteConst.ROUTE_SETTINGS_OTHER
+ ConfigTag.READ_CONFIG -> MainRouteConst.ROUTE_SETTINGS_READ
+ ConfigTag.COVER_CONFIG -> MainRouteConst.ROUTE_SETTINGS_COVER
+ ConfigTag.THEME_CONFIG -> MainRouteConst.ROUTE_SETTINGS_THEME
+ ConfigTag.BACKUP_CONFIG -> MainRouteConst.ROUTE_SETTINGS_BACKUP
+ ConfigTag.DOWNLOAD_CACHE_CONFIG -> MainRouteConst.ROUTE_SETTINGS_DOWNLOAD_CACHE
+ else -> MainRouteConst.ROUTE_SETTINGS
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt b/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt
new file mode 100644
index 000000000..43b35bd92
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/main/MainNavGraph.kt
@@ -0,0 +1,423 @@
+package io.legado.app.ui.main
+
+import androidx.compose.animation.ExperimentalSharedTransitionApi
+import androidx.compose.animation.SharedTransitionScope
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
+import androidx.compose.runtime.LaunchedEffect
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation3.runtime.NavKey
+import androidx.navigation3.runtime.entryProvider
+import androidx.navigation3.ui.LocalNavAnimatedContentScope
+import androidx.navigation3.ui.NavDisplay
+import io.legado.app.ui.book.cache.manage.BookCacheManageRouteScreen
+import io.legado.app.ui.book.explore.ExploreShowScreen
+import io.legado.app.ui.book.import.local.ImportBookScreen
+import io.legado.app.ui.book.import.remote.RemoteBookScreen
+import io.legado.app.ui.book.info.BookInfoRouteScreen
+import io.legado.app.ui.book.info.BookInfoViewModel
+import io.legado.app.ui.book.manage.BookshelfManageRouteScreen
+import io.legado.app.ui.book.readRecord.ReadRecordOverviewScreen
+import io.legado.app.ui.book.readRecord.ReadRecordScreen
+import io.legado.app.ui.book.search.SearchIntent
+import io.legado.app.ui.book.search.SearchScreen
+import io.legado.app.ui.book.search.SearchViewModel
+import io.legado.app.ui.book.source.manage.BookSourceActivity
+import io.legado.app.ui.config.ConfigNavScreen
+import io.legado.app.ui.config.backupConfig.BackupConfigScreen
+import io.legado.app.ui.config.coverConfig.CoverConfigScreen
+import io.legado.app.ui.config.customTheme.CustomThemeScreen
+import io.legado.app.ui.config.downloadCacheConfig.DownloadCacheConfigScreen
+import io.legado.app.ui.config.otherConfig.OtherConfigScreen
+import io.legado.app.ui.config.readConfig.ReadConfigScreen
+import io.legado.app.ui.config.themeConfig.ThemeConfigScreen
+import io.legado.app.ui.config.themeManage.ThemeManageScreen
+import io.legado.app.ui.rss.article.MainRouteRssSort
+import io.legado.app.ui.rss.article.RssSortRouteScreen
+import io.legado.app.ui.rss.favorites.RssFavoritesScreen
+import io.legado.app.ui.rss.read.MainRouteRssRead
+import io.legado.app.ui.rss.read.RssReadRouteScreen
+import io.legado.app.ui.rss.subscription.RuleSubScreen
+import io.legado.app.utils.startActivity
+import io.legado.app.utils.startActivityForBook
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.koin.androidx.compose.koinViewModel
+
+@OptIn(ExperimentalSharedTransitionApi::class)
+fun MainActivity.mainEntryProvider(
+ backStack: MutableList,
+ useRail: Boolean,
+ sharedTransitionScope: SharedTransitionScope,
+ onNavigateToRoute: (NavKey) -> Unit,
+ onNavigateBack: () -> Unit,
+ onRegisterVariableSetter: (((String, String?) -> Unit)?) -> Unit
+) = entryProvider {
+ entry {
+ MainScreen(
+ useRail = useRail,
+ onOpenSettings = {
+ onNavigateToRoute(MainRouteSettings)
+ },
+ onNavigateToSearch = { key ->
+ onNavigateToRoute(
+ MainRouteSearch(
+ key = key?.trim()?.takeIf { it.isNotEmpty() }
+ )
+ )
+ },
+ onNavigateToRemoteImport = {
+ onNavigateToRoute(MainRouteImportRemote)
+ },
+ onNavigateToLocalImport = {
+ onNavigateToRoute(MainRouteImportLocal)
+ },
+ onNavigateToCache = { groupId ->
+ onNavigateToRoute(MainRouteCache(groupId))
+ },
+ onNavigateToBookCacheManage = {
+ onNavigateToRoute(MainRouteBookCacheManage)
+ },
+ onNavigateToBookInfo = { name, author, bookUrl ->
+ onNavigateToRoute(
+ MainRouteBookInfo(
+ name = name,
+ author = author,
+ bookUrl = bookUrl
+ )
+ )
+ },
+ onNavigateToExploreShow = { title, sourceUrl, exploreUrl ->
+ onNavigateToRoute(
+ MainRouteExploreShow(
+ title = title,
+ sourceUrl = sourceUrl,
+ exploreUrl = exploreUrl
+ )
+ )
+ },
+ onNavigateToRssSort = { sourceUrl, sortUrl, key ->
+ onNavigateToRoute(
+ MainRouteRssSort(
+ sourceUrl = sourceUrl,
+ sortUrl = sortUrl,
+ key = key
+ )
+ )
+ },
+ onNavigateToRssRead = { title, origin, link, openUrl ->
+ onNavigateToRoute(
+ MainRouteRssRead(
+ title = title,
+ origin = origin,
+ link = link,
+ openUrl = openUrl
+ )
+ )
+ },
+ onNavigateToRssFavorites = {
+ onNavigateToRoute(MainRouteRssFavorites)
+ },
+ onNavigateToRuleSub = {
+ onNavigateToRoute(MainRouteRuleSub)
+ },
+ onNavigateToReadRecord = {
+ onNavigateToRoute(MainRouteReadRecord)
+ },
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = LocalNavAnimatedContentScope.current,
+ )
+ }
+
+ entry {
+ ConfigNavScreen(
+ onBackClick = { onNavigateBack() },
+ onNavigateToOther = { backStack.add(MainRouteSettingsOther) },
+ onNavigateToRead = { backStack.add(MainRouteSettingsRead) },
+ onNavigateToCover = { backStack.add(MainRouteSettingsCover) },
+ onNavigateToTheme = { backStack.add(MainRouteSettingsTheme) },
+ onNavigateToBackup = { backStack.add(MainRouteSettingsBackup) },
+ onNavigateToDownloadCache = { backStack.add(MainRouteSettingsDownloadCache) }
+ )
+ }
+
+ entry {
+ OtherConfigScreen(onBackClick = { onNavigateBack() })
+ }
+
+ entry {
+ ReadConfigScreen(onBackClick = { onNavigateBack() })
+ }
+
+ entry {
+ CoverConfigScreen(onBackClick = { onNavigateBack() })
+ }
+
+ entry {
+ ThemeConfigScreen(
+ onBackClick = { onNavigateBack() },
+ onNavigateToCustomTheme = { backStack.add(MainRouteSettingsCustomTheme) },
+ onNavigateToThemeManage = { backStack.add(MainRouteSettingsThemeManage) }
+ )
+ }
+
+ entry {
+ BackupConfigScreen(onBackClick = { onNavigateBack() })
+ }
+
+ entry {
+ DownloadCacheConfigScreen(onBackClick = { onNavigateBack() })
+ }
+
+ entry {
+ CustomThemeScreen(
+ onBackClick = { onNavigateBack() }
+ )
+ }
+
+ entry {
+ ThemeManageScreen(onBackClick = { onNavigateBack() })
+ }
+
+ entry {
+ ImportBookScreen(
+ onBackClick = { onNavigateBack() }
+ )
+ }
+
+ entry {
+ RemoteBookScreen(
+ onBackClick = { onNavigateBack() }
+ )
+ }
+
+ entry { route ->
+ BookshelfManageRouteScreen(
+ groupId = route.groupId,
+ onBackClick = { onNavigateBack() },
+ onOpenBookInfo = { name, author, bookUrl ->
+ onNavigateToRoute(
+ MainRouteBookInfo(
+ name = name,
+ author = author,
+ bookUrl = bookUrl
+ )
+ )
+ }
+ )
+ }
+
+ entry {
+ BookCacheManageRouteScreen(
+ onBackClick = { onNavigateBack() }
+ )
+ }
+
+ entry { route ->
+ val searchViewModel = koinViewModel()
+
+ LaunchedEffect(route.key, route.scopeRaw, searchViewModel) {
+ searchViewModel.onIntent(
+ SearchIntent.Initialize(
+ key = route.key,
+ scopeRaw = route.scopeRaw
+ )
+ )
+ }
+
+ SearchScreen(
+ viewModel = searchViewModel,
+ onBack = {
+ searchViewModel.onIntent(SearchIntent.ClearSearchResults)
+ onNavigateBack()
+ },
+ onOpenBookInfo = { name, author, bookUrl ->
+ onNavigateToRoute(
+ MainRouteBookInfo(
+ name = name,
+ author = author,
+ bookUrl = bookUrl
+ )
+ )
+ },
+ onOpenSourceManage = {
+ this@mainEntryProvider.startActivity()
+ },
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = LocalNavAnimatedContentScope.current,
+ )
+ }
+
+ entry { route ->
+ RssSortRouteScreen(
+ sourceUrl = route.sourceUrl,
+ initialSortUrl = route.sortUrl,
+ onBackClick = { onNavigateBack() },
+ onOpenRead = { title, origin, link, openUrl ->
+ if (link?.contains("@js:") == true) {
+ onNavigateToRoute(
+ MainRouteRssSort(
+ sourceUrl = origin,
+ sortUrl = link
+ )
+ )
+ } else {
+ onNavigateToRoute(
+ MainRouteRssRead(
+ title = title,
+ origin = origin,
+ link = link,
+ openUrl = openUrl
+ )
+ )
+ }
+ }
+ )
+ }
+
+ entry { route ->
+ RssReadRouteScreen(
+ title = route.title,
+ origin = route.origin,
+ link = route.link,
+ openUrl = route.openUrl,
+ onBackClick = { onNavigateBack() }
+ )
+ }
+
+ entry {
+ RssFavoritesScreen(
+ onBackClick = { onNavigateBack() },
+ onOpenRead = { title, origin, link, openUrl ->
+ onNavigateToRoute(
+ MainRouteRssRead(
+ title = title,
+ origin = origin,
+ link = link,
+ openUrl = openUrl
+ )
+ )
+ }
+ )
+ }
+
+ entry {
+ RuleSubScreen(
+ onBackClick = { onNavigateBack() }
+ )
+ }
+
+ entry {
+ ReadRecordScreen(
+ onBackClick = { onNavigateBack() },
+ onBookClick = { name, author ->
+ lifecycleScope.launch {
+ val book = withContext(IO) {
+ io.legado.app.data.appDb.bookDao.getBook(name, author)
+ }
+ if (book != null) this@mainEntryProvider.startActivityForBook(book)
+ else {
+ onNavigateToRoute(MainRouteSearch(key = name))
+ }
+ }
+ },
+ onSummaryClick = {
+ onNavigateToRoute(MainRouteReadRecordOverview)
+ }
+ )
+ }
+
+ entry {
+ ReadRecordOverviewScreen(
+ onBackClick = { onNavigateBack() },
+ onBookClick = { name, author ->
+ lifecycleScope.launch {
+ val book = withContext(IO) {
+ io.legado.app.data.appDb.bookDao.getBook(name, author)
+ }
+ if (book != null) this@mainEntryProvider.startActivityForBook(book)
+ else {
+ onNavigateToRoute(MainRouteSearch(key = name))
+ }
+ }
+ }
+ )
+ }
+
+ entry(
+ metadata = NavDisplay.transitionSpec {
+ val from = initialState.key
+ val fromStr = from.toString()
+ if (from is MainRouteHome || from is MainRouteExploreShow || from is MainRouteSearch ||
+ fromStr.startsWith("MainRouteHome") || fromStr.startsWith("MainRouteExploreShow") || fromStr.startsWith(
+ "MainRouteSearch"
+ )
+ ) {
+ fadeIn(animationSpec = tween(300)) togetherWith
+ fadeOut(animationSpec = tween(300))
+ } else null
+ } + NavDisplay.popTransitionSpec {
+ val to = targetState.key
+ val toStr = to.toString()
+ if (to is MainRouteHome || to is MainRouteExploreShow || to is MainRouteSearch ||
+ toStr.startsWith("MainRouteHome") || toStr.startsWith("MainRouteExploreShow") || toStr.startsWith(
+ "MainRouteSearch"
+ )
+ ) {
+ fadeIn(animationSpec = tween(300)) togetherWith
+ fadeOut(animationSpec = tween(300))
+ } else null
+ } + NavDisplay.predictivePopTransitionSpec { _ ->
+ val to = targetState.key
+ val toStr = to.toString()
+ if (to is MainRouteHome || to is MainRouteExploreShow || to is MainRouteSearch ||
+ toStr.startsWith("MainRouteHome") || toStr.startsWith("MainRouteExploreShow") || toStr.startsWith(
+ "MainRouteSearch"
+ )
+ ) {
+ fadeIn(animationSpec = tween(300)) togetherWith
+ fadeOut(animationSpec = tween(300))
+ } else null
+ }
+ ) { route ->
+ val bookInfoViewModel = koinViewModel()
+ BookInfoRouteScreen(
+ bookUrl = route.bookUrl,
+ viewModel = bookInfoViewModel,
+ onBack = { onNavigateBack() },
+ onFinish = { _, _ -> onNavigateBack() },
+ onOpenSearch = { keyword ->
+ onNavigateToRoute(MainRouteSearch(key = keyword))
+ },
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = LocalNavAnimatedContentScope.current,
+ sharedCoverKey = bookCoverSharedElementKey(route.bookUrl),
+ onRegisterVariableSetter = { setter ->
+ onRegisterVariableSetter(setter)
+ }
+ )
+ }
+
+ entry { route ->
+ ExploreShowScreen(
+ title = route.title ?: "探索",
+ sourceUrl = route.sourceUrl,
+ exploreUrl = route.exploreUrl,
+ onBack = { onNavigateBack() },
+ onBookClick = { book ->
+ onNavigateToRoute(
+ MainRouteBookInfo(
+ name = book.name,
+ author = book.author,
+ bookUrl = book.bookUrl
+ )
+ )
+ },
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = LocalNavAnimatedContentScope.current,
+ )
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt b/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt
new file mode 100644
index 000000000..eb170274b
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/main/MainNavKey.kt
@@ -0,0 +1,106 @@
+package io.legado.app.ui.main
+
+import androidx.navigation3.runtime.NavKey
+import kotlinx.serialization.Serializable
+
+@Serializable
+sealed interface MainRoute : NavKey
+
+@Serializable
+data object MainRouteHome : MainRoute
+
+@Serializable
+data object MainRouteSettings : MainRoute
+
+@Serializable
+data object MainRouteSettingsOther : MainRoute
+
+@Serializable
+data object MainRouteSettingsRead : MainRoute
+
+@Serializable
+data object MainRouteSettingsCover : MainRoute
+
+@Serializable
+data object MainRouteSettingsTheme : MainRoute
+
+@Serializable
+data object MainRouteSettingsBackup : MainRoute
+
+@Serializable
+data object MainRouteSettingsCustomTheme : MainRoute
+
+@Serializable
+data object MainRouteSettingsThemeManage : MainRoute
+
+@Serializable
+data object MainRouteSettingsDownloadCache : MainRoute
+
+@Serializable
+data object MainRouteImportLocal : MainRoute
+
+@Serializable
+data object MainRouteImportRemote : MainRoute
+
+@Serializable
+data object MainRouteReadRecord : MainRoute
+
+@Serializable
+data object MainRouteReadRecordOverview : MainRoute
+
+@Serializable
+data class MainRouteCache(val groupId: Long) : MainRoute
+
+@Serializable
+data object MainRouteBookCacheManage : MainRoute
+
+@Serializable
+data class MainRouteSearch(
+ val key: String?,
+ val scopeRaw: String? = null
+) : MainRoute
+
+@Serializable
+data class MainRouteBookInfo(
+ val name: String?,
+ val author: String?,
+ val bookUrl: String,
+) : MainRoute
+
+@Serializable
+data object MainRouteRssFavorites : MainRoute
+
+@Serializable
+data object MainRouteRuleSub : MainRoute
+
+@Serializable
+data class MainRouteExploreShow(
+ val title: String?,
+ val sourceUrl: String,
+ val exploreUrl: String?,
+) : MainRoute
+
+object MainRouteConst {
+ const val ROUTE_MAIN = "main"
+ const val ROUTE_SETTINGS = "settings"
+ const val ROUTE_SETTINGS_OTHER = "settings/other"
+ const val ROUTE_SETTINGS_READ = "settings/read"
+ const val ROUTE_SETTINGS_COVER = "settings/cover"
+ const val ROUTE_SETTINGS_THEME = "settings/theme"
+ const val ROUTE_SETTINGS_BACKUP = "settings/backup"
+ const val ROUTE_SETTINGS_CUSTOM_THEME = "settings/custom_theme"
+ const val ROUTE_SETTINGS_DOWNLOAD_CACHE = "settings/download_cache"
+ const val ROUTE_IMPORT_LOCAL = "import/local"
+ const val ROUTE_IMPORT_REMOTE = "import/remote"
+ const val ROUTE_CACHE = "cache"
+ const val ROUTE_BOOK_CACHE_MANAGE = "book/cache/manage"
+ const val ROUTE_SEARCH = "search"
+ const val ROUTE_BOOK_INFO = "book/info"
+ const val ROUTE_EXPLORE_SHOW = "explore/show"
+ const val ROUTE_RSS_SORT = "rss/sort"
+ const val ROUTE_RSS_READ = "rss/read"
+ const val ROUTE_RSS_FAVORITES = "rss/favorites"
+ const val ROUTE_RULE_SUB = "rss/rule_sub"
+ const val ROUTE_READ_RECORD = "read_record"
+ const val ROUTE_READ_RECORD_OVERVIEW = "read_record_overview"
+}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt b/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt
new file mode 100644
index 000000000..ae71e697e
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/main/MainNavigator.kt
@@ -0,0 +1,253 @@
+package io.legado.app.ui.main
+
+import android.app.Activity
+import android.content.Intent
+import androidx.navigation3.runtime.NavKey
+import io.legado.app.ui.rss.article.MainRouteRssSort
+import io.legado.app.ui.rss.read.MainRouteRssRead
+
+object MainNavigator {
+
+ fun navigateToRoute(backStack: MutableList, route: NavKey) {
+ val currentRoute = backStack.lastOrNull()
+ if (currentRoute == route) return
+
+ when (route) {
+ MainRouteHome -> {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ }
+
+ MainRouteSettings -> {
+ if (currentRoute == MainRouteHome) {
+ backStack.add(MainRouteSettings)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(MainRouteSettings)
+ }
+ }
+
+ MainRouteSettingsOther,
+ MainRouteSettingsRead,
+ MainRouteSettingsCover,
+ MainRouteSettingsTheme,
+ MainRouteSettingsBackup,
+ MainRouteSettingsCustomTheme,
+ MainRouteSettingsThemeManage,
+ MainRouteSettingsDownloadCache -> {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(MainRouteSettings)
+ backStack.add(route)
+ }
+
+ MainRouteImportLocal,
+ MainRouteImportRemote,
+ is MainRouteCache,
+ MainRouteBookCacheManage -> {
+ if (currentRoute == MainRouteHome) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ is MainRouteSearch -> {
+ if (
+ currentRoute == MainRouteHome ||
+ currentRoute is MainRouteBookInfo ||
+ currentRoute is MainRouteExploreShow ||
+ currentRoute is MainRouteSearch
+ ) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ is MainRouteBookInfo -> {
+ if (
+ currentRoute == MainRouteHome ||
+ currentRoute is MainRouteSearch ||
+ currentRoute is MainRouteExploreShow ||
+ currentRoute is MainRouteBookInfo
+ ) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ is MainRouteExploreShow -> {
+ if (
+ currentRoute == MainRouteHome ||
+ currentRoute is MainRouteBookInfo ||
+ currentRoute is MainRouteSearch ||
+ currentRoute is MainRouteExploreShow
+ ) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ is MainRouteRssSort -> {
+ if (
+ currentRoute == MainRouteHome ||
+ currentRoute is MainRouteRssSort ||
+ currentRoute is MainRouteRssRead
+ ) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ is MainRouteRssRead -> {
+ if (
+ currentRoute == MainRouteHome ||
+ currentRoute is MainRouteRssSort ||
+ currentRoute is MainRouteRssRead
+ ) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ MainRouteReadRecord -> {
+ if (currentRoute == MainRouteHome) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+
+ MainRouteReadRecordOverview -> {
+ if (currentRoute == MainRouteHome || currentRoute == MainRouteReadRecord) {
+ backStack.add(route)
+ } else {
+ backStack.clear()
+ backStack.add(MainRouteHome)
+ backStack.add(route)
+ }
+ }
+ }
+ }
+
+ fun navigateBack(activity: Activity, backStack: MutableList) {
+ if (backStack.size > 1) {
+ backStack.removeLastOrNull()
+ } else {
+ activity.finish()
+ }
+ }
+
+ fun resolveStartRoute(intent: Intent?): NavKey {
+ val route = intent?.getStringExtra(MainIntent.EXTRA_START_ROUTE)
+ resolveRssStartRoute(route, intent)?.let { return it }
+ return resolveStartRoute(route, intent)
+ }
+
+ private fun resolveRssStartRoute(route: String?, intent: Intent?): NavKey? {
+ return when (route) {
+ MainRouteConst.ROUTE_RSS_SORT -> {
+ val sourceUrl = intent?.getStringExtra(MainIntent.EXTRA_RSS_SOURCE_URL)
+ if (sourceUrl.isNullOrBlank()) {
+ null
+ } else {
+ MainRouteRssSort(
+ sourceUrl = sourceUrl,
+ sortUrl = intent.getStringExtra(MainIntent.EXTRA_RSS_SORT_URL),
+ key = intent.getStringExtra(MainIntent.EXTRA_RSS_KEY)
+ )
+ }
+ }
+
+ MainRouteConst.ROUTE_RSS_READ -> {
+ val origin = intent?.getStringExtra(MainIntent.EXTRA_RSS_READ_ORIGIN)
+ if (origin.isNullOrBlank()) {
+ null
+ } else {
+ MainRouteRssRead(
+ title = intent.getStringExtra(MainIntent.EXTRA_RSS_READ_TITLE),
+ origin = origin,
+ link = intent.getStringExtra(MainIntent.EXTRA_RSS_READ_LINK),
+ openUrl = intent.getStringExtra(MainIntent.EXTRA_RSS_READ_OPEN_URL)
+ )
+ }
+ }
+
+ MainRouteConst.ROUTE_RSS_FAVORITES -> MainRouteRssFavorites
+
+ MainRouteConst.ROUTE_RULE_SUB -> MainRouteRuleSub
+
+ else -> null
+ }
+ }
+
+ private fun resolveStartRoute(route: String?, intent: Intent?): MainRoute {
+ return when (route) {
+ MainRouteConst.ROUTE_MAIN -> MainRouteHome
+ MainRouteConst.ROUTE_SETTINGS -> MainRouteSettings
+ MainRouteConst.ROUTE_SETTINGS_OTHER -> MainRouteSettingsOther
+ MainRouteConst.ROUTE_SETTINGS_READ -> MainRouteSettingsRead
+ MainRouteConst.ROUTE_SETTINGS_COVER -> MainRouteSettingsCover
+ MainRouteConst.ROUTE_SETTINGS_THEME -> MainRouteSettingsTheme
+ MainRouteConst.ROUTE_SETTINGS_BACKUP -> MainRouteSettingsBackup
+ MainRouteConst.ROUTE_SETTINGS_CUSTOM_THEME -> MainRouteSettingsCustomTheme
+ MainRouteConst.ROUTE_SETTINGS_DOWNLOAD_CACHE -> MainRouteSettingsDownloadCache
+ MainRouteConst.ROUTE_IMPORT_LOCAL -> MainRouteImportLocal
+ MainRouteConst.ROUTE_IMPORT_REMOTE -> MainRouteImportRemote
+ MainRouteConst.ROUTE_CACHE -> MainRouteCache(
+ intent?.getLongExtra(
+ MainIntent.EXTRA_CACHE_GROUP_ID,
+ -1L
+ ) ?: -1L
+ )
+
+ MainRouteConst.ROUTE_BOOK_CACHE_MANAGE -> MainRouteBookCacheManage
+ MainRouteConst.ROUTE_SEARCH -> MainRouteSearch(
+ key = intent?.getStringExtra(MainIntent.EXTRA_SEARCH_KEY),
+ scopeRaw = intent?.getStringExtra(MainIntent.EXTRA_SEARCH_SCOPE)
+ )
+
+ MainRouteConst.ROUTE_BOOK_INFO -> intent?.getStringExtra(MainIntent.EXTRA_BOOK_URL)
+ ?.takeIf { it.isNotBlank() }
+ ?.let { bookUrl ->
+ MainRouteBookInfo(
+ name = intent.getStringExtra(MainIntent.EXTRA_BOOK_NAME),
+ author = intent.getStringExtra(MainIntent.EXTRA_BOOK_AUTHOR),
+ bookUrl = bookUrl
+ )
+ } ?: MainRouteHome
+
+ MainRouteConst.ROUTE_EXPLORE_SHOW -> intent?.getStringExtra(MainIntent.EXTRA_SOURCE_URL)
+ ?.takeIf { it.isNotBlank() }
+ ?.let { sourceUrl ->
+ MainRouteExploreShow(
+ title = intent.getStringExtra(MainIntent.EXTRA_EXPLORE_NAME),
+ sourceUrl = sourceUrl,
+ exploreUrl = intent.getStringExtra(MainIntent.EXTRA_EXPLORE_URL)
+ )
+ } ?: MainRouteHome
+
+ else -> MainRouteHome
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt
index 8354fba85..555da20fc 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainScreen.kt
@@ -113,6 +113,8 @@ fun MainScreen(
onNavigateToExploreShow: (title: String?, sourceUrl: String, exploreUrl: String?) -> Unit,
onNavigateToRssSort: (sourceUrl: String, sortUrl: String?, key: String?) -> Unit,
onNavigateToRssRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit,
+ onNavigateToRssFavorites: () -> Unit,
+ onNavigateToRuleSub: () -> Unit,
onNavigateToReadRecord: () -> Unit,
sharedTransitionScope: SharedTransitionScope? = null,
animatedVisibilityScope: AnimatedVisibilityScope? = null,
@@ -383,7 +385,9 @@ fun MainScreen(
},
onOpenRead = { title, origin, link, openUrl ->
onNavigateToRssRead(title, origin, link, openUrl)
- }
+ },
+ onOpenFavorites = onNavigateToRssFavorites,
+ onOpenRuleSub = onNavigateToRuleSub
)
MainDestination.My -> MyScreen(
onOpenSettings = onOpenSettings,
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt
index e99927cf3..2ed1be80c 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookItem.kt
@@ -26,7 +26,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material3.HorizontalDivider
-import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -36,12 +35,12 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import io.legado.app.R
@@ -49,13 +48,11 @@ import io.legado.app.constant.BookType
import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.card.GlassCard
import io.legado.app.ui.widget.components.card.NormalCard
import io.legado.app.ui.widget.components.card.TextCard
-import io.legado.app.ui.widget.components.cover.CoilBookCover
import io.legado.app.ui.widget.components.cover.BookshelfCover
+import io.legado.app.ui.widget.components.cover.CoilBookCover
import io.legado.app.ui.widget.components.icon.AppIcon
-import io.legado.app.ui.widget.components.icon.AppIcons
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.toTimeAgo
@@ -90,10 +87,11 @@ fun BookshelfItem(
onClick: () -> Unit,
onLongClick: (() -> Unit)?
) {
- val containerColor = if (!isGrid && BookshelfConfig.bookshelfCardColor != 0) {
- Color(BookshelfConfig.bookshelfCardColor)
- } else if (ThemeConfig.enableDeepPersonalization && ThemeConfig.secondaryThemeColor != 0) {
- Color(ThemeConfig.secondaryThemeColor)
+ val isDark = LegadoTheme.isDark
+ val bookshelfCardColor =
+ if (isDark) BookshelfConfig.bookshelfCardColorDark else BookshelfConfig.bookshelfCardColor
+ val containerColor = if (!isGrid && bookshelfCardColor != 0) {
+ Color(bookshelfCardColor)
} else {
LegadoTheme.colorScheme.cardContainer
}
@@ -189,7 +187,7 @@ fun BookshelfItem(
Box(
modifier = Modifier
.align(Alignment.Top)
- .width(if (!isCompact) 80.dp else 56.dp)
+ .width(if (!isCompact) 84.dp else 56.dp)
.padding(end = 8.dp)
) {
Box(
@@ -209,11 +207,13 @@ fun BookshelfItem(
}
}
Column(
- modifier = Modifier.weight(1f).padding(top = 8.dp, bottom = 8.dp, end = 8.dp),
+ modifier = Modifier
+ .weight(1f)
+ .padding(top = 8.dp, bottom = 8.dp, end = 8.dp),
verticalArrangement = Arrangement.Center
) {
Row(
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.Top
) {
AppText(
text = title,
@@ -222,11 +222,15 @@ fun BookshelfItem(
} else {
LegadoTheme.typography.titleMediumEmphasized
},
- maxLines = 1,
+ maxLines = BookshelfConfig.bookshelfTitleMaxLines,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
- titleEnd?.invoke()
+ titleEnd?.let {
+ Box(modifier = Modifier.padding(top = 4.dp)) {
+ it.invoke()
+ }
+ }
}
subTitle?.let {
AppText(
@@ -296,68 +300,82 @@ fun BookGroupCover(
modifier = Modifier.fillMaxSize()
)
} else {
- Column(modifier = Modifier.fillMaxSize()) {
- Row(modifier = Modifier.weight(1f)) {
- Box(
- modifier = Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(1.dp)
- ) {
- books.getOrNull(0)?.let {
- CoilBookCover(
- name = it.name,
- author = it.author,
- path = it.getDisplayCover(),
- modifier = Modifier.fillMaxSize()
- )
- }
- }
- Box(
- modifier = Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(1.dp)
- ) {
- books.getOrNull(1)?.let {
- CoilBookCover(
- name = it.name,
- author = it.author,
- path = it.getDisplayCover(),
- modifier = Modifier.fillMaxSize()
- )
- }
+ Box(
+ modifier = Modifier.run {
+ if (BookshelfConfig.bookshelfCoverShadow) {
+ background(LegadoTheme.colorScheme.surface)
+ } else {
+ this
}
}
- Row(modifier = Modifier.weight(1f)) {
- Box(
- modifier = Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(1.dp)
- ) {
- books.getOrNull(2)?.let {
- CoilBookCover(
- name = it.name,
- author = it.author,
- path = it.getDisplayCover(),
- modifier = Modifier.fillMaxSize()
- )
+ ) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 1.dp),
+ ) {
+ Row(modifier = Modifier.weight(1f)) {
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight()
+ .padding(1.dp)
+ ) {
+ books.getOrNull(0)?.let {
+ CoilBookCover(
+ name = it.name,
+ author = it.author,
+ path = it.getDisplayCover(),
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ }
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight()
+ .padding(1.dp)
+ ) {
+ books.getOrNull(1)?.let {
+ CoilBookCover(
+ name = it.name,
+ author = it.author,
+ path = it.getDisplayCover(),
+ modifier = Modifier.fillMaxSize()
+ )
+ }
}
}
- Box(
- modifier = Modifier
- .weight(1f)
- .fillMaxHeight()
- .padding(1.dp)
- ) {
- books.getOrNull(3)?.let {
- CoilBookCover(
- name = it.name,
- author = it.author,
- path = it.getDisplayCover(),
- modifier = Modifier.fillMaxSize()
- )
+ Row(modifier = Modifier.weight(1f)) {
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight()
+ .padding(1.dp)
+ ) {
+ books.getOrNull(2)?.let {
+ CoilBookCover(
+ name = it.name,
+ author = it.author,
+ path = it.getDisplayCover(),
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ }
+ Box(
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight()
+ .padding(1.dp)
+ ) {
+ books.getOrNull(3)?.let {
+ CoilBookCover(
+ name = it.name,
+ author = it.author,
+ path = it.getDisplayCover(),
+ modifier = Modifier.fillMaxSize()
+ )
+ }
}
}
}
@@ -482,13 +500,16 @@ fun BookGroupItemHorizontalCovers(
onBookClick: ((BookShelfItem) -> Unit)? = null
) {
Column {
+ val isDark = LegadoTheme.isDark
+ val bookshelfCardColor =
+ if (isDark) BookshelfConfig.bookshelfCardColorDark else BookshelfConfig.bookshelfCardColor
NormalCard(
modifier = modifier
.fillMaxWidth()
.padding(all = 4.dp),
cornerRadius = 12.dp,
- containerColor = if (BookshelfConfig.bookshelfCardColor != 0) {
- Color(BookshelfConfig.bookshelfCardColor)
+ containerColor = if (bookshelfCardColor != 0) {
+ Color(bookshelfCardColor)
} else {
LegadoTheme.colorScheme.cardContainer
},
@@ -634,7 +655,9 @@ fun BookItem(
path = book.getDisplayCover(),
isUpdating = isUpdating,
modifier = modifier,
- coverModifier = Modifier.fillMaxWidth().aspectRatio(5f / 7f),
+ coverModifier = Modifier
+ .fillMaxWidth()
+ .aspectRatio(5f / 7f),
sourceOrigin = book.origin,
badgeText = if (layoutMode != 0) unreadText else null,
showBadgeDot = BookshelfConfig.showUnread && BookshelfConfig.showUnreadNew && book.isNew,
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt
index fc8f5bf3d..4091aa5e8 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt
@@ -3,11 +3,16 @@ package io.legado.app.ui.main.bookshelf
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
@@ -17,6 +22,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringArrayResource
@@ -27,8 +34,8 @@ import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
import io.legado.app.ui.config.themeConfig.LabelColorManageSheet
import io.legado.app.ui.config.themeConfig.ThemeConfig
import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.card.GlassCard
import io.legado.app.ui.widget.components.dialog.ColorPickerSheet
+import io.legado.app.ui.widget.components.divider.PillDivider
import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
import io.legado.app.ui.widget.components.settingItem.CompactClickableSettingItem
import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
@@ -45,15 +52,13 @@ fun BookshelfConfigSheet(
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
var showLabelColorManage by remember { mutableStateOf(false) }
var showColorPicker by remember { mutableStateOf(false) }
+ var showColorPickerDark by remember { mutableStateOf(false) }
AppModalBottomSheet(
title = stringResource(R.string.bookshelf_layout),
show = show,
onDismissRequest = onDismissRequest
) {
- GlassCard() {
-
- }
Column(
modifier = Modifier
.fillMaxWidth()
@@ -99,21 +104,12 @@ fun BookshelfConfigSheet(
if (isLandscape) BookshelfConfig.bookshelfFolderLayoutModeLandscape
else BookshelfConfig.bookshelfFolderLayoutModePortrait
- CompactDropdownSettingItem(
- title = stringResource(R.string.layout_mode),
- description = stringResource(if (isLandscape) R.string.screen_landscape else R.string.screen_portrait),
- selectedValue = layoutMode.toString(),
- displayEntries = arrayOf(stringResource(R.string.layout_mode_list),stringResource(R.string.layout_mode_grid)),
- entryValues = arrayOf("0", "1"),
- onValueChange = {
- if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape = it.toInt()
- else BookshelfConfig.bookshelfLayoutModePortrait = it.toInt()
- }
- )
-
// Folder Layout Mode
AnimatedVisibility(visible = BookshelfConfig.bookGroupStyle == 2) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+
+ PillDivider()
+
CompactDropdownSettingItem(
title = "文件夹布局模式",
description = stringResource(if (isLandscape) R.string.screen_landscape else R.string.screen_portrait),
@@ -157,33 +153,54 @@ fun BookshelfConfigSheet(
}
)
}
+
+ AnimatedVisibility(
+ visible = BookshelfConfig.bookGroupStyle == 2 && folderLayoutMode == 0
+ ) {
+ Column(
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ CompactDropdownSettingItem(
+ title = "文件夹列表样式",
+ selectedValue = BookshelfConfig.bookshelfGroupListStyle.toString(),
+ displayEntries = arrayOf("默认", "紧凑", "横排封面"),
+ entryValues = arrayOf("0", "1", "2"),
+ onValueChange = {
+ BookshelfConfig.bookshelfGroupListStyle = it.toInt()
+ }
+ )
+ AnimatedVisibility(visible = BookshelfConfig.bookshelfGroupListStyle == 2) {
+ CompactSliderSettingItem(
+ title = "横排封面数量",
+ value = BookshelfConfig.bookshelfGroupCoverCount.toFloat(),
+ valueRange = 1f..10f,
+ steps = 9,
+ onValueChange = {
+ BookshelfConfig.bookshelfGroupCoverCount = it.toInt()
+ }
+ )
+ }
+ }
+ }
+
+ PillDivider()
}
}
- AnimatedVisibility(
- visible = BookshelfConfig.bookGroupStyle == 2 && folderLayoutMode == 0
- ) {
- Column(
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- CompactDropdownSettingItem(
- title = "文件夹列表样式",
- selectedValue = BookshelfConfig.bookshelfGroupListStyle.toString(),
- displayEntries = arrayOf("默认", "紧凑", "横排封面"),
- entryValues = arrayOf("0", "1", "2"),
- onValueChange = { BookshelfConfig.bookshelfGroupListStyle = it.toInt() }
- )
- AnimatedVisibility(visible = BookshelfConfig.bookshelfGroupListStyle == 2) {
- CompactSliderSettingItem(
- title = "横排封面数量",
- value = BookshelfConfig.bookshelfGroupCoverCount.toFloat(),
- valueRange = 1f..10f,
- steps = 9,
- onValueChange = { BookshelfConfig.bookshelfGroupCoverCount = it.toInt() }
- )
- }
+ CompactDropdownSettingItem(
+ title = stringResource(R.string.layout_mode),
+ description = stringResource(if (isLandscape) R.string.screen_landscape else R.string.screen_portrait),
+ selectedValue = layoutMode.toString(),
+ displayEntries = arrayOf(
+ stringResource(R.string.layout_mode_list),
+ stringResource(R.string.layout_mode_grid)
+ ),
+ entryValues = arrayOf("0", "1"),
+ onValueChange = {
+ if (isLandscape) BookshelfConfig.bookshelfLayoutModeLandscape = it.toInt()
+ else BookshelfConfig.bookshelfLayoutModePortrait = it.toInt()
}
- }
+ )
AnimatedVisibility(
visible = layoutMode == 1
@@ -243,103 +260,135 @@ fun BookshelfConfigSheet(
)
CompactClickableSettingItem(
- title = "卡片背景颜色",
+ title = "日间卡片背景颜色",
color = LegadoTheme.colorScheme.surface,
- onClick = { showColorPicker = true }
+ onClick = { showColorPicker = true },
+ trailingContent = {
+ if (BookshelfConfig.bookshelfCardColor != 0) {
+ Box(
+ modifier = Modifier
+ .size(20.dp)
+ .clip(CircleShape)
+ .background(Color(BookshelfConfig.bookshelfCardColor))
+ .border(
+ 1.dp,
+ MaterialTheme.colorScheme.outlineVariant,
+ CircleShape
+ )
+ )
+ }
+ }
)
- val isHorizontalCovers = BookshelfConfig.bookGroupStyle == 2 && BookshelfConfig.bookshelfGroupListStyle == 2
+ CompactClickableSettingItem(
+ title = "夜间卡片背景颜色",
+ color = LegadoTheme.colorScheme.surface,
+ onClick = { showColorPickerDark = true },
+ trailingContent = {
+ if (BookshelfConfig.bookshelfCardColorDark != 0) {
+ Box(
+ modifier = Modifier
+ .size(20.dp)
+ .clip(CircleShape)
+ .background(Color(BookshelfConfig.bookshelfCardColorDark))
+ .border(
+ 1.dp,
+ MaterialTheme.colorScheme.outlineVariant,
+ CircleShape
+ )
+ )
+ }
+ }
+ )
- AnimatedVisibility(visible = !isHorizontalCovers) {
+ if (BookshelfConfig.bookGroupStyle != 2) {
+ CompactSwitchSettingItem(
+ title = stringResource(R.string.compact_mode),
+ checked = BookshelfConfig.bookshelfLayoutCompact,
+ color = LegadoTheme.colorScheme.surface,
+ onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
+ )
+ }
+
+ val listColCount =
+ if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
+ CompactSliderSettingItem(
+ title = stringResource(R.string.number_rows_columns),
+ value = listColCount.toFloat(),
+ valueRange = 1f..5f,
+ steps = 4,
+ onValueChange = {
+ if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape =
+ it.toInt()
+ else BookshelfConfig.bookshelfLayoutListPortrait = it.toInt()
+ }
+ )
+
+ CompactSwitchSettingItem(
+ title = "显示更多信息",
+ checked = BookshelfConfig.showBookIntro,
+ color = LegadoTheme.colorScheme.surface,
+ onCheckedChange = { BookshelfConfig.showBookIntro = it }
+ )
+
+ AnimatedVisibility(visible = BookshelfConfig.showBookIntro) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
- if (BookshelfConfig.bookGroupStyle != 2) {
- CompactSwitchSettingItem(
- title = stringResource(R.string.compact_mode),
- checked = BookshelfConfig.bookshelfLayoutCompact,
- color = LegadoTheme.colorScheme.surface,
- onCheckedChange = { BookshelfConfig.bookshelfLayoutCompact = it }
- )
- }
-
- val listColCount =
- if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape else BookshelfConfig.bookshelfLayoutListPortrait
- CompactSliderSettingItem(
- title = stringResource(R.string.number_rows_columns),
- value = listColCount.toFloat(),
- valueRange = 1f..5f,
- steps = 4,
- onValueChange = {
- if (isLandscape) BookshelfConfig.bookshelfLayoutListLandscape =
- it.toInt()
- else BookshelfConfig.bookshelfLayoutListPortrait = it.toInt()
+ CompactSwitchSettingItem(
+ title = "显示最新章节",
+ checked = BookshelfConfig.bookshelfShowLatestChapter,
+ color = LegadoTheme.colorScheme.surface,
+ onCheckedChange = {
+ BookshelfConfig.bookshelfShowLatestChapter = it
}
)
CompactSwitchSettingItem(
- title = "显示更多信息",
- checked = BookshelfConfig.showBookIntro,
+ title = "显示简介",
+ checked = BookshelfConfig.bookshelfShowIntro,
color = LegadoTheme.colorScheme.surface,
- onCheckedChange = { BookshelfConfig.showBookIntro = it }
+ onCheckedChange = { BookshelfConfig.bookshelfShowIntro = it }
)
-
- AnimatedVisibility(visible = BookshelfConfig.showBookIntro) {
+ AnimatedVisibility(
+ visible = BookshelfConfig.bookshelfShowIntro
+ ) {
+ CompactSliderSettingItem(
+ title = "简介行数",
+ description = if (BookshelfConfig.bookshelfIntroMaxLines == 0) "显示全部简介" else "显示 ${BookshelfConfig.bookshelfIntroMaxLines} 行简介",
+ value = BookshelfConfig.bookshelfIntroMaxLines.toFloat(),
+ valueRange = 0f..10f,
+ steps = 10,
+ onValueChange = {
+ BookshelfConfig.bookshelfIntroMaxLines = it.toInt()
+ }
+ )
+ }
+ CompactSwitchSettingItem(
+ title = "显示标签",
+ checked = BookshelfConfig.bookshelfShowTag,
+ color = LegadoTheme.colorScheme.surface,
+ onCheckedChange = { BookshelfConfig.bookshelfShowTag = it }
+ )
+ AnimatedVisibility(
+ visible = BookshelfConfig.bookshelfShowTag
+ ) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
CompactSwitchSettingItem(
- title = "显示最新章节",
- checked = BookshelfConfig.bookshelfShowLatestChapter,
+ title = "自定义标签颜色",
+ checked = ThemeConfig.enableCustomTagColors,
color = LegadoTheme.colorScheme.surface,
- onCheckedChange = { BookshelfConfig.bookshelfShowLatestChapter = it }
+ onCheckedChange = { ThemeConfig.enableCustomTagColors = it }
)
-
- CompactSwitchSettingItem(
- title = "显示简介",
- checked = BookshelfConfig.bookshelfShowIntro,
- color = LegadoTheme.colorScheme.surface,
- onCheckedChange = { BookshelfConfig.bookshelfShowIntro = it }
- )
- AnimatedVisibility(
- visible = BookshelfConfig.bookshelfShowIntro
- ) {
- CompactSliderSettingItem(
- title = "简介行数",
- description = if (BookshelfConfig.bookshelfIntroMaxLines == 0) "显示全部简介" else "显示 ${BookshelfConfig.bookshelfIntroMaxLines} 行简介",
- value = BookshelfConfig.bookshelfIntroMaxLines.toFloat(),
- valueRange = 0f..10f,
- steps = 10,
- onValueChange = { BookshelfConfig.bookshelfIntroMaxLines = it.toInt() }
+ AnimatedVisibility(visible = ThemeConfig.enableCustomTagColors) {
+ CompactClickableSettingItem(
+ title = "管理标签颜色",
+ color = LegadoTheme.colorScheme.surface,
+ onClick = { showLabelColorManage = true }
)
}
- CompactSwitchSettingItem(
- title = "显示标签",
- checked = BookshelfConfig.bookshelfShowTag,
- color = LegadoTheme.colorScheme.surface,
- onCheckedChange = { BookshelfConfig.bookshelfShowTag = it }
- )
- AnimatedVisibility(
- visible = BookshelfConfig.bookshelfShowTag
- ) {
- Column(
- verticalArrangement = Arrangement.spacedBy(8.dp)
- ) {
- CompactSwitchSettingItem(
- title = "自定义标签颜色",
- checked = ThemeConfig.enableCustomTagColors,
- color = LegadoTheme.colorScheme.surface,
- onCheckedChange = { ThemeConfig.enableCustomTagColors = it }
- )
- AnimatedVisibility(visible = ThemeConfig.enableCustomTagColors) {
- CompactClickableSettingItem(
- title = "管理标签颜色",
- color = LegadoTheme.colorScheme.surface,
- onClick = { showLabelColorManage = true }
- )
- }
- }
- }
}
}
}
@@ -347,6 +396,8 @@ fun BookshelfConfigSheet(
}
}
+
+
CompactSliderSettingItem(
title = stringResource(R.string.max_title_lines),
value = BookshelfConfig.bookshelfTitleMaxLines.toFloat(),
@@ -448,5 +499,12 @@ fun BookshelfConfigSheet(
onDismissRequest = { showColorPicker = false },
onColorSelected = { BookshelfConfig.bookshelfCardColor = it }
)
+
+ ColorPickerSheet(
+ show = showColorPickerDark,
+ initialColor = if (BookshelfConfig.bookshelfCardColorDark != 0) BookshelfConfig.bookshelfCardColorDark else LegadoTheme.colorScheme.surfaceVariant.toArgb(),
+ onDismissRequest = { showColorPickerDark = false },
+ onColorSelected = { BookshelfConfig.bookshelfCardColorDark = it }
+ )
}
}
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt
index b2499e477..d7eda8276 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt
@@ -778,7 +778,7 @@ fun BookshelfScreen(
),
contentPadding = adaptiveContentPaddingBookshelf(
top = paddingValues.calculateTopPadding(),
- bottom = if (ThemeConfig.useFloatingBottomBar) 120.dp else 8.dp,
+ bottom = if (ThemeConfig.useFloatingBottomBar || ThemeConfig.enableBlur) 120.dp else 8.dp,
horizontal = if (isGridMode) 8.dp else 4.dp
),
verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
@@ -1342,7 +1342,7 @@ fun BookshelfPage(
),
contentPadding = adaptiveContentPaddingBookshelf(
top = paddingValues.calculateTopPadding(),
- bottom = if (ThemeConfig.useFloatingBottomBar) 120.dp else 8.dp,
+ bottom = if (ThemeConfig.useFloatingBottomBar || ThemeConfig.enableBlur) 120.dp else 8.dp,
horizontal = if (isGridMode) 8.dp else 4.dp
),
verticalArrangement = Arrangement.spacedBy(if (isGridMode) 8.dp else 0.dp),
diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt
index de843d4eb..4cf54cd85 100644
--- a/app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt
@@ -45,10 +45,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.legado.app.R
import io.legado.app.data.entities.RssSource
import io.legado.app.ui.login.SourceLoginActivity
-import io.legado.app.ui.rss.favorites.RssFavoritesActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.ui.rss.source.manage.RssSourceActivity
-import io.legado.app.ui.rss.subscription.RuleSubActivity
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.adaptiveContentPadding
import io.legado.app.ui.widget.components.SourceIcon
@@ -71,7 +69,9 @@ import org.koin.androidx.compose.koinViewModel
fun RssScreen(
viewModel: RssViewModel = koinViewModel(),
onOpenSort: (sourceUrl: String, sortUrl: String?, key: String?) -> Unit,
- onOpenRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit
+ onOpenRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit,
+ onOpenRuleSub: () -> Unit,
+ onOpenFavorites: () -> Unit,
) {
val context = LocalContext.current
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
@@ -82,6 +82,8 @@ fun RssScreen(
val currentContext by rememberUpdatedState(context)
val currentOnOpenSort by rememberUpdatedState(onOpenSort)
val currentOnOpenRead by rememberUpdatedState(onOpenRead)
+ val currentOnOpenRuleSub by rememberUpdatedState(onOpenRuleSub)
+ val currentOnOpenFavorites by rememberUpdatedState(onOpenFavorites)
LaunchedEffect(viewModel) {
viewModel.effects.collectLatest { effect ->
@@ -112,11 +114,11 @@ fun RssScreen(
}
RssEffect.OpenRuleSub -> {
- currentContext.startActivity()
+ currentOnOpenRuleSub()
}
RssEffect.OpenFavorites -> {
- currentContext.startActivity()
+ currentOnOpenFavorites()
}
RssEffect.OpenSourceManage -> {
diff --git a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt
deleted file mode 100644
index ef25f1081..000000000
--- a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-package io.legado.app.ui.rss.favorites
-
-import android.os.Bundle
-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 io.legado.app.base.BaseComposeActivity
-import io.legado.app.ui.rss.read.RssReadRouteScreen
-
-/**
- * 收藏夹
- */
-class RssFavoritesActivity : BaseComposeActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- }
-
- @Composable
- override fun Content() {
- var readArgs by remember {
- mutableStateOf(null)
- }
-
- val currentReadArgs = readArgs
- if (currentReadArgs == null) {
- RssFavoritesScreen(
- onBackClick = { finish() },
- onOpenRead = { title, origin, link, openUrl ->
- readArgs = ReadArgs(title, origin, link, openUrl)
- }
- )
- } else {
- RssReadRouteScreen(
- title = currentReadArgs.title,
- origin = currentReadArgs.origin,
- link = currentReadArgs.link,
- openUrl = currentReadArgs.openUrl,
- onBackClick = { readArgs = null }
- )
- }
- }
-
- private data class ReadArgs(
- val title: String?,
- val origin: String,
- val link: String?,
- val openUrl: String?
- )
-}
diff --git a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt
index 8396d9a0c..750840969 100644
--- a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt
@@ -28,7 +28,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.viewmodel.compose.viewModel
import io.legado.app.R
import io.legado.app.data.entities.RssStar
import io.legado.app.ui.theme.adaptiveContentPadding
@@ -41,13 +40,14 @@ import io.legado.app.ui.widget.components.dialog.TextListInputDialog
import io.legado.app.ui.widget.components.divider.PillDivider
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.rules.RuleListScaffold
+import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RssFavoritesScreen(
onBackClick: () -> Unit,
onOpenRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit,
- viewModel: RssFavoritesViewModel = viewModel()
+ viewModel: RssFavoritesViewModel = koinViewModel()
) {
val state by viewModel.state.collectAsState()
val groups by viewModel.groups.collectAsState()
diff --git a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt
deleted file mode 100644
index 500b0edb9..000000000
--- a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubActivity.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.legado.app.ui.rss.subscription
-
-import android.os.Bundle
-import androidx.compose.runtime.Composable
-import io.legado.app.base.BaseComposeActivity
-
-/**
- * 规则订阅界面
- */
-class RuleSubActivity : BaseComposeActivity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- }
-
- @Composable
- override fun Content() {
- RuleSubScreen(
- onBackClick = { finish() }
- )
- }
-
-}
diff --git a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt
index a23a9b89b..2ce8bf638 100644
--- a/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt
@@ -18,7 +18,6 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import io.legado.app.ui.widget.components.AppFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SnackbarHostState
@@ -37,7 +36,6 @@ import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
-import androidx.lifecycle.viewmodel.compose.viewModel
import io.legado.app.R
import io.legado.app.data.entities.RuleSub
import io.legado.app.data.entities.RuleSubType
@@ -46,6 +44,7 @@ import io.legado.app.ui.association.ImportReplaceRuleDialog
import io.legado.app.ui.association.ImportRssSourceDialog
import io.legado.app.ui.theme.LegadoTheme
import io.legado.app.ui.theme.adaptiveContentPadding
+import io.legado.app.ui.widget.components.AppFloatingActionButton
import io.legado.app.ui.widget.components.AppTextField
import io.legado.app.ui.widget.components.EmptyMessage
import io.legado.app.ui.widget.components.alert.AppAlertDialog
@@ -57,12 +56,13 @@ import io.legado.app.ui.widget.components.rules.RuleListScaffold
import io.legado.app.ui.widget.components.text.AppText
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.toastOnUi
+import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun RuleSubScreen(
onBackClick: () -> Unit,
- viewModel: RuleSubViewModel = viewModel()
+ viewModel: RuleSubViewModel = koinViewModel()
) {
val context = LocalContext.current
val state by viewModel.state.collectAsState()
diff --git a/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt b/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt
index 3b4bfe99e..c6a560eb1 100644
--- a/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt
+++ b/app/src/main/java/io/legado/app/ui/theme/AdaptivePadding.kt
@@ -55,7 +55,7 @@ fun adaptiveContentPadding(
bottom: Dp
): PaddingValues {
val horizontal = if (ThemeResolver.isMiuixEngine(composeEngine)) 12.dp else 16.dp
- val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top + 8.dp else top
+ val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top + 12.dp else top + 16.dp
return PaddingValues(
top = adjustedTop,
bottom = bottom,
@@ -70,7 +70,7 @@ fun adaptiveContentPadding(
bottom: Dp,
horizontal: Dp
): PaddingValues {
- val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top + 6.dp else top + 4.dp
+ val adjustedTop = if (ThemeResolver.isMiuixEngine(composeEngine)) top + 12.dp else top + 16.dp
return PaddingValues(
top = adjustedTop,
bottom = bottom,
diff --git a/app/src/main/java/io/legado/app/ui/widget/components/book/SearchBookItem.kt b/app/src/main/java/io/legado/app/ui/widget/components/book/SearchBookItem.kt
index c9804b9aa..3b873aab5 100644
--- a/app/src/main/java/io/legado/app/ui/widget/components/book/SearchBookItem.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/components/book/SearchBookItem.kt
@@ -5,16 +5,16 @@ import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -23,37 +23,18 @@ import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Shuffle
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import io.legado.app.data.entities.SearchBook
import io.legado.app.domain.model.BookShelfState
import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.card.TextCard
import io.legado.app.ui.widget.components.cover.CoilBookCover
import io.legado.app.ui.widget.components.text.AppText
-import kotlinx.coroutines.flow.Flow
-
-@Composable
-fun SearchBookListItem(
- book: SearchBook,
- shelfState: Flow,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- val currentShelfState by shelfState.collectAsState(initial = BookShelfState.NOT_IN_SHELF)
- SearchBookListItem(
- book = book,
- shelfState = currentShelfState,
- onClick = onClick,
- modifier = modifier,
- )
-}
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
@@ -72,17 +53,39 @@ fun SearchBookListItem(
.clickable(onClick = onClick)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
- CoilBookCover(
- name = book.name,
- author = book.author,
- path = book.coverUrl,
- modifier = Modifier.width(72.dp).aspectRatio(5f / 7f),
- sourceOrigin = book.origin,
- sharedTransitionScope = sharedTransitionScope,
- animatedVisibilityScope = animatedVisibilityScope,
- sharedCoverKey = sharedCoverKey,
- showLoadingPlaceholder = sharedCoverKey == null
- )
+ Box(modifier = Modifier
+ .width(72.dp)
+ .aspectRatio(5f / 7f)) {
+ CoilBookCover(
+ name = book.name,
+ author = book.author,
+ path = book.coverUrl,
+ modifier = Modifier.fillMaxSize(),
+ sourceOrigin = book.origin,
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = animatedVisibilityScope,
+ sharedCoverKey = sharedCoverKey,
+ showLoadingPlaceholder = sharedCoverKey == null
+ )
+
+ val shelfIcon = when (shelfState) {
+ BookShelfState.IN_SHELF -> Icons.Default.Check
+ BookShelfState.SAME_NAME_AUTHOR -> Icons.Default.Shuffle
+ else -> null
+ }
+
+ if (shelfIcon != null) {
+ TextCard(
+ icon = shelfIcon,
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .padding(2.dp),
+ cornerRadius = 4.dp,
+ horizontalPadding = 2.dp,
+ verticalPadding = 2.dp
+ )
+ }
+ }
Spacer(modifier = Modifier.width(8.dp))
@@ -151,22 +154,6 @@ fun SearchBookListItem(
}
}
-@Composable
-fun SearchBookGridItem(
- book: SearchBook,
- shelfState: Flow,
- onClick: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- val currentShelfState by shelfState.collectAsState(initial = BookShelfState.NOT_IN_SHELF)
- SearchBookGridItem(
- book = book,
- shelfState = currentShelfState,
- onClick = onClick,
- modifier = modifier,
- )
-}
-
@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun SearchBookGridItem(
@@ -184,19 +171,41 @@ fun SearchBookGridItem(
.clickable(onClick = onClick)
.padding(4.dp)
) {
- CoilBookCover(
- name = book.name,
- author = book.author,
- path = book.coverUrl,
+ Box(
modifier = Modifier
.fillMaxWidth()
- .aspectRatio(5f / 7f),
- sourceOrigin = book.origin,
- sharedTransitionScope = sharedTransitionScope,
- animatedVisibilityScope = animatedVisibilityScope,
- sharedCoverKey = sharedCoverKey,
- showLoadingPlaceholder = sharedCoverKey == null
- )
+ .aspectRatio(5f / 7f)
+ ) {
+ CoilBookCover(
+ name = book.name,
+ author = book.author,
+ path = book.coverUrl,
+ modifier = Modifier.fillMaxSize(),
+ sourceOrigin = book.origin,
+ sharedTransitionScope = sharedTransitionScope,
+ animatedVisibilityScope = animatedVisibilityScope,
+ sharedCoverKey = sharedCoverKey,
+ showLoadingPlaceholder = sharedCoverKey == null
+ )
+
+ val shelfIcon = when (shelfState) {
+ BookShelfState.IN_SHELF -> Icons.Default.Check
+ BookShelfState.SAME_NAME_AUTHOR -> Icons.Default.Shuffle
+ else -> null
+ }
+
+ if (shelfIcon != null) {
+ TextCard(
+ icon = shelfIcon,
+ modifier = Modifier
+ .align(Alignment.TopEnd)
+ .padding(2.dp),
+ cornerRadius = 4.dp,
+ horizontalPadding = 2.dp,
+ verticalPadding = 2.dp
+ )
+ }
+ }
Spacer(modifier = Modifier.height(4.dp))
diff --git a/app/src/main/java/io/legado/app/ui/widget/components/divider/PillDivider.kt b/app/src/main/java/io/legado/app/ui/widget/components/divider/PillDivider.kt
index e1c66ed41..508b60fd7 100644
--- a/app/src/main/java/io/legado/app/ui/widget/components/divider/PillDivider.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/components/divider/PillDivider.kt
@@ -27,12 +27,18 @@ fun PillDivider(
val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
if (isMiuix) {
Box(
- modifier = Modifier
+ modifier = modifier
.fillMaxWidth()
- .padding(vertical = 2.dp)
- .height(4.dp)
- .background(MiuixTheme.colorScheme.dividerLine)
- )
+ .padding(vertical = 4.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth(0.8f)
+ .height(thickness)
+ .background(MiuixTheme.colorScheme.dividerLine)
+ )
+ }
} else {
Box(
modifier = modifier
diff --git a/app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt b/app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt
index 6db85f5eb..f8a942cb3 100644
--- a/app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt
@@ -48,7 +48,7 @@ fun CompactDropdownSettingItem(
entryValues: Array,
description: String? = null,
imageVector: ImageVector? = null,
- color: Color? = LegadoTheme.colorScheme.surface,
+ color: Color? = LegadoTheme.colorScheme.onSheetContent,
shape: Shape = MaterialTheme.shapes.small,
onValueChange: (String) -> Unit
) {
@@ -126,7 +126,7 @@ fun CompactSliderSettingItem(
steps: Int = 0,
description: String? = null,
imageVector: ImageVector? = null,
- color: Color? = MaterialTheme.colorScheme.surface,
+ color: Color? = LegadoTheme.colorScheme.onSheetContent,
shape: Shape = MaterialTheme.shapes.small,
onValueChange: (Float) -> Unit
) {
@@ -222,7 +222,7 @@ fun CompactSwitchSettingItem(
checked: Boolean,
description: String? = null,
imageVector: ImageVector? = null,
- color: Color? = MaterialTheme.colorScheme.surface,
+ color: Color? = LegadoTheme.colorScheme.onSheetContent,
shape: Shape = MaterialTheme.shapes.small,
enabled: Boolean = true,
onCheckedChange: (Boolean) -> Unit
@@ -260,7 +260,7 @@ fun CompactClickableSettingItem(
title: String,
description: String? = null,
imageVector: ImageVector? = null,
- color: Color? = MaterialTheme.colorScheme.surface,
+ color: Color? = LegadoTheme.colorScheme.onSheetContent,
shape: Shape = MaterialTheme.shapes.small,
trailingContent: (@Composable () -> Unit)? = null,
onClick: () -> Unit