From 51e0f5d92817df3658a92af461b669ab0cb98a01 Mon Sep 17 00:00:00 2001 From: lyc Date: Sat, 20 Jun 2026 19:26:25 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=96=87=E5=AD=97?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E8=8F=9C=E5=8D=95=E7=AC=AC=E4=B8=89=E6=96=B9?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=BF=87=E6=BB=A4=E5=8A=9F=E8=83=BD=E5=B9=B6?= =?UTF-8?q?=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