Просмотр исходного кода

refactor: 优化自动切换深浅色提醒功能

fansangg 4 месяцев назад
Родитель
Сommit
37a1048d1d

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

@@ -47,7 +47,7 @@ fun ActionReminder(
 
     LaunchedEffect(show, reminder?.id) {
         if (show) {
-            delay(4.seconds)
+            delay(5.seconds)
             onDismiss()
         }
     }

+ 5 - 0
app/src/main/java/io/legado/app/ui/book/read/ReadBookContract.kt

@@ -29,8 +29,13 @@ data class ReminderUiState(
     val message: String,
     val actionText: String? = null,
     val actionIntent: ReadBookIntent? = null,
+    val type: ReminderType? = null,
 )
 
+sealed interface ReminderType {
+    data class DayNightReminder(val targetIsNight: Boolean) : ReminderType
+}
+
 @Stable
 data class ReadBookMenuState(
     val visible: Boolean = false,

+ 71 - 46
app/src/main/java/io/legado/app/ui/book/read/ReadBookRouteScreen.kt

@@ -1,6 +1,11 @@
 package io.legado.app.ui.book.read
 
+import android.content.Context
 import android.content.Intent
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
 import android.view.KeyEvent
 import android.view.View
 import android.widget.FrameLayout
@@ -17,19 +22,12 @@ import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import android.content.Context
-import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
-import android.hardware.SensorManager
-import androidx.compose.runtime.DisposableEffect
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
 import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleEventObserver
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import io.legado.app.ui.config.readConfig.ReadConfig
+import androidx.lifecycle.LifecycleOwner
 import androidx.lifecycle.compose.LocalLifecycleOwner
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.kyant.backdrop.backdrops.layerBackdrop
 import com.kyant.backdrop.backdrops.rememberLayerBackdrop
@@ -55,9 +53,15 @@ import io.legado.app.ui.replace.ReplaceRuleActivity
 import io.legado.app.utils.StartActivityContract
 import io.legado.app.utils.takePersistablePermissionSafely
 import io.legado.app.utils.toastOnUi
+import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.onSubscription
+import kotlinx.coroutines.isActive
 import kotlinx.coroutines.launch
+import java.util.concurrent.atomic.AtomicReference
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
 
 
 data class ReadBookViewRefs(
@@ -257,43 +261,11 @@ fun ReadBookRouteScreen(
         viewModel.onIntent(ReadBookIntent.BookInfoResult(result.resultCode == android.app.Activity.RESULT_OK))
     }
 
-    DisposableEffect(lifecycleOwner) {
-        val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
-        val lightSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_LIGHT)
-        var listener: SensorEventListener? = null
-
-        val observer = LifecycleEventObserver { _, event ->
-            if (event == Lifecycle.Event.ON_RESUME) {
-                if (!ReadConfig.autoSuggestDayNight || viewModel.isDayNightSwitchCoolingDown()) return@LifecycleEventObserver
-                if (lightSensor != null) {
-                    listener = object : SensorEventListener {
-                        override fun onSensorChanged(sensorEvent: SensorEvent?) {
-                            sensorEvent?.values?.firstOrNull()?.let { lux ->
-                                //Log.d("fansangg","lux = $lux")
-                                viewModel.onIntent(ReadBookIntent.CheckSwitchDayNight(lux))
-                            }
-                            listener?.let { sensorManager.unregisterListener(it) }
-                            listener = null
-                        }
-
-                        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
-                    }
-                    sensorManager.registerListener(listener, lightSensor, SensorManager.SENSOR_DELAY_NORMAL)
-                }
-            } else if (event == Lifecycle.Event.ON_PAUSE) {
-                listener?.let {
-                    sensorManager?.unregisterListener(it)
-                    listener = null
-                }
-            }
-        }
-
-        lifecycleOwner.lifecycle.addObserver(observer)
-        onDispose {
-            lifecycleOwner.lifecycle.removeObserver(observer)
-            listener?.let { sensorManager?.unregisterListener(it) }
-        }
-    }
+    AutoSuggestDayNightObserver(
+        viewModel = viewModel,
+        autoSuggestDayNight = readPreferences.autoSuggestDayNight,
+        lifecycleOwner = lifecycleOwner,
+    )
 
     // ── Effect collection: route handles launcher effects, rest goes to bridge ──
 
@@ -589,3 +561,56 @@ private fun ReadBookViewLayer(
         },
     )
 }
