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