• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.lowlightclock
18 
19 import android.annotation.IntDef
20 import android.hardware.Sensor
21 import android.hardware.SensorEvent
22 import android.hardware.SensorEventListener
23 import android.hardware.SensorManager
24 import android.util.Log
25 import com.android.systemui.Dumpable
26 import com.android.systemui.lowlightclock.dagger.LowLightModule.Companion.LIGHT_SENSOR
27 import com.android.systemui.util.sensors.AsyncSensorManager
28 import java.io.PrintWriter
29 import java.util.Optional
30 import javax.inject.Inject
31 import javax.inject.Named
32 import javax.inject.Provider
33 
34 /**
35  * Monitors ambient light signals, applies a debouncing algorithm, and produces the current ambient
36  * light mode.
37  *
38  * @property algorithm the debounce algorithm which transforms light sensor events into an ambient
39  *   light mode.
40  * @property sensorManager the sensor manager used to register sensor event updates.
41  */
42 class AmbientLightModeMonitor
43 @Inject
44 constructor(
45     private val algorithm: Optional<DebounceAlgorithm>,
46     private val sensorManager: AsyncSensorManager,
47     @Named(LIGHT_SENSOR) private val lightSensor: Optional<Provider<Sensor>>,
48 ) : Dumpable {
49     companion object {
50         private const val TAG = "AmbientLightModeMonitor"
51         private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
52 
53         const val AMBIENT_LIGHT_MODE_LIGHT = 0
54         const val AMBIENT_LIGHT_MODE_DARK = 1
55         const val AMBIENT_LIGHT_MODE_UNDECIDED = 2
56     }
57 
58     // Represents all ambient light modes.
59     @Retention(AnnotationRetention.SOURCE)
60     @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED)
61     annotation class AmbientLightMode
62 
63     /**
64      * Start monitoring the current ambient light mode.
65      *
66      * @param callback callback that gets triggered when the ambient light mode changes.
67      */
startnull68     fun start(callback: Callback) {
69         if (DEBUG) Log.d(TAG, "start monitoring ambient light mode")
70 
71         if (lightSensor.isEmpty || lightSensor.get().get() == null) {
72             if (DEBUG) Log.w(TAG, "light sensor not available")
73             return
74         }
75 
76         if (algorithm.isEmpty) {
77             if (DEBUG) Log.w(TAG, "debounce algorithm not available")
78             return
79         }
80 
81         algorithm.get().start(callback)
82         sensorManager.registerListener(
83             mSensorEventListener,
84             lightSensor.get().get(),
85             SensorManager.SENSOR_DELAY_NORMAL,
86         )
87     }
88 
89     /** Stop monitoring the current ambient light mode. */
stopnull90     fun stop() {
91         if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode")
92 
93         if (algorithm.isPresent) {
94             algorithm.get().stop()
95         }
96         sensorManager.unregisterListener(mSensorEventListener)
97     }
98 
dumpnull99     override fun dump(pw: PrintWriter, args: Array<out String>) {
100         pw.println()
101         pw.println("Ambient light mode monitor:")
102         pw.println("  lightSensor=$lightSensor")
103         pw.println()
104     }
105 
106     private val mSensorEventListener: SensorEventListener =
107         object : SensorEventListener {
onSensorChangednull108             override fun onSensorChanged(event: SensorEvent) {
109                 if (event.values.isEmpty()) {
110                     if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value")
111                     return
112                 }
113 
114                 if (algorithm.isPresent) {
115                     algorithm.get().onUpdateLightSensorEvent(event.values[0])
116                 }
117             }
118 
onAccuracyChangednull119             override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
120                 // Do nothing.
121             }
122         }
123 
124     /** Interface of the ambient light mode callback, which gets triggered when the mode changes. */
interfacenull125     fun interface Callback {
126         fun onChange(@AmbientLightMode mode: Int)
127     }
128 
129     /** Interface of the algorithm that transforms light sensor events to an ambient light mode. */
130     interface DebounceAlgorithm {
131         // Setting Callback to nullable so mockito can verify without throwing NullPointerException.
startnull132         fun start(callback: Callback?)
133 
134         fun stop()
135 
136         fun onUpdateLightSensorEvent(value: Float)
137     }
138 }
139