From 51e0f5d92817df3658a92af461b669ab0cb98a01 Mon Sep 17 00:00:00 2001 From: lyc Date: Sat, 20 Jun 2026 19:26:25 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=96=87?= =?UTF-8?q?=E5=AD=97=E9=80=89=E6=8B=A9=E8=8F=9C=E5=8D=95=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E5=BA=94=E7=94=A8=E8=BF=87=E6=BB=A4=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E5=8A=A0=E8=BD=BD=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/legado/app/constant/PreferKey.kt | 2 + .../data/repository/ReadSettingsRepository.kt | 12 ++ .../app/ui/book/read/ReadBookContract.kt | 7 + .../legado/app/ui/book/read/ReadBookScreen.kt | 15 ++ .../app/ui/book/read/ReadBookViewModel.kt | 12 ++ .../legado/app/ui/book/read/TextActionMenu.kt | 49 ++++- .../app/ui/book/read/sheet/MoreConfigSheet.kt | 18 +- .../app/ui/config/readConfig/ReadConfig.kt | 8 + .../config/readConfig/ReadConfigContract.kt | 4 + .../ui/config/readConfig/ReadConfigScreen.kt | 24 +++ .../config/readConfig/ReadConfigViewModel.kt | 12 ++ .../ui/config/readConfig/ReadMenuConfig.kt | 10 + .../readConfig/TextSelectMenuFilterSheet.kt | 191 ++++++++++++++++++ app/src/main/res/values-zh-rCN/strings.xml | 6 + app/src/main/res/values/strings.xml | 6 + 15 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 1c376760b..2561eb72d 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -132,6 +132,8 @@ object PreferKey { const val brightness = "brightness" const val nightBrightness = "nightBrightness" const val expandTextMenu = "expandTextMenu" + const val showSelectMenuIcon = "showSelectMenuIcon" + const val textSelectMenuFilter = "textSelectMenuFilter" const val doublePageHorizontal = "doubleHorizontalPage" const val readUrlOpenInBrowser = "readUrlInBrowser" const val defaultBookTreeUri = "defaultBookTreeUri" diff --git a/app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt b/app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt index ebf45bb68..06aecf62f 100644 --- a/app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt +++ b/app/src/main/java/io/legado/app/data/repository/ReadSettingsRepository.kt @@ -51,6 +51,8 @@ data class ReadPreferences( val optimizeRender: Boolean = false, val disableReturnKey: Boolean = false, val expandTextMenu: Boolean = false, + val showSelectMenuIcon: Boolean = true, + val textSelectMenuFilter: String = "", val showReadTitleAddition: Boolean = true, val autoReadSpeed: Int = 10, val prevKeys: String = "", @@ -230,6 +232,12 @@ class ReadSettingsRepository( suspend fun setExpandTextMenu(value: Boolean) = settingsRepository.putBoolean(PreferKey.expandTextMenu, value) + suspend fun setShowSelectMenuIcon(value: Boolean) = + settingsRepository.putBoolean(PreferKey.showSelectMenuIcon, value) + + suspend fun setTextSelectMenuFilter(value: String) = + settingsRepository.putString(PreferKey.textSelectMenuFilter, value) + suspend fun setShowReadTitleAddition(value: Boolean) = settingsRepository.putBoolean(PreferKey.showReadTitleAddition, value) @@ -420,6 +428,8 @@ class ReadSettingsRepository( optimizeRender = this[Keys.OptimizeRender] ?: false, disableReturnKey = this[Keys.DisableReturnKey] ?: false, expandTextMenu = this[Keys.ExpandTextMenu] ?: false, + showSelectMenuIcon = this[Keys.ShowSelectMenuIcon] ?: true, + textSelectMenuFilter = this[Keys.TextSelectMenuFilter] ?: "", showReadTitleAddition = this[Keys.ShowReadTitleAddition] ?: true, autoReadSpeed = this[Keys.AutoReadSpeed] ?: 10, prevKeys = this[Keys.PrevKeys] ?: "", @@ -518,6 +528,8 @@ class ReadSettingsRepository( val OptimizeRender = booleanPreferencesKey(PreferKey.optimizeRender) val DisableReturnKey = booleanPreferencesKey(PreferKey.disableReturnKey) val ExpandTextMenu = booleanPreferencesKey(PreferKey.expandTextMenu) + val ShowSelectMenuIcon = booleanPreferencesKey(PreferKey.showSelectMenuIcon) + val TextSelectMenuFilter = stringPreferencesKey(PreferKey.textSelectMenuFilter) val ShowReadTitleAddition = booleanPreferencesKey(PreferKey.showReadTitleAddition) val AutoReadSpeed = intPreferencesKey(PreferKey.autoReadSpeed) val PrevKeys = stringPreferencesKey(PreferKey.prevKeys) diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt index 9159b0438..6c43618e4 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt @@ -684,6 +684,7 @@ sealed interface ReadBookSheet { data object AudioCacheCleanConfig : ReadBookSheet data object ClickActionConfig : ReadBookSheet data object PageKeyConfig : ReadBookSheet + data object TextSelectMenuFilterConfig : ReadBookSheet data object InfoConfig : ReadBookSheet data class Dict(val word: String) : ReadBookSheet data class Bookmark( @@ -1197,6 +1198,12 @@ sealed interface ConfigUpdate { data class ExpandTextMenu(val value: Boolean) : ConfigUpdate { override val actions = emptySet() } + data class ShowSelectMenuIcon(val value: Boolean) : ConfigUpdate { + override val actions = emptySet() + } + data class TextSelectMenuFilter(val value: String) : ConfigUpdate { + override val actions = emptySet() + } data class ShowReadTitleAddition(val value: Boolean) : ConfigUpdate { override val actions = emptySet() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt index cc89c5049..46145652f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt @@ -41,6 +41,7 @@ import io.legado.app.ui.widget.components.FontSelectSheet import io.legado.app.ui.widget.components.alert.AppAlertDialog import io.legado.app.ui.widget.components.changeSource.ChangeSourceSheet import io.legado.app.ui.widget.components.log.AppLogSheet +import io.legado.app.ui.config.readConfig.TextSelectMenuFilterSheet import io.legado.app.utils.toastOnUi import kotlinx.coroutines.flow.collectLatest @@ -243,6 +244,10 @@ fun ReadBookScreen( onIntent(ReadBookIntent.DismissSheet) onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.PageKeyConfig)) }, + onOpenTextSelectMenuFilterConfig = { + onIntent(ReadBookIntent.DismissSheet) + onIntent(ReadBookIntent.ShowSheet(ReadBookSheet.TextSelectMenuFilterConfig)) + }, ) ReadAloudConfigSheet( show = state.activeSheet is ReadBookSheet.ReadAloudConfig, @@ -337,6 +342,16 @@ fun ReadBookScreen( ) } + is ReadBookSheet.TextSelectMenuFilterConfig -> { + TextSelectMenuFilterSheet( + show = true, + onDismissRequest = dismissSheet, + onFilterChanged = { + onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.TextSelectMenuFilter(it))) + } + ) + } + is ReadBookSheet.PageAnim -> { PageAnimConfigSheet( onDismissRequest = dismissSheet, diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt index e267cf84a..935dc9a78 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt @@ -3479,6 +3479,18 @@ class ReadBookViewModel( readSettingsRepository.setExpandTextMenu(update.value) } } + is ConfigUpdate.ShowSelectMenuIcon -> { + ReadConfig.showSelectMenuIcon = update.value + viewModelScope.launch { + readSettingsRepository.setShowSelectMenuIcon(update.value) + } + } + is ConfigUpdate.TextSelectMenuFilter -> { + ReadConfig.textSelectMenuFilter = update.value + viewModelScope.launch { + readSettingsRepository.setTextSelectMenuFilter(update.value) + } + } is ConfigUpdate.ShowReadTitleAddition -> { ReadConfig.showReadTitleAddition = update.value viewModelScope.launch { diff --git a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt index e9e01d010..1955c6ae6 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt @@ -23,6 +23,7 @@ import io.legado.app.constant.AppLog import io.legado.app.databinding.ItemTextBinding import io.legado.app.databinding.PopupActionMenuBinding import io.legado.app.ui.config.readConfig.ReadConfig +import io.legado.app.utils.dpToPx import io.legado.app.utils.gone import io.legado.app.utils.isAbsUrl import io.legado.app.utils.printOnDebug @@ -44,9 +45,10 @@ class TextActionMenu( setHasStableIds(true) } - private val menuItems: List + private var menuItems: List = emptyList() private val visibleMenuItems = arrayListOf() private val moreMenuItems = arrayListOf() + private val myMenu = MenuBuilder(context) init { @SuppressLint("InflateParams") @@ -57,13 +59,7 @@ class TextActionMenu( isFocusable = false animationStyle = R.style.TextActionMenuAnimation - val myMenu = MenuBuilder(context) - val otherMenu = MenuBuilder(context) SupportMenuInflater(context).inflate(R.menu.content_select_action, myMenu) - onInitializeMenu(otherMenu) - menuItems = myMenu.visibleItems + otherMenu.visibleItems - visibleMenuItems.addAll(menuItems.subList(0, 6)) - moreMenuItems.addAll(menuItems.subList(6, menuItems.size)) binding.recyclerView.adapter = adapter binding.recyclerViewMore.adapter = adapter setOnDismissListener { @@ -91,6 +87,18 @@ class TextActionMenu( } fun upMenu() { + val otherMenu = MenuBuilder(context) + onInitializeMenu(otherMenu) + menuItems = myMenu.visibleItems + otherMenu.visibleItems + visibleMenuItems.clear() + moreMenuItems.clear() + if (menuItems.size > 6) { + visibleMenuItems.addAll(menuItems.subList(0, 6)) + moreMenuItems.addAll(menuItems.subList(6, menuItems.size)) + } else { + visibleMenuItems.addAll(menuItems) + } + if (expandTextMenu()) { adapter.setItems(menuItems) binding.ivMenuMore.gone() @@ -172,6 +180,15 @@ class TextActionMenu( ) { with(binding) { textView.text = item.title + val icon = item.icon + if (icon != null && ReadConfig.showSelectMenuIcon) { + val size = 18.dpToPx() + icon.setBounds(0, 0, size, size) + textView.setCompoundDrawables(icon, null, null, null) + textView.compoundDrawablePadding = 6.dpToPx() + } else { + textView.setCompoundDrawables(null, null, null, null) + } } } @@ -255,11 +272,23 @@ class TextActionMenu( private fun onInitializeMenu(menu: Menu) { kotlin.runCatching { var menuItemOrder = 100 + val pm = context.packageManager + val filterSet = ReadConfig.textSelectMenuFilter.split(",").filter { it.isNotEmpty() }.toSet() for (resolveInfo in getSupportedActivities()) { - menu.add( + val componentName = "${resolveInfo.activityInfo.packageName}/${resolveInfo.activityInfo.name}" + if (filterSet.contains(componentName)) { + continue + } + val menuItem = menu.add( Menu.NONE, Menu.NONE, - menuItemOrder++, resolveInfo.loadLabel(context.packageManager) - ).intent = createProcessTextIntentForResolveInfo(resolveInfo) + menuItemOrder++, resolveInfo.loadLabel(pm) + ) + menuItem.intent = createProcessTextIntentForResolveInfo(resolveInfo) + if (ReadConfig.showSelectMenuIcon) { + menuItem.icon = kotlin.runCatching { + resolveInfo.loadIcon(pm) + }.getOrNull() + } } }.onFailure { context.toastOnUi("获取文字操作菜单出错:${it.localizedMessage}") diff --git a/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt b/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt index e3dd3d048..d84202a3c 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/sheet/MoreConfigSheet.kt @@ -31,6 +31,7 @@ fun MoreConfigSheet( onIntent: (ReadBookIntent) -> Unit, onOpenClickRegionalConfig: () -> Unit, onOpenPageKeyConfig: () -> Unit, + onOpenTextSelectMenuFilterConfig: () -> Unit, ) { val readSettingsRepository: ReadSettingsRepository = koinInject() val preferences by readSettingsRepository.preferences.collectAsStateWithLifecycle( @@ -137,13 +138,17 @@ fun MoreConfigSheet( onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ClickImgWay(it))) }, onOpenClickRegionalConfig = onOpenClickRegionalConfig, + onOpenPageKeyConfig = onOpenPageKeyConfig, + onOpenTextSelectMenuFilterConfig = onOpenTextSelectMenuFilterConfig, onDisableReturnKeyChange = { onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.DisableReturnKey(it))) }, - onOpenPageKeyConfig = onOpenPageKeyConfig, onExpandTextMenuChange = { onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ExpandTextMenu(it))) }, + onShowSelectMenuIconChange = { + onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ShowSelectMenuIcon(it))) + }, onShowReadTitleAdditionChange = { onIntent(ReadBookIntent.UpdateConfig(ConfigUpdate.ShowReadTitleAddition(it))) }, @@ -300,7 +305,9 @@ private fun OtherSettings( onOpenClickRegionalConfig: () -> Unit, onDisableReturnKeyChange: (Boolean) -> Unit, onOpenPageKeyConfig: () -> Unit, + onOpenTextSelectMenuFilterConfig: () -> Unit, onExpandTextMenuChange: (Boolean) -> Unit, + onShowSelectMenuIconChange: (Boolean) -> Unit, onShowReadTitleAdditionChange: (Boolean) -> Unit, onShowMenuIconChange: (Boolean) -> Unit, ) { @@ -357,11 +364,20 @@ private fun OtherSettings( title = stringResource(R.string.custom_page_key), onClick = onOpenPageKeyConfig, ) + TinyClickableSettingItem( + title = stringResource(R.string.text_select_menu_filter), + onClick = onOpenTextSelectMenuFilterConfig, + ) TinySwitchSettingItem( title = stringResource(R.string.expand_text_menu), checked = preferences.expandTextMenu, onCheckedChange = onExpandTextMenuChange, ) + TinySwitchSettingItem( + title = stringResource(R.string.show_select_menu_icon), + checked = preferences.showSelectMenuIcon, + onCheckedChange = onShowSelectMenuIconChange, + ) TinySwitchSettingItem( title = stringResource(R.string.show_read_title_addition), checked = preferences.showReadTitleAddition, diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt index 2d321b793..96e9fa044 100644 --- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfig.kt @@ -84,6 +84,12 @@ object ReadConfig { var expandTextMenu get() = ReadMenuConfig.expandTextMenu set(value) { ReadMenuConfig.expandTextMenu = value } + var showSelectMenuIcon + get() = ReadMenuConfig.showSelectMenuIcon + set(value) { ReadMenuConfig.showSelectMenuIcon = value } + var textSelectMenuFilter + get() = ReadMenuConfig.textSelectMenuFilter + set(value) { ReadMenuConfig.textSelectMenuFilter = value } var showReadTitleAddition get() = ReadMenuConfig.showReadTitleAddition set(value) { ReadMenuConfig.showReadTitleAddition = value } @@ -264,6 +270,8 @@ object ReadConfig { readBarStyle = preferences.readBarStyle progressBarBehavior = preferences.progressBarBehavior expandTextMenu = preferences.expandTextMenu + showSelectMenuIcon = preferences.showSelectMenuIcon + textSelectMenuFilter = preferences.textSelectMenuFilter showReadTitleAddition = preferences.showReadTitleAddition showMenuIcon = preferences.showMenuIcon clickActionTL = preferences.clickActionTL diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt index 76cba80f5..949167b99 100644 --- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigContract.kt @@ -35,6 +35,8 @@ data class ReadConfigUiState( val optimizeRender: Boolean = false, val disableReturnKey: Boolean = false, val expandTextMenu: Boolean = false, + val showSelectMenuIcon: Boolean = true, + val textSelectMenuFilter: String = "", val showReadTitleAddition: Boolean = true, val autoReadSpeed: Int = 10, val prevKeys: String = "", @@ -76,6 +78,8 @@ sealed interface ReadConfigIntent { data class OptimizeRenderChanged(val value: Boolean) : ReadConfigIntent data class DisableReturnKeyChanged(val value: Boolean) : ReadConfigIntent data class ExpandTextMenuChanged(val value: Boolean) : ReadConfigIntent + data class ShowSelectMenuIconChanged(val value: Boolean) : ReadConfigIntent + data class TextSelectMenuFilterChanged(val value: String) : ReadConfigIntent data class ShowReadTitleAdditionChanged(val value: Boolean) : ReadConfigIntent data class ShowMenuIconChanged(val value: Boolean) : ReadConfigIntent data class PageKeysChanged(val prevKeys: String, val nextKeys: String) : ReadConfigIntent diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt index e3c00d775..542b18a9e 100644 --- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt @@ -369,6 +369,30 @@ fun ReadConfigScreen( } ) + SwitchSettingItem( + title = stringResource(R.string.show_select_menu_icon), + checked = state.showSelectMenuIcon, + onCheckedChange = { + viewModel.onIntent(ReadConfigIntent.ShowSelectMenuIconChanged(it)) + } + ) + + var showSelectMenuFilterSheet by remember { mutableStateOf(false) } + + ClickableSettingItem( + title = stringResource(R.string.text_select_menu_filter), + description = stringResource(R.string.text_select_menu_filter_desc), + onClick = { showSelectMenuFilterSheet = true } + ) + + TextSelectMenuFilterSheet( + show = showSelectMenuFilterSheet, + onDismissRequest = { showSelectMenuFilterSheet = false }, + onFilterChanged = { + viewModel.onIntent(ReadConfigIntent.TextSelectMenuFilterChanged(it)) + } + ) + SwitchSettingItem( title = stringResource(R.string.show_read_title_addition), checked = state.showReadTitleAddition, diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt index fe0e47c51..c230d5bd7 100644 --- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigViewModel.kt @@ -197,6 +197,16 @@ class ReadConfigViewModel( readSettingsRepository.setExpandTextMenu(intent.value) } + is ReadConfigIntent.ShowSelectMenuIconChanged -> { + ReadConfig.showSelectMenuIcon = intent.value + readSettingsRepository.setShowSelectMenuIcon(intent.value) + } + + is ReadConfigIntent.TextSelectMenuFilterChanged -> { + ReadConfig.textSelectMenuFilter = intent.value + readSettingsRepository.setTextSelectMenuFilter(intent.value) + } + is ReadConfigIntent.ShowReadTitleAdditionChanged -> { ReadConfig.showReadTitleAddition = intent.value readSettingsRepository.setShowReadTitleAddition(intent.value) @@ -263,6 +273,8 @@ class ReadConfigViewModel( optimizeRender = optimizeRender, disableReturnKey = disableReturnKey, expandTextMenu = expandTextMenu, + showSelectMenuIcon = showSelectMenuIcon, + textSelectMenuFilter = textSelectMenuFilter, showReadTitleAddition = showReadTitleAddition, autoReadSpeed = autoReadSpeed, prevKeys = prevKeys, diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadMenuConfig.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadMenuConfig.kt index 4ea290aa4..d67360349 100644 --- a/app/src/main/java/io/legado/app/ui/config/readConfig/ReadMenuConfig.kt +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/ReadMenuConfig.kt @@ -40,6 +40,16 @@ object ReadMenuConfig { false ) + var showSelectMenuIcon by prefDelegate( + PreferKey.showSelectMenuIcon, + true + ) + + var textSelectMenuFilter by prefDelegate( + PreferKey.textSelectMenuFilter, + "" + ) + var showReadTitleAddition by prefDelegate( PreferKey.showReadTitleAddition, true diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt new file mode 100644 index 000000000..1b079e5e5 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt @@ -0,0 +1,191 @@ +package io.legado.app.ui.config.readConfig + +import android.content.Intent +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.SelectAll +import androidx.compose.material.icons.filled.Deselect +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import io.legado.app.R +import io.legado.app.ui.theme.LegadoTheme +import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet +import io.legado.app.ui.widget.components.text.AppText +import io.legado.app.ui.widget.components.card.NormalCard +import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow +import io.legado.app.ui.widget.components.button.series.SmallTonalButton + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun TextSelectMenuFilterSheet( + show: Boolean, + onDismissRequest: () -> Unit, + onFilterChanged: (String) -> Unit +) { + val context = LocalContext.current + val pm = context.packageManager + + // 查询所有注册了 ACTION_PROCESS_TEXT 的第三方 Activity + var apps by remember { mutableStateOf>(emptyList()) } + LaunchedEffect(show) { + if (show) { + val list = kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { + val intent = Intent().setAction(Intent.ACTION_PROCESS_TEXT).setType("text/plain") + pm.queryIntentActivities(intent, 0).map { resolveInfo -> + val title = resolveInfo.loadLabel(pm).toString() + val componentName = "${resolveInfo.activityInfo.packageName}/${resolveInfo.activityInfo.name}" + val icon = kotlin.runCatching { resolveInfo.loadIcon(pm) }.getOrNull() + TextSelectMenuAppInfo(title, componentName, icon) + } + } + apps = list + } else { + apps = emptyList() + } + } + + var pendingFilterString by remember(show) { + mutableStateOf(ReadConfig.textSelectMenuFilter) + } + + val disabledSet = remember(pendingFilterString) { + pendingFilterString.split(",").filter { it.isNotEmpty() }.toSet() + } + + AppModalBottomSheet( + show = show, + onDismissRequest = onDismissRequest, + title = stringResource(R.string.text_select_menu_filter), + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(0.8f) + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + + + if (apps.isEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + contentAlignment = Alignment.Center + ) { + AppText(text = stringResource(R.string.no_apps_found)) + } + } else { + LazyColumn( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(apps) { app -> + val isEnabled = !disabledSet.contains(app.componentName) + NormalCard( + onClick = { + val nextSet = if (isEnabled) { + disabledSet + app.componentName + } else { + disabledSet - app.componentName + } + pendingFilterString = nextSet.joinToString(",") + }, + cornerRadius = 12.dp, + containerColor = LegadoTheme.colorScheme.surfaceContainerHigh, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(40.dp) + .padding(4.dp), + contentAlignment = Alignment.Center + ) { + AsyncImage( + model = app.icon, + contentDescription = null, + modifier = Modifier.fillMaxSize() + ) + } + Column( + modifier = Modifier + .weight(1f) + .padding(horizontal = 8.dp) + ) { + AppText( + text = app.title, + style = MaterialTheme.typography.titleMedium + ) + } + Switch( + checked = isEnabled, + onCheckedChange = { checked -> + val nextSet = if (checked) { + disabledSet - app.componentName + } else { + disabledSet + app.componentName + } + pendingFilterString = nextSet.joinToString(",") + } + ) + } + } + } + } + } + Row( + modifier = Modifier.fillMaxWidth().padding(top = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { + IconButton(onClick = { pendingFilterString = "" }) { + Icon( + imageVector = Icons.Default.SelectAll, + contentDescription = stringResource(R.string.show_all), + tint = LegadoTheme.colorScheme.primary + ) + } + IconButton(onClick = { + val allNames = apps.joinToString(",") { it.componentName } + pendingFilterString = allNames + }) { + Icon( + imageVector = Icons.Default.Deselect, + contentDescription = stringResource(R.string.hide_all), + tint = LegadoTheme.colorScheme.error + ) + } + } + ConfirmDismissButtonsRow( + modifier = Modifier.weight(1f), + onDismiss = onDismissRequest, + onConfirm = { + onFilterChanged(pendingFilterString) + onDismissRequest() + }, + dismissText = stringResource(R.string.cancel), + confirmText = stringResource(R.string.action_save) + ) + } + } + } +} + +data class TextSelectMenuAppInfo( + val title: String, + val componentName: String, + val icon: android.graphics.drawable.Drawable? +) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b5bb25a70..f6607f776 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1098,6 +1098,12 @@ 打开系统文件夹选择器出错,将自动打开应用文件夹选择器 打开系统文件选择器出错,将自动打开应用文件选择器 展开文本选择菜单 + 展示选择菜单图标 + 选择菜单应用过滤 + 过滤文字选择操作菜单中显示的第三方应用 + 全部显示 + 全不显示 + 未找到支持文字处理的第三方应用 书籍保存位置 从其它应用打开的书籍保存位置 选择保存书籍的文件夹 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9fc11b631..12cd6b6a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1130,6 +1130,12 @@ Error opening system folder selector, automatically open application folder selector Error opening system file selector, automatically open application file selector Expand Text Selection Menu + Show Text Selection Menu Icons + Text Selection Menu Filter + Filter the third-party apps displayed in the selection menu + Show All + Hide All + No third-party apps supporting text actions found Book Save Location Save location for books opened from other apps Select the folder where the books are saved From eb7723adcd7d7f1f056651c1c7a61ad6acdef911 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:32:37 +0800 Subject: [PATCH 2/2] =?UTF-8?q?refactor:=20TextSelectMenuFilterSheet=20?= =?UTF-8?q?=E9=81=B5=E5=BE=AA=E9=A1=B9=E7=9B=AE=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MaterialTheme.typography → LegadoTheme.typography - 原生 Switch → AdaptiveSwitch(适配 Miuix/Material3 主题引擎) - 去掉底部全选/取消全选按钮和 ConfirmDismissButtonsRow - 保存按钮改为 Check 图标放在 AppModalBottomSheet 的 endAction 中 - 通配符导入改为显式导入 --- .../readConfig/TextSelectMenuFilterSheet.kt | 78 ++++++++----------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt b/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt index 1b079e5e5..789c4ccd5 100644 --- a/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt +++ b/app/src/main/java/io/legado/app/ui/config/readConfig/TextSelectMenuFilterSheet.kt @@ -1,14 +1,26 @@ package io.legado.app.ui.config.readConfig import android.content.Intent -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.SelectAll -import androidx.compose.material.icons.filled.Deselect -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -17,11 +29,11 @@ import androidx.compose.ui.unit.dp import coil.compose.AsyncImage import io.legado.app.R import io.legado.app.ui.theme.LegadoTheme +import io.legado.app.ui.widget.components.AdaptiveSwitch +import io.legado.app.ui.widget.components.button.series.SmallPlainButton +import io.legado.app.ui.widget.components.card.NormalCard import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet import io.legado.app.ui.widget.components.text.AppText -import io.legado.app.ui.widget.components.card.NormalCard -import io.legado.app.ui.widget.components.button.ConfirmDismissButtonsRow -import io.legado.app.ui.widget.components.button.series.SmallTonalButton @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -64,6 +76,15 @@ fun TextSelectMenuFilterSheet( show = show, onDismissRequest = onDismissRequest, title = stringResource(R.string.text_select_menu_filter), + endAction = { + SmallPlainButton( + icon = Icons.Default.Check, + onClick = { + onFilterChanged(pendingFilterString) + onDismissRequest() + } + ) + }, ) { Column( modifier = Modifier @@ -71,8 +92,6 @@ fun TextSelectMenuFilterSheet( .fillMaxHeight(0.8f) .padding(horizontal = 16.dp, vertical = 8.dp) ) { - - if (apps.isEmpty()) { Box( modifier = Modifier @@ -126,10 +145,10 @@ fun TextSelectMenuFilterSheet( ) { AppText( text = app.title, - style = MaterialTheme.typography.titleMedium + style = LegadoTheme.typography.titleMedium ) } - Switch( + AdaptiveSwitch( checked = isEnabled, onCheckedChange = { checked -> val nextSet = if (checked) { @@ -145,41 +164,6 @@ fun TextSelectMenuFilterSheet( } } } - Row( - modifier = Modifier.fillMaxWidth().padding(top = 16.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { - IconButton(onClick = { pendingFilterString = "" }) { - Icon( - imageVector = Icons.Default.SelectAll, - contentDescription = stringResource(R.string.show_all), - tint = LegadoTheme.colorScheme.primary - ) - } - IconButton(onClick = { - val allNames = apps.joinToString(",") { it.componentName } - pendingFilterString = allNames - }) { - Icon( - imageVector = Icons.Default.Deselect, - contentDescription = stringResource(R.string.hide_all), - tint = LegadoTheme.colorScheme.error - ) - } - } - ConfirmDismissButtonsRow( - modifier = Modifier.weight(1f), - onDismiss = onDismissRequest, - onConfirm = { - onFilterChanged(pendingFilterString) - onDismissRequest() - }, - dismissText = stringResource(R.string.cancel), - confirmText = stringResource(R.string.action_save) - ) - } } } }