Pārlūkot izejas kodu

fix:修复词典查询弹窗问题

HapeLee 5 mēneši atpakaļ
vecāks
revīzija
3833ca1f7c

+ 5 - 0
app/src/main/AndroidManifest.xml

@@ -263,6 +263,11 @@
             android:enableOnBackInvokedCallback="true"
             android:configChanges="orientation|screenSize"
             android:hardwareAccelerated="true" />
+        <activity
+            android:name=".ui.dict.DictActivity"
+            android:enableOnBackInvokedCallback="true"
+            android:configChanges="orientation|screenSize"
+            android:hardwareAccelerated="true" />
         <!-- 文件管理 -->
         <activity
             android:name=".ui.file.FileManageActivity"

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/read/ReadBookScreen.kt

@@ -20,7 +20,7 @@ import io.legado.app.ui.book.read.sheet.ChangeChapterSourceSheet
 import io.legado.app.ui.book.read.sheet.CharsetConfigSheet
 import io.legado.app.ui.book.read.sheet.ClickActionConfigSheet
 import io.legado.app.ui.book.read.sheet.ContentEditSheet
-import io.legado.app.ui.book.read.sheet.DictSheet
+import io.legado.app.ui.dict.DictSheet
 import io.legado.app.ui.book.read.sheet.DownloadSheet
 import io.legado.app.ui.book.read.sheet.EffectiveReplacesSheet
 import io.legado.app.ui.book.read.sheet.HighlightRuleConfigSheet

+ 0 - 170
app/src/main/java/io/legado/app/ui/book/read/sheet/DictSheet.kt

@@ -1,170 +0,0 @@
-package io.legado.app.ui.book.read.sheet
-
-import android.text.method.LinkMovementMethod
-import android.widget.TextView
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.CircularProgressIndicator
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.PrimaryScrollableTabRow
-import androidx.compose.material3.Tab
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import io.legado.app.R
-import io.legado.app.data.entities.DictRule
-import io.legado.app.help.GlideImageGetter
-import io.legado.app.ui.dict.DictViewModel
-import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
-import io.legado.app.utils.setHtml
-import org.koin.androidx.compose.koinViewModel
-
-@Composable
-fun DictSheet(
-    show: Boolean,
-    word: String,
-    onDismissRequest: () -> Unit,
-    viewModel: DictViewModel = koinViewModel(),
-) {
-    val context = LocalContext.current
-    var dictRules by remember { mutableStateOf<List<DictRule>>(emptyList()) }
-    var selectedTab by remember { mutableIntStateOf(0) }
-    var isLoading by remember { mutableStateOf(true) }
-    var htmlContent by remember { mutableStateOf("") }
-    var emptyMessage by remember { mutableStateOf<String?>(null) }
-    var glideImageGetter by remember { mutableStateOf<GlideImageGetter?>(null) }
-
-    LaunchedEffect(show) {
-        if (!show) return@LaunchedEffect
-        viewModel.initData { rules ->
-            dictRules = rules
-            if (rules.isEmpty()) {
-                emptyMessage = context.getString(R.string.empty)
-                isLoading = false
-            } else {
-                // Auto-select first tab
-                viewModel.dict(rules[0], word) { result ->
-                    isLoading = false
-                    if (result.isBlank()) {
-                        emptyMessage = "没有查询到结果"
-                    } else {
-                        htmlContent = result
-                        emptyMessage = null
-                    }
-                }
-            }
-        }
-    }
-
-    AppModalBottomSheet(
-        show = show,
-        onDismissRequest = {
-            glideImageGetter?.clear()
-            glideImageGetter = null
-            onDismissRequest()
-        },
-        title = word,
-    ) {
-        Column(
-            modifier = Modifier
-                .fillMaxWidth()
-                .padding(bottom = 16.dp),
-        ) {
-            // Tab row
-            if (dictRules.size > 1) {
-                PrimaryScrollableTabRow(
-                    selectedTabIndex = selectedTab.coerceIn(
-                        0,
-                        (dictRules.size - 1).coerceAtLeast(0)
-                    ),
-                    modifier = Modifier.fillMaxWidth(),
-                    edgePadding = 0.dp,
-                ) {
-                    dictRules.forEachIndexed { index, rule ->
-                        Tab(
-                            selected = selectedTab == index,
-                            onClick = {
-                                selectedTab = index
-                                isLoading = true
-                                emptyMessage = null
-                                viewModel.dict(rule, word) { result ->
-                                    isLoading = false
-                                    if (result.isBlank()) {
-                                        emptyMessage = "没有查询到结果"
-                                    } else {
-                                        htmlContent = result
-                                        emptyMessage = null
-                                    }
-                                }
-                            },
-                            text = { Text(rule.name) },
-                        )
-                    }
-                }
-            }
-
-            // Content
-            when {
-                isLoading -> {
-                    Box(
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .height(200.dp),
-                        contentAlignment = Alignment.Center,
-                    ) {
-                        CircularProgressIndicator()
-                    }
-                }
-
-                emptyMessage != null -> {
-                    Box(
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .padding(32.dp),
-                        contentAlignment = Alignment.Center,
-                    ) {
-                        Text(
-                            text = emptyMessage!!,
-                            style = MaterialTheme.typography.bodyMedium,
-                            color = MaterialTheme.colorScheme.onSurfaceVariant,
-                        )
-                    }
-                }
-
-                else -> {
-                    AndroidView(
-                        factory = { ctx ->
-                            TextView(ctx).apply {
-                                movementMethod = LinkMovementMethod()
-                                setPadding(32, 16, 32, 16)
-                            }
-                        },
-                        update = { textView ->
-                            textView.setHtml(htmlContent)
-                            glideImageGetter?.clear()
-                            glideImageGetter =
-                                GlideImageGetter.create(context, textView, htmlContent)
-                            textView.setHtml(htmlContent, glideImageGetter)
-                        },
-                        modifier = Modifier
-                            .fillMaxWidth()
-,
-                    )
-                }
-            }
-        }
-    }
-}

