diff --git a/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt b/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt new file mode 100644 index 0000000000000..85644970c5e08 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/idle/AmbientLightModeMonitor.kt @@ -0,0 +1,127 @@ +/* + * 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 diff --git a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java index 192c4184a6eb8..6b212b7b2ca02 100644 --- a/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java +++ b/packages/SystemUI/src/com/android/systemui/idle/IdleHostViewController.java @@ -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; @@ -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; @@ -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 implements - SensorEventListener { +public class IdleHostViewController extends ViewController { private static final String INPUT_MONITOR_IDENTIFIER = "IdleHostViewController"; private static final String TAG = "IdleHostViewController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -114,11 +108,6 @@ public class IdleHostViewController extends ViewController 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; @@ -146,6 +135,9 @@ public class IdleHostViewController extends ViewController 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) { @@ -181,6 +173,31 @@ public class IdleHostViewController extends ViewController 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 mIdleViewProvider; @Inject @@ -188,7 +205,6 @@ public class IdleHostViewController extends ViewController impleme Context context, BroadcastDispatcher broadcastDispatcher, PowerManager powerManager, - AsyncSensorManager sensorManager, IdleHostView view, InputMonitorFactory factory, @Main DelayableExecutor delayableExecutor, @Main Resources resources, @@ -197,18 +213,19 @@ public class IdleHostViewController extends ViewController 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; @@ -222,7 +239,6 @@ public class IdleHostViewController extends ViewController 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 @@ -405,11 +421,10 @@ public class IdleHostViewController extends ViewController 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(); } } @@ -449,28 +464,6 @@ public class IdleHostViewController extends ViewController 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) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt new file mode 100644 index 0000000000000..9bd5bc7a9d3de --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/idle/AmbientLightModeMonitorTest.kt @@ -0,0 +1,160 @@ +/* + * 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 }) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java index a685e20a3c858..8ff33a0fc5fd5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/idle/IdleHostViewControllerTest.java @@ -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; @@ -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; @@ -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 @@ -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; @@ -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; @@ -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(); @@ -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 callbackCapture = ArgumentCaptor.forClass(Runnable.class); @@ -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()); @@ -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 callbackCapture = ArgumentCaptor.forClass(Runnable.class); verify(mDelayableExecutor).executeDelayed(callbackCapture.capture(), anyLong()); @@ -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()); @@ -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()); @@ -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 captor = + ArgumentCaptor.forClass(AmbientLightModeMonitor.Callback.class); + verify(mAmbientLightModeMonitor).start(captor.capture()); + return captor.getValue(); + } }