diff options
author | 2023-01-13 14:33:14 +0000 | |
---|---|---|
committer | 2023-01-13 14:33:14 +0000 | |
commit | 686cd7cdd4db973a30b97477beed91862bb5e33d (patch) | |
tree | e2b8c7977cba3b09dcd026e75d4f09fce29e32af | |
parent | 2bd5e20280dcfb880668705152a09ab4a85e03da (diff) | |
parent | feb44ada1282f3dc6c85765f593524825996a71b (diff) |
Merge "Add inactivity timer to turn off keyboard backlight"
5 files changed, 279 insertions, 124 deletions
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java index ca42614624ea..4d03e44bfd19 100644 --- a/services/core/java/com/android/server/input/InputManagerInternal.java +++ b/services/core/java/com/android/server/input/InputManagerInternal.java @@ -213,4 +213,10 @@ public abstract class InputManagerInternal { * @param enabled When true, stylus buttons will not be reported through motion events. */ public abstract void setStylusButtonMotionEventsEnabled(boolean enabled); + + /** + * Notify whether any user activity occurred. This includes any input activity on any + * display, external peripherals, fingerprint sensor, etc. + */ + public abstract void notifyUserActivity(); } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index e86632c588ad..be4373ab48fc 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3271,6 +3271,7 @@ public class InputManagerService extends IInputManager.Stub public void setInteractive(boolean interactive) { mNative.setInteractive(interactive); mBatteryController.onInteractiveChanged(interactive); + mKeyboardBacklightController.onInteractiveChanged(interactive); } @Override @@ -3358,6 +3359,11 @@ public class InputManagerService extends IInputManager.Stub } @Override + public void notifyUserActivity() { + mKeyboardBacklightController.notifyUserActivity(); + } + + @Override public void incrementKeyboardBacklight(int deviceId) { mKeyboardBacklightController.incrementKeyboardBacklight(deviceId); } @@ -3489,6 +3495,8 @@ public class InputManagerService extends IInputManager.Stub default void decrementKeyboardBacklight(int deviceId) {} default void registerKeyboardBacklightListener(IKeyboardBacklightListener l, int pid) {} default void unregisterKeyboardBacklightListener(IKeyboardBacklightListener l, int pid) {} + default void onInteractiveChanged(boolean isInteractive) {} + default void notifyUserActivity() {} default void systemRunning() {} default void dump(PrintWriter pw) {} } diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java index 653a821f4193..e1e3dd9967e0 100644 --- a/services/core/java/com/android/server/input/KeyboardBacklightController.java +++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java @@ -17,7 +17,6 @@ package com.android.server.input; import android.annotation.BinderThread; -import android.annotation.ColorInt; import android.content.Context; import android.graphics.Color; import android.hardware.input.IKeyboardBacklightListener; @@ -29,6 +28,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemClock; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; @@ -39,9 +39,10 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.time.Duration; +import java.util.Arrays; import java.util.Objects; import java.util.OptionalInt; -import java.util.TreeSet; /** * A thread-safe component of {@link InputManagerService} responsible for managing the keyboard @@ -59,12 +60,20 @@ final class KeyboardBacklightController implements private enum Direction { DIRECTION_UP, DIRECTION_DOWN } - private static final int MSG_INCREMENT_KEYBOARD_BACKLIGHT = 1; - private static final int MSG_DECREMENT_KEYBOARD_BACKLIGHT = 2; + private static final int MSG_UPDATE_EXISTING_DEVICES = 1; + private static final int MSG_INCREMENT_KEYBOARD_BACKLIGHT = 2; + private static final int MSG_DECREMENT_KEYBOARD_BACKLIGHT = 3; + private static final int MSG_NOTIFY_USER_ACTIVITY = 4; + private static final int MSG_NOTIFY_USER_INACTIVITY = 5; + private static final int MSG_INTERACTIVE_STATE_CHANGED = 6; private static final int MAX_BRIGHTNESS = 255; private static final int NUM_BRIGHTNESS_CHANGE_STEPS = 10; + + @VisibleForTesting + static final long USER_INACTIVITY_THRESHOLD_MILLIS = Duration.ofSeconds(30).toMillis(); + @VisibleForTesting - static final TreeSet<Integer> BRIGHTNESS_LEVELS = new TreeSet<>(); + static final int[] BRIGHTNESS_VALUE_FOR_LEVEL = new int[NUM_BRIGHTNESS_CHANGE_STEPS + 1]; private final Context mContext; private final NativeInputManagerService mNative; @@ -72,7 +81,12 @@ final class KeyboardBacklightController implements @GuardedBy("mDataStore") private final PersistentDataStore mDataStore; private final Handler mHandler; - private final SparseArray<Light> mKeyboardBacklights = new SparseArray<>(); + // Always access on handler thread or need to lock this for synchronization. + private final SparseArray<KeyboardBacklightState> mKeyboardBacklights = new SparseArray<>(1); + // Maintains state if all backlights should be on or turned off + private boolean mIsBacklightOn = false; + // Maintains state if currently the device is interactive or not + private boolean mIsInteractive = true; // List of currently registered keyboard backlight listeners @GuardedBy("mKeyboardBacklightListenerRecords") @@ -84,8 +98,8 @@ final class KeyboardBacklightController implements // device brightness range to [0-255] // Levels are: 0, 25, 51, ..., 255 for (int i = 0; i <= NUM_BRIGHTNESS_CHANGE_STEPS; i++) { - BRIGHTNESS_LEVELS.add( - (int) Math.floor(((float) i * MAX_BRIGHTNESS) / NUM_BRIGHTNESS_CHANGE_STEPS)); + BRIGHTNESS_VALUE_FOR_LEVEL[i] = (int) Math.floor( + ((float) i * MAX_BRIGHTNESS) / NUM_BRIGHTNESS_CHANGE_STEPS); } } @@ -102,10 +116,10 @@ final class KeyboardBacklightController implements InputManager inputManager = Objects.requireNonNull( mContext.getSystemService(InputManager.class)); inputManager.registerInputDeviceListener(this, mHandler); - // Circle through all the already added input devices - for (int deviceId : inputManager.getInputDeviceIds()) { - onInputDeviceAdded(deviceId); - } + + Message msg = Message.obtain(mHandler, MSG_UPDATE_EXISTING_DEVICES, + inputManager.getInputDeviceIds()); + mHandler.sendMessage(msg); } @Override @@ -120,37 +134,41 @@ final class KeyboardBacklightController implements mHandler.sendMessage(msg); } + @Override + public void notifyUserActivity() { + Message msg = Message.obtain(mHandler, MSG_NOTIFY_USER_ACTIVITY); + mHandler.sendMessage(msg); + } + + @Override + public void onInteractiveChanged(boolean isInteractive) { + Message msg = Message.obtain(mHandler, MSG_INTERACTIVE_STATE_CHANGED, isInteractive); + mHandler.sendMessage(msg); + } + private void updateKeyboardBacklight(int deviceId, Direction direction) { InputDevice inputDevice = getInputDevice(deviceId); - Light keyboardBacklight = mKeyboardBacklights.get(deviceId); - if (inputDevice == null || keyboardBacklight == null) { + KeyboardBacklightState state = mKeyboardBacklights.get(deviceId); + if (inputDevice == null || state == null) { return; } + Light keyboardBacklight = state.mLight; // Follow preset levels of brightness defined in BRIGHTNESS_LEVELS - int currBrightness = BRIGHTNESS_LEVELS.floor(Color.alpha( - mNative.getLightColor(deviceId, keyboardBacklight.getId()))); - int newBrightness; + final int currBrightnessLevel = state.mBrightnessLevel; + final int newBrightnessLevel; if (direction == Direction.DIRECTION_UP) { - newBrightness = currBrightness != MAX_BRIGHTNESS ? BRIGHTNESS_LEVELS.higher( - currBrightness) : currBrightness; + newBrightnessLevel = Math.min(currBrightnessLevel + 1, NUM_BRIGHTNESS_CHANGE_STEPS); } else { - newBrightness = currBrightness != 0 ? BRIGHTNESS_LEVELS.lower(currBrightness) - : currBrightness; + newBrightnessLevel = Math.max(currBrightnessLevel - 1, 0); } - @ColorInt int newColor = Color.argb(newBrightness, 0, 0, 0); - mNative.setLightColor(deviceId, keyboardBacklight.getId(), newColor); - if (DEBUG) { - Slog.d(TAG, "Changing brightness from " + currBrightness + " to " + newBrightness); - } - - notifyKeyboardBacklightChanged(deviceId, BRIGHTNESS_LEVELS.headSet(newBrightness).size(), - true/* isTriggeredByKeyPress */); + updateBacklightState(deviceId, keyboardBacklight, newBrightnessLevel, + true /* isTriggeredByKeyPress */); synchronized (mDataStore) { try { mDataStore.setKeyboardBacklightBrightness(inputDevice.getDescriptor(), keyboardBacklight.getId(), - newBrightness); + BRIGHTNESS_VALUE_FOR_LEVEL[newBrightnessLevel]); } finally { mDataStore.saveIfNeeded(); } @@ -163,23 +181,83 @@ final class KeyboardBacklightController implements brightness = mDataStore.getKeyboardBacklightBrightness( inputDevice.getDescriptor(), keyboardBacklight.getId()); } - if (!brightness.isEmpty()) { - mNative.setLightColor(inputDevice.getId(), keyboardBacklight.getId(), - Color.argb(brightness.getAsInt(), 0, 0, 0)); + if (brightness.isPresent()) { + int brightnessValue = Math.max(0, Math.min(MAX_BRIGHTNESS, brightness.getAsInt())); + int brightnessLevel = Arrays.binarySearch(BRIGHTNESS_VALUE_FOR_LEVEL, brightnessValue); + updateBacklightState(inputDevice.getId(), keyboardBacklight, brightnessLevel, + false /* isTriggeredByKeyPress */); if (DEBUG) { Slog.d(TAG, "Restoring brightness level " + brightness.getAsInt()); } } } + private void handleUserActivity() { + // Ignore user activity if device is not interactive. When device becomes interactive, we + // will send another user activity to turn backlight on. + if (!mIsInteractive) { + return; + } + if (!mIsBacklightOn) { + mIsBacklightOn = true; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + int deviceId = mKeyboardBacklights.keyAt(i); + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + updateBacklightState(deviceId, state.mLight, state.mBrightnessLevel, + false /* isTriggeredByKeyPress */); + } + } + mHandler.removeMessages(MSG_NOTIFY_USER_INACTIVITY); + mHandler.sendEmptyMessageAtTime(MSG_NOTIFY_USER_INACTIVITY, + SystemClock.uptimeMillis() + USER_INACTIVITY_THRESHOLD_MILLIS); + } + + private void handleUserInactivity() { + if (mIsBacklightOn) { + mIsBacklightOn = false; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + int deviceId = mKeyboardBacklights.keyAt(i); + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + updateBacklightState(deviceId, state.mLight, state.mBrightnessLevel, + false /* isTriggeredByKeyPress */); + } + } + } + + @VisibleForTesting + public void handleInteractiveStateChange(boolean isInteractive) { + // Interactive state changes should force the keyboard to turn on/off irrespective of + // whether time out occurred or not. + mIsInteractive = isInteractive; + if (isInteractive) { + handleUserActivity(); + } else { + handleUserInactivity(); + } + } + private boolean handleMessage(Message msg) { switch (msg.what) { + case MSG_UPDATE_EXISTING_DEVICES: + for (int deviceId : (int[]) msg.obj) { + onInputDeviceAdded(deviceId); + } + return true; case MSG_INCREMENT_KEYBOARD_BACKLIGHT: updateKeyboardBacklight((int) msg.obj, Direction.DIRECTION_UP); return true; case MSG_DECREMENT_KEYBOARD_BACKLIGHT: updateKeyboardBacklight((int) msg.obj, Direction.DIRECTION_DOWN); return true; + case MSG_NOTIFY_USER_ACTIVITY: + handleUserActivity(); + return true; + case MSG_NOTIFY_USER_INACTIVITY: + handleUserInactivity(); + return true; + case MSG_INTERACTIVE_STATE_CHANGED: + handleInteractiveStateChange((boolean) msg.obj); + return true; } return false; } @@ -208,12 +286,12 @@ final class KeyboardBacklightController implements mKeyboardBacklights.remove(deviceId); return; } - final Light oldBacklight = mKeyboardBacklights.get(deviceId); - if (oldBacklight != null && oldBacklight.getId() == keyboardBacklight.getId()) { + KeyboardBacklightState state = mKeyboardBacklights.get(deviceId); + if (state != null && state.mLight.getId() == keyboardBacklight.getId()) { return; } // The keyboard backlight was added or changed. - mKeyboardBacklights.put(deviceId, keyboardBacklight); + mKeyboardBacklights.put(deviceId, new KeyboardBacklightState(keyboardBacklight)); restoreBacklightBrightness(inputDevice, keyboardBacklight); } @@ -275,13 +353,29 @@ final class KeyboardBacklightController implements } } - private void notifyKeyboardBacklightChanged(int deviceId, int currentBacklightLevel, + private void updateBacklightState(int deviceId, Light light, int brightnessLevel, boolean isTriggeredByKeyPress) { + KeyboardBacklightState state = mKeyboardBacklights.get(deviceId); + if (state == null) { + return; + } + + mNative.setLightColor(deviceId, light.getId(), + mIsBacklightOn ? Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[brightnessLevel], 0, 0, 0) + : 0); + if (DEBUG) { + Slog.d(TAG, "Changing state from " + state.mBrightnessLevel + " to " + brightnessLevel + + "(isBacklightOn = " + mIsBacklightOn + ")"); + } + state.mBrightnessLevel = brightnessLevel; + synchronized (mKeyboardBacklightListenerRecords) { for (int i = 0; i < mKeyboardBacklightListenerRecords.size(); i++) { + IKeyboardBacklightState callbackState = new IKeyboardBacklightState(); + callbackState.brightnessLevel = brightnessLevel; + callbackState.maxBrightnessLevel = NUM_BRIGHTNESS_CHANGE_STEPS; mKeyboardBacklightListenerRecords.valueAt(i).notifyKeyboardBacklightChanged( - deviceId, new KeyboardBacklightState(currentBacklightLevel), - isTriggeredByKeyPress); + deviceId, callbackState, isTriggeredByKeyPress); } } } @@ -295,11 +389,14 @@ final class KeyboardBacklightController implements @Override public void dump(PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); - ipw.println(TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights"); + ipw.println( + TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights, isBacklightOn = " + + mIsBacklightOn); + ipw.increaseIndent(); for (int i = 0; i < mKeyboardBacklights.size(); i++) { - Light light = mKeyboardBacklights.get(i); - ipw.println(i + ": { id: " + light.getId() + ", name: " + light.getName() + " }"); + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + ipw.println(i + ": " + state.toString()); } ipw.decreaseIndent(); } @@ -334,17 +431,18 @@ final class KeyboardBacklightController implements } } - private static class KeyboardBacklightState extends IKeyboardBacklightState { + private static class KeyboardBacklightState { + private final Light mLight; + private int mBrightnessLevel; - KeyboardBacklightState(int brightnessLevel) { - this.brightnessLevel = brightnessLevel; - this.maxBrightnessLevel = NUM_BRIGHTNESS_CHANGE_STEPS; + KeyboardBacklightState(Light light) { + mLight = light; } @Override public String toString() { - return "KeyboardBacklightState{brightnessLevel=" + brightnessLevel - + ", maxBrightnessLevel=" + maxBrightnessLevel + return "KeyboardBacklightState{Light=" + mLight.getId() + + ", BrightnessLevel=" + mBrightnessLevel + "}"; } } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 326d70930639..ed6a46f9b43f 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -737,6 +737,7 @@ public class Notifier { } TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); tm.notifyUserActivity(); + mInputManagerInternal.notifyUserActivity(); mPolicy.userActivity(displayGroupId, event); mFaceDownDetector.userActivity(event); mScreenUndimDetector.userActivity(displayGroupId); diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt index b5dad945ffac..1d23e12d0a10 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -28,7 +28,8 @@ import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.view.InputDevice import androidx.test.core.app.ApplicationProvider -import com.android.server.input.KeyboardBacklightController.BRIGHTNESS_LEVELS +import com.android.server.input.KeyboardBacklightController.BRIGHTNESS_VALUE_FOR_LEVEL +import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -78,6 +79,7 @@ class KeyboardBacklightControllerTests { const val DEVICE_ID = 1 const val LIGHT_ID = 2 const val SECOND_LIGHT_ID = 3 + const val MAX_BRIGHTNESS = 255 } @get:Rule @@ -118,10 +120,6 @@ class KeyboardBacklightControllerTests { val args = it.arguments lightColorMap.put(args[1] as Int, args[2] as Int) } - `when`(native.getLightColor(anyInt(), anyInt())).then { - val args = it.arguments - lightColorMap.getOrDefault(args[1] as Int, -1) - } lightColorMap.clear() } @@ -137,94 +135,67 @@ class KeyboardBacklightControllerTests { `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - // Initially backlight is at min - lightColorMap[LIGHT_ID] = Color.argb(BRIGHTNESS_LEVELS.first(), 0, 0, 0) - - val brightnessLevelsArray = BRIGHTNESS_LEVELS.toTypedArray() - for (level in 1 until brightnessLevelsArray.size) { - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() - assertEquals( - "Light value for level $level mismatched", - Color.argb(brightnessLevelsArray[level], 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for level $level must be correctly stored in the datastore", - brightnessLevelsArray[level], - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) - } - for (level in brightnessLevelsArray.size - 2 downTo 0) { - keyboardBacklightController.decrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + for (level in 1 until BRIGHTNESS_VALUE_FOR_LEVEL.size) { + incrementKeyboardBacklight(DEVICE_ID) assertEquals( "Light value for level $level mismatched", - Color.argb(brightnessLevelsArray[level], 0, 0, 0), + Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), lightColorMap[LIGHT_ID] ) assertEquals( "Light value for level $level must be correctly stored in the datastore", - brightnessLevelsArray[level], + BRIGHTNESS_VALUE_FOR_LEVEL[level], dataStore.getKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID ).asInt ) } - } - @Test - fun testKeyboardBacklightIncrementAboveMaxLevel() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - // Initially backlight is at max - lightColorMap[LIGHT_ID] = Color.argb(BRIGHTNESS_LEVELS.last(), 0, 0, 0) - - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + // Increment above max level + incrementKeyboardBacklight(DEVICE_ID) assertEquals( "Light value for max level mismatched", - Color.argb(BRIGHTNESS_LEVELS.last(), 0, 0, 0), + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), lightColorMap[LIGHT_ID] ) assertEquals( "Light value for max level must be correctly stored in the datastore", - BRIGHTNESS_LEVELS.last(), + MAX_BRIGHTNESS, dataStore.getKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID ).asInt ) - } - @Test - fun testKeyboardBacklightDecrementBelowMin() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - // Initially backlight is at min - lightColorMap[LIGHT_ID] = Color.argb(BRIGHTNESS_LEVELS.first(), 0, 0, 0) + for (level in BRIGHTNESS_VALUE_FOR_LEVEL.size - 2 downTo 0) { + decrementKeyboardBacklight(DEVICE_ID) + assertEquals( + "Light value for level $level mismatched", + Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + assertEquals( + "Light value for level $level must be correctly stored in the datastore", + BRIGHTNESS_VALUE_FOR_LEVEL[level], + dataStore.getKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID + ).asInt + ) + } - keyboardBacklightController.decrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + // Decrement below min level + decrementKeyboardBacklight(DEVICE_ID) assertEquals( "Light value for min level mismatched", - Color.argb(BRIGHTNESS_LEVELS.first(), 0, 0, 0), + Color.argb(0, 0, 0, 0), lightColorMap[LIGHT_ID] ) assertEquals( "Light value for min level must be correctly stored in the datastore", - BRIGHTNESS_LEVELS.first(), + 0, dataStore.getKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID @@ -240,7 +211,7 @@ class KeyboardBacklightControllerTests { `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) + incrementKeyboardBacklight(DEVICE_ID) assertTrue("Non Keyboard backlights should not change", lightColorMap.isEmpty()) } @@ -258,8 +229,7 @@ class KeyboardBacklightControllerTests { ) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + incrementKeyboardBacklight(DEVICE_ID) assertEquals("Only keyboard backlights should change", 1, lightColorMap.size) assertNotNull("Keyboard backlight should change", lightColorMap[LIGHT_ID]) assertNull("Input lights should not change", lightColorMap[SECOND_LIGHT_ID]) @@ -275,14 +245,15 @@ class KeyboardBacklightControllerTests { dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, - BRIGHTNESS_LEVELS.last() + MAX_BRIGHTNESS ) - lightColorMap.clear() keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() assertEquals( "Keyboard backlight level should be restored to the level saved in the data store", - Color.argb(BRIGHTNESS_LEVELS.last(), 0, 0, 0), + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), lightColorMap[LIGHT_ID] ) } @@ -295,11 +266,12 @@ class KeyboardBacklightControllerTests { dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, - BRIGHTNESS_LEVELS.last() + MAX_BRIGHTNESS ) - lightColorMap.clear() keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() assertTrue( "Keyboard backlight should not be changed until its added", lightColorMap.isEmpty() @@ -307,22 +279,22 @@ class KeyboardBacklightControllerTests { `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() assertEquals( "Keyboard backlight level should be restored to the level saved in the data store", - Color.argb(BRIGHTNESS_LEVELS.last(), 0, 0, 0), + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), lightColorMap[LIGHT_ID] ) } @Test - fun testKeyboardBacklightT_registerUnregisterListener() { + fun testKeyboardBacklight_registerUnregisterListener() { val keyboardWithBacklight = createKeyboard(DEVICE_ID) val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - // Initially backlight is at min - lightColorMap[LIGHT_ID] = Color.argb(BRIGHTNESS_LEVELS.first(), 0, 0, 0) // Register backlight listener val listener = KeyboardBacklightListener() @@ -343,8 +315,8 @@ class KeyboardBacklightControllerTests { lastBacklightState!!.brightnessLevel ) assertEquals( - "Backlight state maxBrightnessLevel should be " + (BRIGHTNESS_LEVELS.size - 1), - (BRIGHTNESS_LEVELS.size - 1), + "Backlight state maxBrightnessLevel should be " + (BRIGHTNESS_VALUE_FOR_LEVEL.size - 1), + (BRIGHTNESS_VALUE_FOR_LEVEL.size - 1), lastBacklightState!!.maxBrightnessLevel ) assertEquals( @@ -357,12 +329,70 @@ class KeyboardBacklightControllerTests { keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0) lastBacklightState = null - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + incrementKeyboardBacklight(DEVICE_ID) assertNull("Listener should not receive any updates", lastBacklightState) } + @Test + fun testKeyboardBacklight_userActivity() { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) + + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data store", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + + testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000) + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be turned off after inactivity", + 0, + lightColorMap[LIGHT_ID] + ) + } + + @Test + fun testKeyboardBacklight_displayOnOff() { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) + + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */) + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data " + + "store when display turned on", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + + keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */) + assertEquals( + "Keyboard backlight level should be turned off after display is turned off", + 0, + lightColorMap[LIGHT_ID] + ) + } + inner class KeyboardBacklightListener : IKeyboardBacklightListener.Stub() { override fun onBrightnessChanged( deviceId: Int, @@ -378,6 +408,18 @@ class KeyboardBacklightControllerTests { } } + private fun incrementKeyboardBacklight(deviceId: Int) { + keyboardBacklightController.incrementKeyboardBacklight(deviceId) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchAll() + } + + private fun decrementKeyboardBacklight(deviceId: Int) { + keyboardBacklightController.decrementKeyboardBacklight(deviceId) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchAll() + } + class KeyboardBacklightState( val deviceId: Int, val brightnessLevel: Int, |