Procházet zdrojové kódy

修复段评页面日夜模式即时切换

5151561 před 3 měsíci
rodič
revize
73d9887c26

+ 54 - 1
app/src/main/java/io/legado/app/ui/widget/dialog/BottomWebViewDialog.kt

@@ -3,6 +3,7 @@ package io.legado.app.ui.widget.dialog
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
 import android.content.Context
 import android.content.Context
 import android.content.pm.ActivityInfo
 import android.content.pm.ActivityInfo
+import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.Bitmap
 import android.net.Uri
 import android.net.Uri
 import android.net.http.SslError
 import android.net.http.SslError
@@ -31,7 +32,9 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.core.net.toUri
 import androidx.core.net.toUri
 import androidx.core.view.size
 import androidx.core.view.size
 import androidx.fragment.app.FragmentManager
 import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
 import com.google.android.material.bottomsheet.BottomSheetBehavior
 import com.google.android.material.bottomsheet.BottomSheetBehavior
 import com.google.android.material.bottomsheet.BottomSheetDialogFragment
 import com.google.android.material.bottomsheet.BottomSheetDialogFragment
 import io.legado.app.R
 import io.legado.app.R
@@ -41,6 +44,7 @@ import io.legado.app.constant.AppLog
 import io.legado.app.data.appDb
 import io.legado.app.data.appDb
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.data.entities.BaseSource
 import io.legado.app.databinding.DialogWebViewBinding
 import io.legado.app.databinding.DialogWebViewBinding
+import io.legado.app.domain.gateway.AppUiConfigurationGateway
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.exception.NoStackTraceException
 import io.legado.app.help.WebCacheManager
 import io.legado.app.help.WebCacheManager
 import io.legado.app.help.config.AppConfig
 import io.legado.app.help.config.AppConfig
@@ -68,6 +72,7 @@ import io.legado.app.utils.invisible
 import io.legado.app.utils.longSnackbar
 import io.legado.app.utils.longSnackbar
 import io.legado.app.utils.openUrl
 import io.legado.app.utils.openUrl
 import io.legado.app.utils.setLayout
 import io.legado.app.utils.setLayout
+import io.legado.app.utils.setDarkeningAllowed
 import io.legado.app.utils.startActivity
 import io.legado.app.utils.startActivity
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.toastOnUi
 import io.legado.app.utils.viewbindingdelegate.viewBinding
 import io.legado.app.utils.viewbindingdelegate.viewBinding
@@ -75,11 +80,14 @@ import io.legado.app.utils.visible
 import io.legado.app.utils.writeBytes
 import io.legado.app.utils.writeBytes
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers.IO
 import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
 import java.io.ByteArrayInputStream
 import java.io.ByteArrayInputStream
 import java.lang.ref.WeakReference
 import java.lang.ref.WeakReference
 import java.util.Date
 import java.util.Date
+import org.koin.android.ext.android.inject
 
 
 class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view),
 class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view),
     WebJsExtensions.Callback {
     WebJsExtensions.Callback {
@@ -116,6 +124,8 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
         }
         }
     }
     }
     private val displayMetrics by lazy { resources.displayMetrics }
     private val displayMetrics by lazy { resources.displayMetrics }
+    private val appUiConfigurationGateway by inject<AppUiConfigurationGateway>()
+    private var appliedDarkTheme: Boolean? = null
     private val selectImageDir = registerForActivityResult(HandleFileContract()) {
     private val selectImageDir = registerForActivityResult(HandleFileContract()) {
         it.uri?.let { uri ->
         it.uri?.let { uri ->
             ACache.get().put(imagePathKey, uri.toString())
             ACache.get().put(imagePathKey, uri.toString())
@@ -373,6 +383,7 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
         super.onViewCreated(view, savedInstanceState)
         super.onViewCreated(view, savedInstanceState)
         view.setBackgroundColor(0)
         view.setBackgroundColor(0)
         binding.webViewContainer.addView(currentWebView)
         binding.webViewContainer.addView(currentWebView)
+        observeAppTheme()
         lifecycleScope.launch(IO) {
         lifecycleScope.launch(IO) {
             val args = arguments
             val args = arguments
             if (args == null) {
             if (args == null) {
@@ -502,6 +513,43 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
         currentWebView.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)
         currentWebView.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)
     }
     }
 
 
+    private fun observeAppTheme() {
+        viewLifecycleOwner.lifecycleScope.launch {
+            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                appUiConfigurationGateway.configuration
+                    .map { it.isDarkTheme }
+                    .distinctUntilChanged()
+                    .collect(::applyWebViewTheme)
+            }
+        }
+    }
+
+    private fun applyWebViewTheme(isDark: Boolean, force: Boolean = false) {
+        if (!force && appliedDarkTheme == isDark) return
+        appliedDarkTheme = isDark
+        val configuration = Configuration(currentWebView.resources.configuration).apply {
+            uiMode = (uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) or if (isDark) {
+                Configuration.UI_MODE_NIGHT_YES
+            } else {
+                Configuration.UI_MODE_NIGHT_NO
+            }
+        }
+        currentWebView.dispatchConfigurationChanged(configuration)
+        currentWebView.settings.setDarkeningAllowed(isDark)
+        currentWebView.evaluateJavascript(
+            """
+            (() => {
+              const root = document.documentElement;
+              if (!root) return;
+              root.style.setProperty('color-scheme', '${if (isDark) "dark" else "light"}', 'important');
+              window.dispatchEvent(new CustomEvent('legado-theme-change', { detail: { dark: $isDark } }));
+            })();
+            """.trimIndent(),
+            null,
+        )
+        currentWebView.postInvalidate()
+    }
+
     private fun buildSpliceHtml(originalHtml: String): String {
     private fun buildSpliceHtml(originalHtml: String): String {
         if (preloadJs.isNullOrEmpty()) return originalHtml
         if (preloadJs.isNullOrEmpty()) return originalHtml
         val headIndex = originalHtml.indexOf("<head", ignoreCase = true)
         val headIndex = originalHtml.indexOf("<head", ignoreCase = true)
@@ -736,6 +784,11 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
             }
             }
         }
         }
 
 
+        override fun onPageFinished(view: WebView?, url: String?) {
+            super.onPageFinished(view, url)
+            appliedDarkTheme?.let { applyWebViewTheme(it, force = true) }
+        }
+
         private fun shouldOverrideUrlLoading(url: Uri): Boolean {
         private fun shouldOverrideUrlLoading(url: Uri): Boolean {
             return when (url.scheme) {
             return when (url.scheme) {
                 "http", "https" -> {
                 "http", "https" -> {
@@ -816,4 +869,4 @@ class BottomWebViewDialog() : BottomSheetDialogFragment(R.layout.dialog_web_view
             }
             }
         }
         }
     }
     }
-}
+}