+ 33 - 0
app/src/main/java/io/legado/app/ui/dict/DictActivity.kt

@@ -0,0 +1,33 @@
+package io.legado.app.ui.dict
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import androidx.compose.runtime.Composable
+import io.legado.app.base.BaseComposeActivity
+
+class DictActivity : BaseComposeActivity() {
+
+    @Composable
+    override fun Content() {
+        DictSheet(
+            show = true,
+            word = intent.getStringExtra(EXTRA_WORD).orEmpty(),
+            onDismissRequest = { finish() },
+        )
+    }
+
+    companion object {
+        private const val EXTRA_WORD = "word"
+
+        fun startIntent(context: Context, word: String): Intent {
+            return Intent(context, DictActivity::class.java)
+                .putExtra(EXTRA_WORD, word)
+                .apply {
+                    if (context !is Activity) {
+                        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    }
+                }
+        }
+    }
+}

+ 35 - 0
app/src/main/java/io/legado/app/ui/dict/DictContract.kt

@@ -0,0 +1,35 @@
+package io.legado.app.ui.dict
+
+import androidx.compose.runtime.Stable
+import kotlinx.collections.immutable.ImmutableList
+import kotlinx.collections.immutable.persistentListOf
+
+@Stable
+data class DictUiState(
+    val word: String = "",
+    val isLoading: Boolean = false,
+    val rules: ImmutableList<DictRuleUi> = persistentListOf(),
+    val selectedIndex: Int = 0,
+    val htmlContent: String = "",
+    val emptyReason: DictEmptyReason? = null,
+)
+
+@Stable
+data class DictRuleUi(
+    val name: String,
+)
+
+sealed interface DictEmptyReason {
+    data object BlankWord : DictEmptyReason
+    data object NoRules : DictEmptyReason
+    data object NoResult : DictEmptyReason
+}
+
+sealed interface DictIntent {
+    data class Load(val word: String) : DictIntent
+    data class SelectRule(val index: Int) : DictIntent
+}
+
+sealed interface DictEffect {
+    data class ShowToast(val message: String) : DictEffect
+}

