Loading packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.idle import android.annotation.IntDef import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager import android.util.Log import com.android.systemui.util.sensors.AsyncSensorManager import javax.inject.Inject import kotlin.properties.Delegates /** * Monitors ambient light signals, applies a debouncing algorithm, and produces the current * [AmbientLightMode]. * * For debouncer behavior, refer to go/titan-light-sensor-debouncer. * * @property sensorManager the sensor manager used to register sensor event updates. */ class AmbientLightModeMonitor @Inject constructor( private val sensorManager: AsyncSensorManager ) { companion object { private const val TAG = "AmbientLightModeMonitor" private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) const val AMBIENT_LIGHT_MODE_LIGHT = 0 const val AMBIENT_LIGHT_MODE_DARK = 1 const val AMBIENT_LIGHT_MODE_UNDECIDED = 2 } // Light sensor used to detect ambient lighting conditions. private val lightSensor: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) // Registered callback, which gets triggered when the ambient light mode changes. private var callback: Callback? = null // Represents all ambient light modes. @Retention(AnnotationRetention.SOURCE) @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED) annotation class AmbientLightMode // The current ambient light mode. @AmbientLightMode private var mode: Int by Delegates.observable(AMBIENT_LIGHT_MODE_UNDECIDED ) { _, old, new -> if (old != new) { callback?.onChange(new) } } /** * Start monitoring the current ambient light mode. * * @param callback callback that gets triggered when the ambient light mode changes. It also * gets triggered immediately to update the current value when this function is called. */ fun start(callback: Callback) { if (DEBUG) Log.d(TAG, "start monitoring ambient light mode") if (this.callback != null) { if (DEBUG) Log.w(TAG, "already started") return } this.callback = callback callback.onChange(mode) sensorManager.registerListener(mSensorEventListener, lightSensor, SensorManager.SENSOR_DELAY_NORMAL) } /** * Stop monitoring the current ambient light mode. */ fun stop() { if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode") if (callback == null) { if (DEBUG) Log.w(TAG, "haven't started") return } callback = null sensorManager.unregisterListener(mSensorEventListener) } private val mSensorEventListener: SensorEventListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { if (event.values.isEmpty()) { if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value") return } // TODO(b/201657509): add debouncing logic. val shouldBeLowLight = event.values[0] < 10 mode = if (shouldBeLowLight) AMBIENT_LIGHT_MODE_DARK else AMBIENT_LIGHT_MODE_LIGHT } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do nothing. } } /** * Interface of the ambient light mode callback, which gets triggered when the mode changes. */ interface Callback { fun onChange(@AmbientLightMode mode: Int) } } No newline at end of file packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java +34 −41 Original line number Diff line number Diff line Loading @@ -24,10 +24,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; Loading @@ -44,7 +40,6 @@ import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading @@ -56,8 +51,7 @@ import javax.inject.Provider; /** * {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen. */ public class IdleHostViewController extends ViewController<IdleHostView> implements SensorEventListener { public class IdleHostViewController extends ViewController<IdleHostView> { private static final String INPUT_MONITOR_IDENTIFIER = "IdleHostViewController"; private static final String TAG = "IdleHostViewController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); Loading Loading @@ -114,11 +108,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme private final PowerManager mPowerManager; private final AsyncSensorManager mSensorManager; // Light sensor used to detect low light condition. private final Sensor mSensor; // Runnable for canceling enabling idle. private Runnable mCancelEnableIdling; Loading Loading @@ -146,6 +135,9 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme // Intent filter for receiving dream broadcasts. private IntentFilter mDreamIntentFilter; // Monitor for the current ambient light mode. Used to trigger / exit low-light mode. private final AmbientLightModeMonitor mAmbientLightModeMonitor; // Delayed callback for starting idling. private final Runnable mEnableIdlingCallback = () -> { if (DEBUG) { Loading Loading @@ -181,6 +173,31 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme } }; private final AmbientLightModeMonitor.Callback mAmbientLightModeCallback = mode -> { boolean shouldBeLowLight; switch (mode) { case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED: return; case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT: shouldBeLowLight = false; break; case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK: shouldBeLowLight = true; break; default: Log.w(TAG, "invalid ambient light mode"); return; } if (DEBUG) Log.d(TAG, "ambient light mode changed to " + mode); final boolean isLowLight = getState(STATE_LOW_LIGHT); if (shouldBeLowLight != isLowLight) { setState(STATE_LOW_LIGHT, shouldBeLowLight); } }; final Provider<View> mIdleViewProvider; @Inject Loading @@ -188,7 +205,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme Context context, BroadcastDispatcher broadcastDispatcher, PowerManager powerManager, AsyncSensorManager sensorManager, IdleHostView view, InputMonitorFactory factory, @Main DelayableExecutor delayableExecutor, @Main Resources resources, Loading @@ -197,18 +213,19 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme Choreographer choreographer, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, DreamHelper dreamHelper) { DreamHelper dreamHelper, AmbientLightModeMonitor ambientLightModeMonitor) { super(view); mContext = context; mBroadcastDispatcher = broadcastDispatcher; mPowerManager = powerManager; mSensorManager = sensorManager; mIdleViewProvider = idleViewProvider; mKeyguardStateController = keyguardStateController; mStatusBarStateController = statusBarStateController; mLooper = looper; mChoreographer = choreographer; mDreamHelper = dreamHelper; mAmbientLightModeMonitor = ambientLightModeMonitor; mState = STATE_KEYGUARD_SHOWING; Loading @@ -222,7 +239,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme mIdleTimeout = resources.getInteger(R.integer.config_idleModeTimeout); mInputMonitorFactory = factory; mDelayableExecutor = delayableExecutor; mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); if (DEBUG) { Log.d(TAG, "initial state:" + mState + " enabled:" + enabled Loading Loading @@ -405,11 +421,10 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme if (mIsMonitoringLowLight) { if (DEBUG) Log.d(TAG, "enable low light monitoring"); mSensorManager.registerListener(this /*listener*/, mSensor, SensorManager.SENSOR_DELAY_NORMAL); mAmbientLightModeMonitor.start(mAmbientLightModeCallback); } else { if (DEBUG) Log.d(TAG, "disable low light monitoring"); mSensorManager.unregisterListener(this); mAmbientLightModeMonitor.stop(); } } Loading Loading @@ -449,28 +464,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme mStatusBarStateController.removeCallback(mStatusBarCallback); } @Override public void onSensorChanged(SensorEvent event) { if (event.values.length == 0) { if (DEBUG) Log.w(TAG, "SensorEvent doesn't have value"); return; } final boolean shouldBeLowLight = event.values[0] < 10; final boolean isLowLight = getState(STATE_LOW_LIGHT); if (shouldBeLowLight != isLowLight) { setState(STATE_LOW_LIGHT, shouldBeLowLight); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { if (DEBUG) { Log.d(TAG, "onAccuracyChanged accuracy=" + accuracy); } } // Returns whether the device just stopped idling by comparing the previous state with the // current one. private boolean stoppedIdling(int oldState) { Loading packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt 0 → 100644 +160 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.idle import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.sensors.AsyncSensorManager import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.reset import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.anyInt import org.mockito.Mockito.any import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) class AmbientLightModeMonitorTest : SysuiTestCase() { @Mock private lateinit var sensorManager: AsyncSensorManager @Mock private lateinit var sensor: Sensor private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor @Before fun setUp() { MockitoAnnotations.initMocks(this) `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(sensor) ambientLightModeMonitor = AmbientLightModeMonitor(sensorManager) } @Test fun shouldTriggerCallbackImmediatelyOnStart() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) // Monitor just started, should receive UNDECIDED. verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) // Receives SensorEvent, and now mode is LIGHT. val sensorEventListener = captureSensorEventListener() sensorEventListener.onSensorChanged(sensorEventWithSingleValue(15f)) // Stop monitor. ambientLightModeMonitor.stop() // Restart monitor. reset(callback) ambientLightModeMonitor.start(callback) // Verify receiving current mode (LIGHT) immediately. verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) } @Test fun shouldReportDarkModeWhenSensorValueIsLessThanTen() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) val sensorEventListener = captureSensorEventListener() reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(0f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(1f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(5f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(9.9f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) } @Test fun shouldReportLightModeWhenSensorValueIsGreaterThanOrEqualToTen() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) val sensorEventListener = captureSensorEventListener() reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(10f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(10.1f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(15f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(100f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) } @Test fun shouldOnlyTriggerCallbackWhenValueChanges() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) val sensorEventListener = captureSensorEventListener() // Light mode, should trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(20f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) // Light mode again, should NOT trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(25f)) verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) // Dark mode, should trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(2f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) // Dark mode again, should not trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(3f)) verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) } // Captures [SensorEventListener], assuming it has been registered with [sensorManager]. private fun captureSensorEventListener(): SensorEventListener { val captor = ArgumentCaptor.forClass(SensorEventListener::class.java) verify(sensorManager).registerListener(captor.capture(), any(), anyInt()) return captor.value } // Returns a [SensorEvent] with a single [value]. private fun sensorEventWithSingleValue(value: Float): SensorEvent { return SensorEvent(sensor, 1, 1, FloatArray(1) { value }) } } packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java +32 −34 Original line number Diff line number Diff line Loading @@ -28,8 +28,6 @@ import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.os.Looper; import android.os.PowerManager; import android.testing.AndroidTestingRunner; Loading @@ -46,7 +44,6 @@ import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import org.junit.Before; import org.junit.Test; Loading @@ -55,8 +52,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.time.Instant; import javax.inject.Provider; @SmallTest Loading @@ -64,7 +59,6 @@ import javax.inject.Provider; public class IdleHostViewControllerTest extends SysuiTestCase { @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private PowerManager mPowerManager; @Mock private AsyncSensorManager mSensorManager; @Mock private IdleHostView mIdleHostView; @Mock private InputMonitorFactory mInputMonitorFactory; @Mock private DelayableExecutor mDelayableExecutor; Loading @@ -74,12 +68,11 @@ public class IdleHostViewControllerTest extends SysuiTestCase { @Mock private Choreographer mChoreographer; @Mock private KeyguardStateController mKeyguardStateController; @Mock private StatusBarStateController mStatusBarStateController; @Mock private Sensor mSensor; @Mock private DreamHelper mDreamHelper; @Mock private InputMonitorCompat mInputMonitor; @Mock private InputChannelCompat.InputEventReceiver mInputEventReceiver; @Mock private AmbientLightModeMonitor mAmbientLightModeMonitor; private final long mTimestamp = Instant.now().toEpochMilli(); private KeyguardStateController.Callback mKeyguardStateCallback; private StatusBarStateController.StateListener mStatusBarStateListener; private IdleHostViewController mController; Loading @@ -90,15 +83,15 @@ public class IdleHostViewControllerTest extends SysuiTestCase { when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true); when(mStatusBarStateController.isDozing()).thenReturn(false); when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mSensor); when(mInputMonitorFactory.getInputMonitor("IdleHostViewController")) .thenReturn(mInputMonitor); when(mInputMonitor.getInputReceiver(any(), any(), any())).thenReturn(mInputEventReceiver); mController = new IdleHostViewController(mContext, mBroadcastDispatcher, mPowerManager, mSensorManager, mIdleHostView, mBroadcastDispatcher, mPowerManager, mIdleHostView, mInputMonitorFactory, mDelayableExecutor, mResources, mLooper, mViewProvider, mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper); mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper, mAmbientLightModeMonitor); mController.init(); mController.onViewAttached(); Loading @@ -122,9 +115,9 @@ public class IdleHostViewControllerTest extends SysuiTestCase { mKeyguardStateCallback.onKeyguardShowingChanged(); // Regular ambient lighting. final SensorEvent sensorEvent = new SensorEvent(mSensor, 3, mTimestamp, new float[]{90}); mController.onSensorChanged(sensorEvent); final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Times out. ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class); Loading @@ -149,9 +142,9 @@ public class IdleHostViewControllerTest extends SysuiTestCase { final BroadcastReceiver dreamBroadcastReceiver = dreamBroadcastReceiverCaptor.getValue(); // Low ambient lighting. final SensorEvent sensorEvent = new SensorEvent(mSensor, 3, mTimestamp, new float[]{5}); mController.onSensorChanged(sensorEvent); final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK); // Verifies it goes to sleep because of low light. verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); Loading Loading @@ -179,15 +172,15 @@ public class IdleHostViewControllerTest extends SysuiTestCase { @Test public void testTransitionBetweenIdleAndLowLightMode() { // Regular ambient lighting. final SensorEvent sensorEventRegularLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{90}); mController.onSensorChanged(sensorEventRegularLight); // Keyguard showing. when(mKeyguardStateController.isShowing()).thenReturn(true); mKeyguardStateCallback.onKeyguardShowingChanged(); // Regular ambient lighting. final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Times out. ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class); verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong()); Loading @@ -198,15 +191,13 @@ public class IdleHostViewControllerTest extends SysuiTestCase { clearInvocations(mDreamHelper); // Ambient lighting becomes dim. final SensorEvent sensorEventLowLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{5}); mController.onSensorChanged(sensorEventLowLight); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK); // Verifies in low light mode (dozing). verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); // Ambient lighting becomes bright again. mController.onSensorChanged(sensorEventRegularLight); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Verifies in idle mode (dreaming). verify(mDreamHelper).startDreaming(any()); Loading @@ -214,22 +205,20 @@ public class IdleHostViewControllerTest extends SysuiTestCase { @Test public void testStartDozingWhenLowLight() { // Regular ambient lighting. final SensorEvent sensorEventRegularLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{90}); mController.onSensorChanged(sensorEventRegularLight); // Keyguard showing. when(mKeyguardStateController.isShowing()).thenReturn(true); mKeyguardStateCallback.onKeyguardShowingChanged(); // Regular ambient lighting. final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Verifies it doesn't go to sleep yet. verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt()); // Ambient lighting becomes dim. final SensorEvent sensorEventLowLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{5}); mController.onSensorChanged(sensorEventLowLight); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK); // Verifies it goes to sleep. verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); Loading @@ -251,4 +240,13 @@ public class IdleHostViewControllerTest extends SysuiTestCase { // Should dispose input event receiver. verify(mInputEventReceiver).dispose(); } // Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor // has been started. private AmbientLightModeMonitor.Callback captureAmbientLightModeMonitorCallback() { ArgumentCaptor<AmbientLightModeMonitor.Callback> captor = ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class); verify(mAmbientLightModeMonitor).start(captor.capture()); return captor.getValue(); } } Loading
packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt 0 → 100644 +127 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.idle import android.annotation.IntDef import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager import android.util.Log import com.android.systemui.util.sensors.AsyncSensorManager import javax.inject.Inject import kotlin.properties.Delegates /** * Monitors ambient light signals, applies a debouncing algorithm, and produces the current * [AmbientLightMode]. * * For debouncer behavior, refer to go/titan-light-sensor-debouncer. * * @property sensorManager the sensor manager used to register sensor event updates. */ class AmbientLightModeMonitor @Inject constructor( private val sensorManager: AsyncSensorManager ) { companion object { private const val TAG = "AmbientLightModeMonitor" private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) const val AMBIENT_LIGHT_MODE_LIGHT = 0 const val AMBIENT_LIGHT_MODE_DARK = 1 const val AMBIENT_LIGHT_MODE_UNDECIDED = 2 } // Light sensor used to detect ambient lighting conditions. private val lightSensor: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT) // Registered callback, which gets triggered when the ambient light mode changes. private var callback: Callback? = null // Represents all ambient light modes. @Retention(AnnotationRetention.SOURCE) @IntDef(AMBIENT_LIGHT_MODE_LIGHT, AMBIENT_LIGHT_MODE_DARK, AMBIENT_LIGHT_MODE_UNDECIDED) annotation class AmbientLightMode // The current ambient light mode. @AmbientLightMode private var mode: Int by Delegates.observable(AMBIENT_LIGHT_MODE_UNDECIDED ) { _, old, new -> if (old != new) { callback?.onChange(new) } } /** * Start monitoring the current ambient light mode. * * @param callback callback that gets triggered when the ambient light mode changes. It also * gets triggered immediately to update the current value when this function is called. */ fun start(callback: Callback) { if (DEBUG) Log.d(TAG, "start monitoring ambient light mode") if (this.callback != null) { if (DEBUG) Log.w(TAG, "already started") return } this.callback = callback callback.onChange(mode) sensorManager.registerListener(mSensorEventListener, lightSensor, SensorManager.SENSOR_DELAY_NORMAL) } /** * Stop monitoring the current ambient light mode. */ fun stop() { if (DEBUG) Log.d(TAG, "stop monitoring ambient light mode") if (callback == null) { if (DEBUG) Log.w(TAG, "haven't started") return } callback = null sensorManager.unregisterListener(mSensorEventListener) } private val mSensorEventListener: SensorEventListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { if (event.values.isEmpty()) { if (DEBUG) Log.w(TAG, "SensorEvent doesn't have any value") return } // TODO(b/201657509): add debouncing logic. val shouldBeLowLight = event.values[0] < 10 mode = if (shouldBeLowLight) AMBIENT_LIGHT_MODE_DARK else AMBIENT_LIGHT_MODE_LIGHT } override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Do nothing. } } /** * Interface of the ambient light mode callback, which gets triggered when the mode changes. */ interface Callback { fun onChange(@AmbientLightMode mode: Int) } } No newline at end of file
packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java +34 −41 Original line number Diff line number Diff line Loading @@ -24,10 +24,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; Loading @@ -44,7 +40,6 @@ import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading @@ -56,8 +51,7 @@ import javax.inject.Provider; /** * {@link IdleHostViewController} processes signals to control the lifecycle of the idle screen. */ public class IdleHostViewController extends ViewController<IdleHostView> implements SensorEventListener { public class IdleHostViewController extends ViewController<IdleHostView> { private static final String INPUT_MONITOR_IDENTIFIER = "IdleHostViewController"; private static final String TAG = "IdleHostViewController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); Loading Loading @@ -114,11 +108,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme private final PowerManager mPowerManager; private final AsyncSensorManager mSensorManager; // Light sensor used to detect low light condition. private final Sensor mSensor; // Runnable for canceling enabling idle. private Runnable mCancelEnableIdling; Loading Loading @@ -146,6 +135,9 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme // Intent filter for receiving dream broadcasts. private IntentFilter mDreamIntentFilter; // Monitor for the current ambient light mode. Used to trigger / exit low-light mode. private final AmbientLightModeMonitor mAmbientLightModeMonitor; // Delayed callback for starting idling. private final Runnable mEnableIdlingCallback = () -> { if (DEBUG) { Loading Loading @@ -181,6 +173,31 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme } }; private final AmbientLightModeMonitor.Callback mAmbientLightModeCallback = mode -> { boolean shouldBeLowLight; switch (mode) { case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED: return; case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT: shouldBeLowLight = false; break; case AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK: shouldBeLowLight = true; break; default: Log.w(TAG, "invalid ambient light mode"); return; } if (DEBUG) Log.d(TAG, "ambient light mode changed to " + mode); final boolean isLowLight = getState(STATE_LOW_LIGHT); if (shouldBeLowLight != isLowLight) { setState(STATE_LOW_LIGHT, shouldBeLowLight); } }; final Provider<View> mIdleViewProvider; @Inject Loading @@ -188,7 +205,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme Context context, BroadcastDispatcher broadcastDispatcher, PowerManager powerManager, AsyncSensorManager sensorManager, IdleHostView view, InputMonitorFactory factory, @Main DelayableExecutor delayableExecutor, @Main Resources resources, Loading @@ -197,18 +213,19 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme Choreographer choreographer, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, DreamHelper dreamHelper) { DreamHelper dreamHelper, AmbientLightModeMonitor ambientLightModeMonitor) { super(view); mContext = context; mBroadcastDispatcher = broadcastDispatcher; mPowerManager = powerManager; mSensorManager = sensorManager; mIdleViewProvider = idleViewProvider; mKeyguardStateController = keyguardStateController; mStatusBarStateController = statusBarStateController; mLooper = looper; mChoreographer = choreographer; mDreamHelper = dreamHelper; mAmbientLightModeMonitor = ambientLightModeMonitor; mState = STATE_KEYGUARD_SHOWING; Loading @@ -222,7 +239,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme mIdleTimeout = resources.getInteger(R.integer.config_idleModeTimeout); mInputMonitorFactory = factory; mDelayableExecutor = delayableExecutor; mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); if (DEBUG) { Log.d(TAG, "initial state:" + mState + " enabled:" + enabled Loading Loading @@ -405,11 +421,10 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme if (mIsMonitoringLowLight) { if (DEBUG) Log.d(TAG, "enable low light monitoring"); mSensorManager.registerListener(this /*listener*/, mSensor, SensorManager.SENSOR_DELAY_NORMAL); mAmbientLightModeMonitor.start(mAmbientLightModeCallback); } else { if (DEBUG) Log.d(TAG, "disable low light monitoring"); mSensorManager.unregisterListener(this); mAmbientLightModeMonitor.stop(); } } Loading Loading @@ -449,28 +464,6 @@ public class IdleHostViewController extends ViewController<IdleHostView> impleme mStatusBarStateController.removeCallback(mStatusBarCallback); } @Override public void onSensorChanged(SensorEvent event) { if (event.values.length == 0) { if (DEBUG) Log.w(TAG, "SensorEvent doesn't have value"); return; } final boolean shouldBeLowLight = event.values[0] < 10; final boolean isLowLight = getState(STATE_LOW_LIGHT); if (shouldBeLowLight != isLowLight) { setState(STATE_LOW_LIGHT, shouldBeLowLight); } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { if (DEBUG) { Log.d(TAG, "onAccuracyChanged accuracy=" + accuracy); } } // Returns whether the device just stopped idling by comparing the previous state with the // current one. private boolean stoppedIdling(int oldState) { Loading
packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt 0 → 100644 +160 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.idle import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.sensors.AsyncSensorManager import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.reset import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.anyInt import org.mockito.Mockito.any import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) class AmbientLightModeMonitorTest : SysuiTestCase() { @Mock private lateinit var sensorManager: AsyncSensorManager @Mock private lateinit var sensor: Sensor private lateinit var ambientLightModeMonitor: AmbientLightModeMonitor @Before fun setUp() { MockitoAnnotations.initMocks(this) `when`(sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(sensor) ambientLightModeMonitor = AmbientLightModeMonitor(sensorManager) } @Test fun shouldTriggerCallbackImmediatelyOnStart() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) // Monitor just started, should receive UNDECIDED. verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_UNDECIDED) // Receives SensorEvent, and now mode is LIGHT. val sensorEventListener = captureSensorEventListener() sensorEventListener.onSensorChanged(sensorEventWithSingleValue(15f)) // Stop monitor. ambientLightModeMonitor.stop() // Restart monitor. reset(callback) ambientLightModeMonitor.start(callback) // Verify receiving current mode (LIGHT) immediately. verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) } @Test fun shouldReportDarkModeWhenSensorValueIsLessThanTen() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) val sensorEventListener = captureSensorEventListener() reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(0f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(1f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(5f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(9.9f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) } @Test fun shouldReportLightModeWhenSensorValueIsGreaterThanOrEqualToTen() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) val sensorEventListener = captureSensorEventListener() reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(10f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(10.1f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(15f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(100f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) } @Test fun shouldOnlyTriggerCallbackWhenValueChanges() { val callback = mock(AmbientLightModeMonitor.Callback::class.java) ambientLightModeMonitor.start(callback) val sensorEventListener = captureSensorEventListener() // Light mode, should trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(20f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) // Light mode again, should NOT trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(25f)) verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT) // Dark mode, should trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(2f)) verify(callback).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) // Dark mode again, should not trigger callback. reset(callback) sensorEventListener.onSensorChanged(sensorEventWithSingleValue(3f)) verify(callback, never()).onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK) } // Captures [SensorEventListener], assuming it has been registered with [sensorManager]. private fun captureSensorEventListener(): SensorEventListener { val captor = ArgumentCaptor.forClass(SensorEventListener::class.java) verify(sensorManager).registerListener(captor.capture(), any(), anyInt()) return captor.value } // Returns a [SensorEvent] with a single [value]. private fun sensorEventWithSingleValue(value: Float): SensorEvent { return SensorEvent(sensor, 1, 1, FloatArray(1) { value }) } }
packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java +32 −34 Original line number Diff line number Diff line Loading @@ -28,8 +28,6 @@ import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Intent; import android.content.res.Resources; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.os.Looper; import android.os.PowerManager; import android.testing.AndroidTestingRunner; Loading @@ -46,7 +44,6 @@ import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.sensors.AsyncSensorManager; import org.junit.Before; import org.junit.Test; Loading @@ -55,8 +52,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.time.Instant; import javax.inject.Provider; @SmallTest Loading @@ -64,7 +59,6 @@ import javax.inject.Provider; public class IdleHostViewControllerTest extends SysuiTestCase { @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private PowerManager mPowerManager; @Mock private AsyncSensorManager mSensorManager; @Mock private IdleHostView mIdleHostView; @Mock private InputMonitorFactory mInputMonitorFactory; @Mock private DelayableExecutor mDelayableExecutor; Loading @@ -74,12 +68,11 @@ public class IdleHostViewControllerTest extends SysuiTestCase { @Mock private Choreographer mChoreographer; @Mock private KeyguardStateController mKeyguardStateController; @Mock private StatusBarStateController mStatusBarStateController; @Mock private Sensor mSensor; @Mock private DreamHelper mDreamHelper; @Mock private InputMonitorCompat mInputMonitor; @Mock private InputChannelCompat.InputEventReceiver mInputEventReceiver; @Mock private AmbientLightModeMonitor mAmbientLightModeMonitor; private final long mTimestamp = Instant.now().toEpochMilli(); private KeyguardStateController.Callback mKeyguardStateCallback; private StatusBarStateController.StateListener mStatusBarStateListener; private IdleHostViewController mController; Loading @@ -90,15 +83,15 @@ public class IdleHostViewControllerTest extends SysuiTestCase { when(mResources.getBoolean(R.bool.config_enableIdleMode)).thenReturn(true); when(mStatusBarStateController.isDozing()).thenReturn(false); when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mSensor); when(mInputMonitorFactory.getInputMonitor("IdleHostViewController")) .thenReturn(mInputMonitor); when(mInputMonitor.getInputReceiver(any(), any(), any())).thenReturn(mInputEventReceiver); mController = new IdleHostViewController(mContext, mBroadcastDispatcher, mPowerManager, mSensorManager, mIdleHostView, mBroadcastDispatcher, mPowerManager, mIdleHostView, mInputMonitorFactory, mDelayableExecutor, mResources, mLooper, mViewProvider, mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper); mChoreographer, mKeyguardStateController, mStatusBarStateController, mDreamHelper, mAmbientLightModeMonitor); mController.init(); mController.onViewAttached(); Loading @@ -122,9 +115,9 @@ public class IdleHostViewControllerTest extends SysuiTestCase { mKeyguardStateCallback.onKeyguardShowingChanged(); // Regular ambient lighting. final SensorEvent sensorEvent = new SensorEvent(mSensor, 3, mTimestamp, new float[]{90}); mController.onSensorChanged(sensorEvent); final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Times out. ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class); Loading @@ -149,9 +142,9 @@ public class IdleHostViewControllerTest extends SysuiTestCase { final BroadcastReceiver dreamBroadcastReceiver = dreamBroadcastReceiverCaptor.getValue(); // Low ambient lighting. final SensorEvent sensorEvent = new SensorEvent(mSensor, 3, mTimestamp, new float[]{5}); mController.onSensorChanged(sensorEvent); final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK); // Verifies it goes to sleep because of low light. verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); Loading Loading @@ -179,15 +172,15 @@ public class IdleHostViewControllerTest extends SysuiTestCase { @Test public void testTransitionBetweenIdleAndLowLightMode() { // Regular ambient lighting. final SensorEvent sensorEventRegularLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{90}); mController.onSensorChanged(sensorEventRegularLight); // Keyguard showing. when(mKeyguardStateController.isShowing()).thenReturn(true); mKeyguardStateCallback.onKeyguardShowingChanged(); // Regular ambient lighting. final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Times out. ArgumentCaptor<Runnable> callbackCapture = ArgumentCaptor.forClass(Runnable.class); verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong()); Loading @@ -198,15 +191,13 @@ public class IdleHostViewControllerTest extends SysuiTestCase { clearInvocations(mDreamHelper); // Ambient lighting becomes dim. final SensorEvent sensorEventLowLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{5}); mController.onSensorChanged(sensorEventLowLight); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK); // Verifies in low light mode (dozing). verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); // Ambient lighting becomes bright again. mController.onSensorChanged(sensorEventRegularLight); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Verifies in idle mode (dreaming). verify(mDreamHelper).startDreaming(any()); Loading @@ -214,22 +205,20 @@ public class IdleHostViewControllerTest extends SysuiTestCase { @Test public void testStartDozingWhenLowLight() { // Regular ambient lighting. final SensorEvent sensorEventRegularLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{90}); mController.onSensorChanged(sensorEventRegularLight); // Keyguard showing. when(mKeyguardStateController.isShowing()).thenReturn(true); mKeyguardStateCallback.onKeyguardShowingChanged(); // Regular ambient lighting. final AmbientLightModeMonitor.Callback lightMonitorCallback = captureAmbientLightModeMonitorCallback(); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_LIGHT); // Verifies it doesn't go to sleep yet. verify(mPowerManager, never()).goToSleep(anyLong(), anyInt(), anyInt()); // Ambient lighting becomes dim. final SensorEvent sensorEventLowLight = new SensorEvent(mSensor, 3, mTimestamp, new float[]{5}); mController.onSensorChanged(sensorEventLowLight); lightMonitorCallback.onChange(AmbientLightModeMonitor.AMBIENT_LIGHT_MODE_DARK); // Verifies it goes to sleep. verify(mPowerManager).goToSleep(anyLong(), anyInt(), anyInt()); Loading @@ -251,4 +240,13 @@ public class IdleHostViewControllerTest extends SysuiTestCase { // Should dispose input event receiver. verify(mInputEventReceiver).dispose(); } // Captures [AmbientLightModeMonitor.Callback] assuming that the ambient light mode monitor // has been started. private AmbientLightModeMonitor.Callback captureAmbientLightModeMonitorCallback() { ArgumentCaptor<AmbientLightModeMonitor.Callback> captor = ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class); verify(mAmbientLightModeMonitor).start(captor.capture()); return captor.getValue(); } }