+
+@Composable
+private fun AutoSuggestDayNightObserver(
+    viewModel: ReadBookViewModel,
+    autoSuggestDayNight: Boolean,
+    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
+) {
+    val context = LocalContext.current
+    LaunchedEffect(autoSuggestDayNight) {
+        if (!autoSuggestDayNight) return@LaunchedEffect
+        lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+            val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as? SensorManager
+            val lightSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_LIGHT)
+            if (sensorManager != null && lightSensor != null) {
+                while (isActive) {
+                    if (!viewModel.isDayNightSwitchCoolingDown()) {
+                        val finalLux = AtomicReference<Float?>(null)
+                        val listener = object : SensorEventListener {
+                            override fun onSensorChanged(event: SensorEvent?) {
+                                event?.values?.firstOrNull()?.let { lux ->
+                                    finalLux.set(lux)
+                                }
+                            }
+
+                            override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
+                        }
+                        try {
+                            sensorManager.registerListener(
+                                listener,
+                                lightSensor,
+                                SensorManager.SENSOR_DELAY_NORMAL
+                            )
+                            delay(1.seconds)
+                        } catch (e: CancellationException) {
+                            throw e
+                        } catch (e: Exception) {
+                            AppLog.put("lightSensor收集异常", e)
+                        } finally {
+                            sensorManager.unregisterListener(listener)
+                        }
+
+                        finalLux.get()?.let { lux ->
+                            viewModel.onIntent(ReadBookIntent.CheckSwitchDayNight(lux))
+                        }
+
+                    }
+
+                    delay(15.minutes)
+                }
+            }
+        }
+    }
+}

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

@@ -505,7 +505,7 @@ fun ReadBookScreen(
     }
 
     ActionReminder(
-        reminder = state.activeReminder,
+        reminder = if (state.menuVisible) null else state.activeReminder,
         onAction = { reminder -> reminder.actionIntent?.let { onIntent(it) } },
         onDismiss = { onIntent(ReadBookIntent.DismissReminder) },
     )

+ 39 - 5
app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@@ -78,7 +78,6 @@ import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
 import io.legado.app.ui.widget.components.importComponents.ImportItemWrapper
 import io.legado.app.ui.widget.components.importComponents.ImportStatus
-import io.legado.app.utils.ColorUtils
 import io.legado.app.utils.GSON
 import io.legado.app.utils.ImageSaveUtils
 import io.legado.app.utils.NetworkUtils