+ 0 - 123
app/src/main/java/io/legado/app/ui/dict/DictDialog.kt

@@ -1,123 +0,0 @@
-package io.legado.app.ui.dict
-
-import android.os.Bundle
-import android.text.method.LinkMovementMethod
-import android.view.View
-import com.google.android.material.tabs.TabLayout
-import io.legado.app.R
-import io.legado.app.base.BaseBottomSheetDialogFragment
-import io.legado.app.data.entities.DictRule
-import io.legado.app.databinding.DialogDictBinding
-import io.legado.app.help.GlideImageGetter
-import io.legado.app.utils.gone
-import io.legado.app.utils.setHtml
-import io.legado.app.utils.toastOnUi
-import io.legado.app.utils.viewbindingdelegate.viewBinding
-import io.legado.app.utils.visible
-import org.koin.androidx.viewmodel.ext.android.viewModel
-
-/**
- * 词典
- */
-class DictDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_dict) {
-
-    constructor(word: String) : this() {
-        arguments = Bundle().apply {
-            putString("word", word)
-        }
-    }
-
-    private val viewModel by viewModel<DictViewModel>()
-    private val binding by viewBinding(DialogDictBinding::bind)
-    private var word: String? = null
-    private var glideImageGetter: GlideImageGetter? = null
-
-    override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
-        binding.tvDict.movementMethod = LinkMovementMethod()
-        word = arguments?.getString("word")
-        if (word.isNullOrEmpty()) {
-            toastOnUi(R.string.cannot_empty)
-            dismiss()
-            return
-        }
-
-        setupTabs()
-        observeDicts()
-    }
-
-    override fun onDestroyView() {
-        glideImageGetter?.clear()
-        glideImageGetter = null
-        super.onDestroyView()
-    }
-
-    private fun setupTabs() {
-        binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
-            override fun onTabReselected(tab: TabLayout.Tab) = Unit
-            override fun onTabUnselected(tab: TabLayout.Tab) = Unit
-
-            override fun onTabSelected(tab: TabLayout.Tab) {
-                val dictRule = tab.tag as DictRule
-                loadDict(dictRule)
-            }
-        })
-    }
-
-    private fun observeDicts() {
-        viewModel.initData { dictRules ->
-            if (dictRules.isEmpty()) {
-                showEmptyView("暂无可用词典")
-                return@initData
-            }
-
-            binding.emptyMessageView.gone()
-            dictRules.forEach {
-                binding.tabLayout.addTab(binding.tabLayout.newTab().apply {
-                    text = it.name
-                    tag = it
-                })
-            }
-            setupTabLayoutMode(dictRules.size)
-
-
-            binding.tabLayout.getTabAt(0)?.select()
-        }
-    }
-
-    private fun loadDict(dictRule: DictRule) {
-
-        binding.tvDict.gone()
-        binding.emptyMessageView.gone()
-        binding.rotateLoading.visible()
-
-        viewModel.dict(dictRule, word!!) { result ->
-            binding.rotateLoading.gone()
-            if (result.isBlank()) {
-                showEmptyView("没有查询到结果")
-            } else {
-                binding.tvDict.visible()
-                binding.tvDict.setHtml(result)
-                glideImageGetter?.clear()
-                glideImageGetter = GlideImageGetter.create(requireContext(), binding.tvDict, result)
-                binding.tvDict.setHtml(result, glideImageGetter)
-            }
-        }
-    }
-
-    private fun setupTabLayoutMode(dictCount: Int) {
-        if (dictCount <= 4) {
-            binding.tabLayout.tabMode = TabLayout.MODE_FIXED
-            binding.tabLayout.tabGravity = TabLayout.GRAVITY_FILL
-        } else {
-            binding.tabLayout.tabMode = TabLayout.MODE_SCROLLABLE
-            binding.tabLayout.tabGravity = TabLayout.GRAVITY_CENTER
-        }
-    }
-
-    private fun showEmptyView(message: String) {
-        binding.tvDict.gone()
-        binding.rotateLoading.gone()
-        binding.emptyMessageView.visible()
-        binding.emptyMessageView.setMessage(message)
-    }
-}

