Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa7b28f9 authored by brycelee's avatar brycelee
Browse files

Associate hardware sensor and debounce algorithm together.

This changelist introduces LightSensor, which contains a sensor and
debounce algorithm. The algorithm and sensor are designed to work
together and thus should be considered a tuple.

Test: atest AmbientLightModeMonitorTest
Flag: EXEMPT refactor
Bug: 432733048
Change-Id: I5f6eb5fc6576f190f4dbbc77c17da8b4d744fb63
parent 3272ed06
Loading
Loading
Loading
Loading
+14 −28
Original line number Diff line number Diff line
@@ -22,13 +22,11 @@ import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lowlight.dagger.LowLightModule.Companion.LIGHT_SENSOR
import com.android.systemui.lowlight.dagger.AmbientLightModeModule.Companion.MONITOR_LIGHT_SENSOR
import com.android.systemui.lowlight.shared.model.LightSensor
import com.android.systemui.util.sensors.AsyncSensorManager
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Provider

interface AmbientLightModeMonitor {
    companion object {
@@ -76,13 +74,11 @@ interface AmbientLightModeMonitor {
 *   light mode.
 * @property sensorManager the sensor manager used to register sensor event updates.
 */
@SysUISingleton
class AmbientLightModeMonitorImpl
@Inject
constructor(
    private val algorithm: Optional<AmbientLightModeMonitor.DebounceAlgorithm>,
    private val sensorManager: AsyncSensorManager,
    @Named(LIGHT_SENSOR) private val lightSensor: Optional<Provider<Sensor>>,
    @Named(MONITOR_LIGHT_SENSOR) private val lightSensor: LightSensor,
) : AmbientLightModeMonitor {
    companion object {
        private const val TAG = "AmbientLightModeMonitor"
@@ -92,33 +88,25 @@ constructor(
    override fun start(callback: AmbientLightModeMonitor.Callback) {
        if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")

        if (lightSensor.isEmpty || lightSensor.get().get() == null) {
            if (DEBUG) Log.w(TAG, "light sensor not available")
            return
        }

        if (algorithm.isEmpty) {
            if (DEBUG) Log.w(TAG, "debounce algorithm not available")
            return
        }

        algorithm.get().start(callback)
        lightSensor.apply {
            algorithm.start(callback)
            sensorManager.registerListener(
                mSensorEventListener,
            lightSensor.get().get(),
                sensor,
                SensorManager.SENSOR_DELAY_NORMAL,
            )
        }
    }

    /** Stop monitoring the current ambient light mode. */
    override fun stop() {
        if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")

        if (algorithm.isPresent) {
            algorithm.get().stop()
        }
        lightSensor.apply {
            algorithm.stop()
            sensorManager.unregisterListener(mSensorEventListener)
        }
    }

    private val mSensorEventListener: SensorEventListener =
        object : SensorEventListener {
@@ -128,9 +116,7 @@ constructor(
                    return
                }

                if (algorithm.isPresent) {
                    algorithm.get().onUpdateLightSensorEvent(event.values[0])
                }
                lightSensor.apply { algorithm.onUpdateLightSensorEvent(event.values[0]) }
            }

            override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
+24 −20
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.display.domain.interactor.DisplayStateInteractor
import com.android.systemui.dreams.domain.interactor.DreamSettingsInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
import com.android.systemui.lowlight.dagger.LowLightModule.Companion.LOW_LIGHT_MONITOR
import com.android.systemui.lowlight.domain.interactor.LowLightInteractor
import com.android.systemui.lowlight.domain.interactor.LowLightSettingsInteractor
import com.android.systemui.lowlight.shared.model.LowLightActionEntry
@@ -45,6 +46,7 @@ import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -73,7 +75,7 @@ constructor(
    private val userLockedInteractor: UserLockedInteractor,
    keyguardInteractor: KeyguardInteractor,
    powerInteractor: PowerInteractor,
    private val ambientLightModeMonitor: AmbientLightModeMonitor,
    @Named(LOW_LIGHT_MONITOR) private val ambientLightModeMonitor: AmbientLightModeMonitor?,
    private val uiEventLogger: UiEventLogger,
    private val lowLightBehaviorShellCommand: LowLightBehaviorShellCommand,
    private val lowLightShellCommand: LowLightShellCommand,
@@ -96,9 +98,10 @@ constructor(
    /** Whether the device is currently in a low-light environment. */
    private val isLowLightFromSensor =
        if (Flags.lowLightDreamBehavior()) {
            ambientLightModeMonitor?.let { monitor ->
                conflatedCallbackFlow {
                    ambientLightModeMonitor.start { lowLightMode: Int -> trySend(lowLightMode) }
                    awaitClose { ambientLightModeMonitor.stop() }
                        monitor.start { lowLightMode: Int -> trySend(lowLightMode) }
                        awaitClose { monitor.stop() }
                    }
                    .filterNot { it == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED }
                    .map { it == AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK }
@@ -116,6 +119,7 @@ constructor(
                        started = SharingStarted.WhileSubscribed(replayExpirationMillis = 0),
                        initialValue = false,
                    )
            } ?: MutableStateFlow(false)
        } else {
            MutableStateFlow(false)
        }
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.lowlight.dagger

import com.android.systemui.lowlight.AmbientLightModeMonitor
import com.android.systemui.lowlight.dagger.AmbientLightModeModule.Companion.MONITOR_LIGHT_SENSOR
import com.android.systemui.lowlight.shared.model.LightSensor
import dagger.BindsInstance
import dagger.Subcomponent
import javax.inject.Named

@Subcomponent(modules = [AmbientLightModeModule::class])
interface AmbientLightModeComponent {
    @Subcomponent.Factory
    interface Factory {
        fun create(
            @BindsInstance @Named(MONITOR_LIGHT_SENSOR) sensor: LightSensor
        ): AmbientLightModeComponent
    }

    fun getAmbientLightModeMonitor(): AmbientLightModeMonitor
}
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.lowlight.dagger

import com.android.systemui.lowlight.AmbientLightModeMonitor
import com.android.systemui.lowlight.AmbientLightModeMonitorImpl
import dagger.Binds
import dagger.Module

@Module
abstract class AmbientLightModeModule {
    companion object {
        const val MONITOR_LIGHT_SENSOR: String = "monitor_light_sensor"
    }

    @Binds
    abstract fun bindsAmbientLightModeMonitor(
        monitor: AmbientLightModeMonitorImpl
    ): AmbientLightModeMonitor
}
+20 −12
Original line number Diff line number Diff line
@@ -16,23 +16,27 @@

package com.android.systemui.lowlight.dagger

import android.hardware.Sensor
import com.android.systemui.CoreStartable
import com.android.systemui.lowlight.AmbientLightModeMonitor
import com.android.systemui.lowlight.AmbientLightModeMonitor.DebounceAlgorithm
import com.android.systemui.lowlight.AmbientLightModeMonitorImpl
import com.android.systemui.lowlight.LowLightBehaviorCoreStartable
import com.android.systemui.lowlight.data.repository.dagger.LowLightRepositoryModule
import com.android.systemui.lowlight.data.repository.dagger.LowLightSettingsRepositoryModule
import com.android.systemui.lowlight.shared.model.LightSensor
import com.android.systemui.lowlightclock.LowLightDisplayController
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import java.util.Optional
import javax.inject.Named
import javax.inject.Provider

@Module(includes = [LowLightSettingsRepositoryModule::class, LowLightRepositoryModule::class])
@Module(
    includes = [LowLightSettingsRepositoryModule::class, LowLightRepositoryModule::class],
    subcomponents = [AmbientLightModeComponent::class],
)
abstract class LowLightModule {
    @Binds
    @IntoMap
@@ -43,16 +47,20 @@ abstract class LowLightModule {

    @BindsOptionalOf abstract fun bindsLowLightDisplayController(): LowLightDisplayController

    @BindsOptionalOf abstract fun bindsDebounceAlgorithm(): DebounceAlgorithm

    @Binds
    abstract fun bindAmbientLightModeMonitor(
        impl: AmbientLightModeMonitorImpl
    ): AmbientLightModeMonitor

    @BindsOptionalOf @Named(LIGHT_SENSOR) abstract fun bindsLightSensor(): Sensor
    @BindsOptionalOf @Named(LIGHT_SENSOR) abstract fun bindsLightSensor(): LightSensor

    companion object {
        const val LIGHT_SENSOR: String = "low_light_monitor_light_sensor"
        const val LOW_LIGHT_MONITOR: String = "low_light_monitor"

        @Provides
        @Named(LOW_LIGHT_MONITOR)
        fun providesLowLightMonitor(
            factory: AmbientLightModeComponent.Factory,
            @Named(LIGHT_SENSOR) sensor: Optional<Provider<LightSensor>>,
        ): AmbientLightModeMonitor? {
            return if (sensor.isEmpty) null
            else factory.create(sensor.get().get()).getAmbientLightModeMonitor()
        }
    }
}
Loading