@@ -3479,6 +3478,10 @@ class ReadBookViewModel(
             }
             is ConfigUpdate.AutoSuggestDayNight -> {
                 ReadConfig.autoSuggestDayNight = update.value
+                if (update.value) {
+                    hasDismissedDarkReminder = false
+                    hasDismissedLightReminder = false
+                }
                 viewModelScope.launch {
                     readSettingsRepository.setAutoSuggestDayNight(update.value)
                 }
@@ -3892,9 +3895,23 @@ class ReadBookViewModel(
     }
 
     private fun toggleDayNight() {
+        lastSwitchDayNightReminderTime = System.currentTimeMillis()
+        hasDismissedDarkReminder = false
+        hasDismissedLightReminder = false
         val nextMode = if (ReadConfig.isNightTheme) "1" else "2"
         ThemeConfig.themeMode = nextMode
-        _uiState.update { it.copy(styleConfig = buildStyleConfig()) }
+        _uiState.update {
+            val newActiveReminder = if (it.activeReminder?.type is ReminderType.DayNightReminder) {
+                null
+            } else {
+                it.activeReminder
+            }
+            it.copy(
+                activeReminder = newActiveReminder,
+                styleConfig = buildStyleConfig()
+            )
+        }
+        reminderQueue.removeAll { it.type is ReminderType.DayNightReminder }
         _effects.tryEmit(ReadBookEffect.UpdateReadViewConfig(
             setOf(
                 ConfigUpdateAction.UpdateBackground,
@@ -4295,6 +4312,8 @@ class ReadBookViewModel(
 
     private var lastSwitchDayNightReminderTime: Long = 0L
     private val reminderQueue = ArrayDeque<ReminderUiState>()
+    private var hasDismissedDarkReminder = false
+    private var hasDismissedLightReminder = false
 
     fun isDayNightSwitchCoolingDown(): Boolean {
         return System.currentTimeMillis() - lastSwitchDayNightReminderTime < REMINDER_COOLDOWN_MS
@@ -4319,13 +4338,13 @@ class ReadBookViewModel(
         val isNight = ReadConfig.isNightTheme
         val styleConfig = _uiState.value.styleConfig
         if (!isNight && lux <= DARK_LUX_THRESHOLD) {
+            if (hasDismissedDarkReminder) return
             val bgType = styleConfig.bgType
             val isLightBg = if (bgType == 0) {
                 val colorInt = runCatching { styleConfig.bgStr.toColorInt() }.getOrDefault(0xFFEEEEEE.toInt())
                 isReadBgLight(colorInt)
             } else {
                 val meanColor = ReadBookConfig.bgMeanColor
-                //Log.d("fansangg","meanColor = ${ColorUtils.intToString(meanColor)},isLight = ${isReadBgLight(meanColor)}")
                 if (meanColor != 0) isReadBgLight(meanColor) else true
             }
             if (isLightBg) {
@@ -4335,17 +4354,18 @@ class ReadBookViewModel(
                         message = context.getString(R.string.switch_to_dark_mode_tip),
                         actionText = context.getString(R.string.switch_action),
                         actionIntent = ReadBookIntent.ToggleDayNight,
+                        type = ReminderType.DayNightReminder(targetIsNight = true),
                     )
                 )
             }
         } else if (isNight && lux >= BRIGHT_LUX_THRESHOLD) {
+            if (hasDismissedLightReminder) return
             val bgTypeNight = styleConfig.bgTypeNight
             val isDarkBg = if (bgTypeNight == 0) {
                 val colorInt = runCatching { styleConfig.bgStrNight.toColorInt() }.getOrDefault(0xFF000000.toInt())
                 !isReadBgLight(colorInt)
             } else {
                 val meanColor = ReadBookConfig.bgMeanColor
-                //Log.d("fansangg","meanColor = ${ColorUtils.intToString(meanColor)},isLight = ${isReadBgLight(meanColor)}")
                 if (meanColor != 0) !isReadBgLight(meanColor) else true
             }
             if (isDarkBg) {
@@ -4355,6 +4375,7 @@ class ReadBookViewModel(
                         message = context.getString(R.string.switch_to_light_mode_tip),
                         actionText = context.getString(R.string.switch_action),
                         actionIntent = ReadBookIntent.ToggleDayNight,
+                        type = ReminderType.DayNightReminder(targetIsNight = false),
                     )
                 )
             }
@@ -4362,6 +4383,19 @@ class ReadBookViewModel(
     }
 
     private fun dismissReminder() {
+        val currentReminder = _uiState.value.activeReminder
+        if (currentReminder != null) {
+            when (val type = currentReminder.type) {
+                is ReminderType.DayNightReminder -> {
+                    if (type.targetIsNight) {
+                        hasDismissedDarkReminder = true
+                    } else {
+                        hasDismissedLightReminder = true
+                    }
+                }
+                else -> {}
+            }
+        }
         _uiState.update { it.copy(activeReminder = null) }
         if (reminderQueue.isNotEmpty()) {
             viewModelScope.launch {
@@ -4382,7 +4416,7 @@ private const val TOOL_BUTTON_PREFS = "tool_button_config"
 private const val TOOL_BUTTON_KEY = "tool_buttons"
 private const val DEFAULT_ENABLED_BUTTON_COUNT = 5
 
-private const val DARK_LUX_THRESHOLD = 10f
+private const val DARK_LUX_THRESHOLD = 8f
 private const val BRIGHT_LUX_THRESHOLD = 100f
 private const val LIGHT_LUMINANCE_THRESHOLD = 0.35
 private const val REMINDER_COOLDOWN_MS = 10 * 60 * 1000L