+ 179 - 0
app/src/main/java/io/legado/app/ui/dict/DictSheet.kt

@@ -0,0 +1,179 @@
+package io.legado.app.ui.dict
+
+import android.text.method.LinkMovementMethod
+import android.widget.TextView
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+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
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import io.legado.app.R
+import io.legado.app.help.GlideImageGetter
+import io.legado.app.ui.widget.components.EmptyMessage
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
+import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
+import io.legado.app.ui.widget.components.tabRow.AppTabRow
+import io.legado.app.utils.setHtml
+import kotlinx.coroutines.flow.Flow
+import org.koin.androidx.compose.koinViewModel
+
+@Composable
+fun DictSheet(
+    show: Boolean,
+    word: String,
+    onDismissRequest: () -> Unit,
+    viewModel: DictViewModel = koinViewModel(),
+) {
+    val state by viewModel.uiState.collectAsStateWithLifecycle()
+
+    LaunchedEffect(show, word) {
+        if (show) {
+            viewModel.onIntent(DictIntent.Load(word))
+        }
+    }
+
+    AppModalBottomSheet(
+        show = show,
+        onDismissRequest = onDismissRequest,
+        title = word,
+    ) {
+        DictSheetContent(
+            state = state,
+            onIntent = viewModel::onIntent,
+            effects = viewModel.effects,
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(bottom = 16.dp),
+        )
+    }
+}
+
+@Composable
+private fun DictSheetContent(
+    state: DictUiState,
+    onIntent: (DictIntent) -> Unit,
+    effects: Flow<DictEffect>,
+    modifier: Modifier = Modifier,
+) {
+    LaunchedEffect(effects) {
+        effects.collect { effect ->
+            when (effect) {
+                is DictEffect.ShowToast -> Unit
+            }
+        }
+    }
+
+    DictContent(
+        state = state,
+        onIntent = onIntent,
+        modifier = modifier,
+    )
+}
+
+@Composable
+private fun DictContent(
+    state: DictUiState,
+    onIntent: (DictIntent) -> Unit,
+    modifier: Modifier = Modifier,
+) {
+    val blankWordText = stringResource(R.string.cannot_empty)
+    val emptyText = stringResource(R.string.empty)
+    val searchEmptyText = stringResource(R.string.search_empty)
+    val tabTitles = remember(state.rules) { state.rules.map { it.name } }
+    val emptyMessage = when (state.emptyReason) {
+        DictEmptyReason.BlankWord -> blankWordText
+        DictEmptyReason.NoRules -> emptyText
+        DictEmptyReason.NoResult -> searchEmptyText
+        null -> null
+    }
+
+    Column(modifier = modifier) {
+        if (state.rules.size > 1) {
+            AppTabRow(
+                tabTitles = tabTitles,
+                selectedTabIndex = state.selectedIndex.coerceIn(0, state.rules.lastIndex),
+                onTabSelected = { index ->
+                    onIntent(DictIntent.SelectRule(index))
+                },
+                modifier = Modifier.fillMaxWidth(),
+                isScrollable = true,
+            )
+        }
+
+        when {
+            state.isLoading -> {
+                Box(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .height(200.dp),
+                    contentAlignment = Alignment.Center,
+                ) {
+                    AppCircularProgressIndicator()
+                }
+            }
+
+            emptyMessage != null -> {
+                Box(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .padding(32.dp),
+                    contentAlignment = Alignment.Center,
+                ) {
+                    EmptyMessage(message = emptyMessage)
+                }
+            }
+
+            state.htmlContent.isNotBlank() -> {
+                DictHtmlContent(
+                    htmlContent = state.htmlContent,
+                    modifier = Modifier.fillMaxWidth(),
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun DictHtmlContent(
+    htmlContent: String,
+    modifier: Modifier = Modifier,
+) {
+    val context = LocalContext.current
+    var glideImageGetter by remember { mutableStateOf<GlideImageGetter?>(null) }
+
+    DisposableEffect(Unit) {
+        onDispose {
+            glideImageGetter?.clear()
+            glideImageGetter = null
+        }
+    }
+
+    AndroidView(
+        factory = { ctx ->
+            TextView(ctx).apply {
+                movementMethod = LinkMovementMethod()
+                setPadding(32, 16, 32, 16)
+            }
+        },
+        update = { textView ->
+            glideImageGetter?.clear()
+            glideImageGetter = GlideImageGetter.create(context, textView, htmlContent)
+            textView.setHtml(htmlContent, glideImageGetter)
+        },
+        modifier = modifier,
+    )
+}

+ 94 - 22
app/src/main/java/io/legado/app/ui/dict/DictViewModel.kt

@@ -1,40 +1,112 @@
 package io.legado.app.ui.dict
 
-import android.app.Application
-import io.legado.app.base.BaseViewModel
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
 import io.legado.app.data.entities.DictRule
 import io.legado.app.data.repository.DictRuleRepository
-import io.legado.app.help.coroutine.Coroutine
+import kotlinx.collections.immutable.toImmutableList
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 
 class DictViewModel(
-    application: Application,
     private val dictRuleRepository: DictRuleRepository
-) : BaseViewModel(application) {
+) : ViewModel() {
 
-    private var dictJob: Coroutine<String>? = null
+    private val _uiState = MutableStateFlow(DictUiState())
+    val uiState = _uiState.asStateFlow()
 
-    fun initData(onSuccess: (List<DictRule>) -> Unit) {
-        execute {
-            dictRuleRepository.getEnabled()
-        }.onSuccess {
-            onSuccess.invoke(it)
+    private val _effects = MutableSharedFlow<DictEffect>(extraBufferCapacity = 16)
+    val effects = _effects.asSharedFlow()
+
+    private var dictRules: List<DictRule> = emptyList()
+    private var dictJob: Job? = null
+
+    fun onIntent(intent: DictIntent) {
+        when (intent) {
+            is DictIntent.Load -> load(intent.word)
+            is DictIntent.SelectRule -> selectRule(intent.index)
         }
     }
 
-    fun dict(
-        dictRule: DictRule,
-        word: String,
-        onFinally: (String) -> Unit
-    ) {
+    private fun load(word: String) {
         dictJob?.cancel()
-        dictJob = execute {
-            dictRule.search(word)
-        }.onSuccess {
-            onFinally.invoke(it)
-        }.onError {
-            onFinally.invoke(it.localizedMessage ?: "ERROR")
+        val query = word.trim()
+        _uiState.value = DictUiState(
+            word = query,
+            isLoading = query.isNotBlank(),
+            emptyReason = if (query.isBlank()) DictEmptyReason.BlankWord else null,
+        )
+        if (query.isBlank()) return
+
+        viewModelScope.launch {
+            dictRules = withContext(Dispatchers.IO) {
+                dictRuleRepository.getEnabled()
+            }
+            _uiState.update { state ->
+                state.copy(
+                    rules = dictRules.map { DictRuleUi(it.name) }.toImmutableList(),
+                    isLoading = dictRules.isNotEmpty(),
+                    emptyReason = if (dictRules.isEmpty()) DictEmptyReason.NoRules else null,
+                )
+            }
+            if (dictRules.isNotEmpty()) {
+                searchRule(index = 0, word = query)
+            }
         }
     }
 
+    private fun selectRule(index: Int) {
+        val state = _uiState.value
+        if (index !in dictRules.indices || state.selectedIndex == index && state.isLoading) return
+        searchRule(index = index, word = state.word)
+    }
 
+    private fun searchRule(index: Int, word: String) {
+        dictJob?.cancel()
+        val rule = dictRules.getOrNull(index) ?: return
+        _uiState.update {
+            it.copy(
+                selectedIndex = index,
+                isLoading = true,
+                htmlContent = "",
+                emptyReason = null,
+            )
+        }
+        dictJob = viewModelScope.launch {
+            val result = runCatching {
+                withContext(Dispatchers.IO) {
+                    rule.search(word)
+                }
+            }.getOrElse { error ->
+                error.localizedMessage ?: "ERROR"
+            }
+            _uiState.update {
+                if (result.isBlank()) {
+                    it.copy(
+                        isLoading = false,
+                        htmlContent = "",
+                        emptyReason = DictEmptyReason.NoResult,
+                    )
+                } else {
+                    it.copy(
+                        isLoading = false,
+                        htmlContent = result,
+                        emptyReason = null,
+                    )
+                }
+            }
+        }
+    }
+
+    override fun onCleared() {
+        dictJob?.cancel()
+        super.onCleared()
+    }
 }

+ 4 - 9
app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt

@@ -10,14 +10,13 @@ import android.view.ViewGroup
 import android.webkit.JavascriptInterface
 import android.webkit.WebView
 import android.widget.FrameLayout
-import androidx.appcompat.app.AppCompatActivity
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.viewinterop.AndroidView
 import io.legado.app.R
-import io.legado.app.ui.dict.DictDialog
+import io.legado.app.ui.dict.DictActivity
 import io.legado.app.utils.toastOnUi
 
 @SuppressLint("SetJavaScriptEnabled")
@@ -83,7 +82,7 @@ class VisibleWebView(
                         postDelayed({
                             getSelectedText { selectedText ->
                                 if (selectedText.isNotBlank()) {
-                                    showDictDialog(selectedText)
+                                    showDictSheet(selectedText)
                                 } else {
                                     context.toastOnUi("未获取到选中文本,请重试")
                                 }
@@ -124,12 +123,8 @@ class VisibleWebView(
         }
     }
 
-    private fun showDictDialog(selectedText: String) {
-        val activity = context as? AppCompatActivity ?: return
-        val dialog = DictDialog(selectedText)
-        activity.supportFragmentManager.beginTransaction()
-            .add(dialog, "DictDialog")
-            .commitAllowingStateLoss()
+    private fun showDictSheet(selectedText: String) {
+        context.startActivity(DictActivity.startIntent(context, selectedText))
     }
 
     companion object {

+ 0 - 82
app/src/main/res/layout/dialog_dict.xml

@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:paddingHorizontal="16dp"
-    android:paddingBottom="16dp">
-
-    <LinearLayout
-        android:id="@+id/header_layout"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        android:gravity="center"
-        android:paddingHorizontal="16dp"
-        android:paddingBottom="8dp"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent">
-
-        <com.google.android.material.bottomsheet.BottomSheetDragHandleView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingVertical="16dp"
-            android:minHeight="0dp"
-            android:layout_gravity="center_horizontal" />
-
-        <TextView
-            android:id="@+id/bottom_sheet_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/dict"
-            android:textSize="18sp"
-            android:textStyle="bold" />
-    </LinearLayout>
-
-    <com.google.android.material.tabs.TabLayout
-        android:id="@+id/tab_layout"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="@android:color/transparent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/header_layout" />
-
-    <com.google.android.material.loadingindicator.LoadingIndicator
-        android:id="@+id/rotate_loading"
-        style="@style/Widget.Material3.LoadingIndicator.Contained"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:layout_margin="32dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/tab_layout" />
-
-    <androidx.core.widget.NestedScrollView
-        android:id="@+id/scroll_container"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/tab_layout">
-
-        <TextView
-            android:id="@+id/tv_dict"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" />
-
-    </androidx.core.widget.NestedScrollView>
-
-    <io.legado.app.ui.widget.EmptyMessageView
-        android:id="@+id/empty_message_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        app:messageText="词典内容为空!"/>
-
-</LinearLayout>