Merge "Add ALS controlled keyboard backlight support" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0bec56ca04
@@ -412,6 +412,15 @@ public abstract class DisplayManagerInternal {
|
||||
@Nullable
|
||||
public abstract HostUsiVersion getHostUsiVersion(int displayId);
|
||||
|
||||
/**
|
||||
* Get the ALS data for a particular display.
|
||||
*
|
||||
* @param displayId The id of the display.
|
||||
* @return {@link AmbientLightSensorData}
|
||||
*/
|
||||
@Nullable
|
||||
public abstract AmbientLightSensorData getAmbientLightSensorData(int displayId);
|
||||
|
||||
/**
|
||||
* Get all available DisplayGroupIds.
|
||||
*/
|
||||
@@ -669,4 +678,23 @@ public abstract class DisplayManagerInternal {
|
||||
return "RefreshRateLimitation(" + type + ": " + range + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to provide Ambient sensor data using the API
|
||||
* {@link DisplayManagerInternal#getAmbientLightSensorData(int)}
|
||||
*/
|
||||
public static final class AmbientLightSensorData {
|
||||
public String sensorName;
|
||||
public String sensorType;
|
||||
|
||||
public AmbientLightSensorData(String name, String type) {
|
||||
sensorName = name;
|
||||
sensorType = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AmbientLightSensorData(" + sensorName + ", " + sensorType + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1675,16 +1675,56 @@
|
||||
in the config_autoBrightnessLevels array. This array should have size one greater
|
||||
than the size of the config_autoBrightnessLevels array.
|
||||
The brightness values must be between 0 and 255 and be non-decreasing.
|
||||
|
||||
This must be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoBrightnessButtonBacklightValues">
|
||||
</integer-array>
|
||||
|
||||
<!-- Array of output values for keyboard backlight corresponding to the lux values
|
||||
in the config_autoBrightnessLevels array. This array should have size one greater
|
||||
than the size of the config_autoBrightnessLevels array.
|
||||
The brightness values must be between 0 and 255 and be non-decreasing.
|
||||
<!-- Smoothing constant for Ambient keyboard backlight change. It should contain value
|
||||
in the range (0.0, 1.0] that will be used to calculate smoothed lux values using
|
||||
simple exponential smoothing. This value indicated how quickly we transition to
|
||||
the lux values provided by the Ambient light sensor.
|
||||
Simple formula for newLuxValue = (1-constant)*currLuxValue + constant*rawLuxValue, where
|
||||
rawLuxValue is the value provided by the Ambient light sensor. (e.g. value of 1.0 means we
|
||||
immediately start using the value provided by the Ambient light sensor)
|
||||
This must be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoBrightnessKeyboardBacklightValues">
|
||||
<item name="config_autoKeyboardBrightnessSmoothingConstant" format="float" type="dimen">
|
||||
1.0
|
||||
</item>
|
||||
|
||||
<!-- Array of output values for keyboard backlight corresponding to the lux values
|
||||
in the config_autoKeyboardBacklight(Increase/Decrease)LuxThreshold arrays.
|
||||
The brightness values must be between 0 and 255 and be non-decreasing.
|
||||
|
||||
This can be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoKeyboardBacklightBrightnessValues">
|
||||
<item>102</item>
|
||||
<item>153</item>
|
||||
<item>0</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- Array of threshold values for keyboard backlight corresponding to the values
|
||||
in the config_autoKeyboardBacklightBrightnessValues array.
|
||||
These lux values indicate when to move to a lower keyboard backlight value,
|
||||
as defined in config_autoKeyboardBacklightBrightnessValues array.
|
||||
|
||||
This can be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoKeyboardBacklightDecreaseLuxThreshold">
|
||||
<item>-1</item>
|
||||
<item>40</item>
|
||||
<item>150</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- Array of threshold values for keyboard backlight corresponding to the values
|
||||
in the config_autoKeyboardBacklightBrightnessValues array.
|
||||
These lux values indicate when to move to a higher keyboard backlight value,
|
||||
as defined in config_autoKeyboardBacklightBrightnessValues array.
|
||||
|
||||
This can be overridden in platform specific overlays -->
|
||||
<integer-array name="config_autoKeyboardBacklightIncreaseLuxThreshold">
|
||||
<item>55</item>
|
||||
<item>200</item>
|
||||
<item>-1</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- An array describing the screen's backlight values corresponding to the brightness
|
||||
|
||||
@@ -520,6 +520,7 @@
|
||||
<java-symbol type="dimen" name="config_viewConfigurationHandwritingSlop" />
|
||||
<java-symbol type="dimen" name="config_viewConfigurationHoverSlop" />
|
||||
<java-symbol type="dimen" name="config_ambiguousGestureMultiplier" />
|
||||
<java-symbol type="dimen" name="config_autoKeyboardBrightnessSmoothingConstant" />
|
||||
<java-symbol type="dimen" name="config_viewMinFlingVelocity" />
|
||||
<java-symbol type="dimen" name="config_viewMaxFlingVelocity" />
|
||||
<java-symbol type="dimen" name="config_viewMinRotaryEncoderFlingVelocity" />
|
||||
@@ -1888,11 +1889,13 @@
|
||||
<java-symbol type="anim" name="dream_activity_open_enter" />
|
||||
<java-symbol type="anim" name="dream_activity_close_exit" />
|
||||
<java-symbol type="array" name="config_autoBrightnessButtonBacklightValues" />
|
||||
<java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
|
||||
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
|
||||
<java-symbol type="array" name="config_autoBrightnessLcdBacklightValues_doze" />
|
||||
<java-symbol type="array" name="config_autoBrightnessLevels" />
|
||||
<java-symbol type="array" name="config_autoBrightnessLevelsIdle" />
|
||||
<java-symbol type="array" name="config_autoKeyboardBacklightBrightnessValues" />
|
||||
<java-symbol type="array" name="config_autoKeyboardBacklightDecreaseLuxThreshold" />
|
||||
<java-symbol type="array" name="config_autoKeyboardBacklightIncreaseLuxThreshold" />
|
||||
<java-symbol type="array" name="config_ambientThresholdLevels" />
|
||||
<java-symbol type="array" name="config_ambientBrighteningThresholds" />
|
||||
<java-symbol type="array" name="config_ambientDarkeningThresholds" />
|
||||
|
||||
@@ -4627,6 +4627,22 @@ public final class DisplayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmbientLightSensorData getAmbientLightSensorData(int displayId) {
|
||||
synchronized (mSyncRoot) {
|
||||
final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
|
||||
if (display == null) {
|
||||
return null;
|
||||
}
|
||||
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
|
||||
if (device == null) {
|
||||
return null;
|
||||
}
|
||||
SensorData data = device.getDisplayDeviceConfig().getAmbientLightSensor();
|
||||
return new AmbientLightSensorData(data.name, data.type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntArray getDisplayGroupIds() {
|
||||
Set<Integer> visitedIds = new ArraySet<>();
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright 2023 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.server.input;
|
||||
|
||||
import android.annotation.MainThread;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.display.utils.SensorUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A thread-safe component of {@link InputManagerService} responsible for managing the keyboard
|
||||
* backlight based on ambient light sensor.
|
||||
*/
|
||||
final class AmbientKeyboardBacklightController implements DisplayManager.DisplayListener,
|
||||
SensorEventListener {
|
||||
|
||||
private static final String TAG = "KbdBacklightController";
|
||||
|
||||
// To enable these logs, run:
|
||||
// 'adb shell setprop log.tag.KbdBacklightController DEBUG' (requires restart)
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
// Number of light sensor responses required to overcome temporal hysteresis.
|
||||
@VisibleForTesting
|
||||
public static final int HYSTERESIS_THRESHOLD = 2;
|
||||
|
||||
private static final int MSG_BRIGHTNESS_CALLBACK = 0;
|
||||
private static final int MSG_SETUP_DISPLAY_AND_SENSOR = 1;
|
||||
|
||||
private static final Object sAmbientControllerLock = new Object();
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
|
||||
@Nullable
|
||||
@GuardedBy("sAmbientControllerLock")
|
||||
private Sensor mLightSensor;
|
||||
@GuardedBy("sAmbientControllerLock")
|
||||
private String mCurrentDefaultDisplayUniqueId;
|
||||
|
||||
// List of currently registered ambient backlight listeners
|
||||
@GuardedBy("sAmbientControllerLock")
|
||||
private final List<AmbientKeyboardBacklightListener> mAmbientKeyboardBacklightListeners =
|
||||
new ArrayList<>();
|
||||
|
||||
private BrightnessStep[] mBrightnessSteps;
|
||||
private int mCurrentBrightnessStepIndex;
|
||||
private HysteresisState mHysteresisState;
|
||||
private int mHysteresisCount = 0;
|
||||
private float mSmoothingConstant;
|
||||
private int mSmoothedLux;
|
||||
private int mSmoothedLuxAtLastAdjustment;
|
||||
|
||||
private enum HysteresisState {
|
||||
// The most-recent mSmoothedLux matched mSmoothedLuxAtLastAdjustment.
|
||||
STABLE,
|
||||
// The most-recent mSmoothedLux was less than mSmoothedLuxAtLastAdjustment.
|
||||
DECREASING,
|
||||
// The most-recent mSmoothedLux was greater than mSmoothedLuxAtLastAdjustment.
|
||||
INCREASING,
|
||||
// The brightness should be adjusted immediately after the next sensor reading.
|
||||
IMMEDIATE,
|
||||
}
|
||||
|
||||
AmbientKeyboardBacklightController(Context context, Looper looper) {
|
||||
mContext = context;
|
||||
mHandler = new Handler(looper, this::handleMessage);
|
||||
initConfiguration();
|
||||
}
|
||||
|
||||
public void systemRunning() {
|
||||
mHandler.sendEmptyMessage(MSG_SETUP_DISPLAY_AND_SENSOR);
|
||||
DisplayManager displayManager = Objects.requireNonNull(
|
||||
mContext.getSystemService(DisplayManager.class));
|
||||
displayManager.registerDisplayListener(this, mHandler);
|
||||
}
|
||||
|
||||
public void registerAmbientBacklightListener(AmbientKeyboardBacklightListener listener) {
|
||||
synchronized (sAmbientControllerLock) {
|
||||
if (mAmbientKeyboardBacklightListeners.contains(listener)) {
|
||||
throw new IllegalStateException(
|
||||
"AmbientKeyboardBacklightListener was already registered, listener = "
|
||||
+ listener);
|
||||
}
|
||||
if (mAmbientKeyboardBacklightListeners.isEmpty()) {
|
||||
// Add sensor listener when we add the first ambient backlight listener.
|
||||
addSensorListener(mLightSensor);
|
||||
}
|
||||
mAmbientKeyboardBacklightListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterAmbientBacklightListener(AmbientKeyboardBacklightListener listener) {
|
||||
synchronized (sAmbientControllerLock) {
|
||||
if (!mAmbientKeyboardBacklightListeners.contains(listener)) {
|
||||
throw new IllegalStateException(
|
||||
"AmbientKeyboardBacklightListener was never registered, listener = "
|
||||
+ listener);
|
||||
}
|
||||
mAmbientKeyboardBacklightListeners.remove(listener);
|
||||
if (mAmbientKeyboardBacklightListeners.isEmpty()) {
|
||||
removeSensorListener(mLightSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendBrightnessAdjustment(int brightnessValue) {
|
||||
Message msg = Message.obtain(mHandler, MSG_BRIGHTNESS_CALLBACK, brightnessValue);
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private void handleBrightnessCallback(int brightnessValue) {
|
||||
synchronized (sAmbientControllerLock) {
|
||||
for (AmbientKeyboardBacklightListener listener : mAmbientKeyboardBacklightListeners) {
|
||||
listener.onKeyboardBacklightValueChanged(brightnessValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private void handleAmbientLuxChange(float rawLux) {
|
||||
if (rawLux < 0) {
|
||||
Slog.w(TAG, "Light sensor doesn't have valid value");
|
||||
return;
|
||||
}
|
||||
updateSmoothedLux(rawLux);
|
||||
|
||||
if (mHysteresisState != HysteresisState.IMMEDIATE
|
||||
&& mSmoothedLux == mSmoothedLuxAtLastAdjustment) {
|
||||
mHysteresisState = HysteresisState.STABLE;
|
||||
return;
|
||||
}
|
||||
|
||||
int newStepIndex = Math.max(0, mCurrentBrightnessStepIndex);
|
||||
int numSteps = mBrightnessSteps.length;
|
||||
|
||||
if (mSmoothedLux > mSmoothedLuxAtLastAdjustment) {
|
||||
if (mHysteresisState != HysteresisState.IMMEDIATE
|
||||
&& mHysteresisState != HysteresisState.INCREASING) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "ALS transitioned to brightness increasing state");
|
||||
}
|
||||
mHysteresisState = HysteresisState.INCREASING;
|
||||
mHysteresisCount = 0;
|
||||
}
|
||||
for (; newStepIndex < numSteps; newStepIndex++) {
|
||||
if (mSmoothedLux < mBrightnessSteps[newStepIndex].mIncreaseLuxThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (mSmoothedLux < mSmoothedLuxAtLastAdjustment) {
|
||||
if (mHysteresisState != HysteresisState.IMMEDIATE
|
||||
&& mHysteresisState != HysteresisState.DECREASING) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "ALS transitioned to brightness decreasing state");
|
||||
}
|
||||
mHysteresisState = HysteresisState.DECREASING;
|
||||
mHysteresisCount = 0;
|
||||
}
|
||||
for (; newStepIndex >= 0; newStepIndex--) {
|
||||
if (mSmoothedLux > mBrightnessSteps[newStepIndex].mDecreaseLuxThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mHysteresisState == HysteresisState.IMMEDIATE) {
|
||||
mCurrentBrightnessStepIndex = newStepIndex;
|
||||
mSmoothedLuxAtLastAdjustment = mSmoothedLux;
|
||||
mHysteresisState = HysteresisState.STABLE;
|
||||
mHysteresisCount = 0;
|
||||
sendBrightnessAdjustment(mBrightnessSteps[newStepIndex].mBrightnessValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newStepIndex == mCurrentBrightnessStepIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHysteresisCount++;
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Incremented hysteresis count to " + mHysteresisCount + " (lux went from "
|
||||
+ mSmoothedLuxAtLastAdjustment + " to " + mSmoothedLux + ")");
|
||||
}
|
||||
if (mHysteresisCount >= HYSTERESIS_THRESHOLD) {
|
||||
mCurrentBrightnessStepIndex = newStepIndex;
|
||||
mSmoothedLuxAtLastAdjustment = mSmoothedLux;
|
||||
mHysteresisCount = 1;
|
||||
sendBrightnessAdjustment(mBrightnessSteps[newStepIndex].mBrightnessValue);
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private void handleDisplayChange() {
|
||||
DisplayManagerInternal displayManagerInternal = LocalServices.getService(
|
||||
DisplayManagerInternal.class);
|
||||
DisplayInfo displayInfo = displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY);
|
||||
synchronized (sAmbientControllerLock) {
|
||||
if (Objects.equals(mCurrentDefaultDisplayUniqueId, displayInfo.uniqueId)) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Default display changed: resetting the light sensor");
|
||||
}
|
||||
// Keep track of current default display
|
||||
mCurrentDefaultDisplayUniqueId = displayInfo.uniqueId;
|
||||
// Clear all existing sensor listeners
|
||||
if (!mAmbientKeyboardBacklightListeners.isEmpty()) {
|
||||
removeSensorListener(mLightSensor);
|
||||
}
|
||||
mLightSensor = getAmbientLightSensor(
|
||||
displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY));
|
||||
// Re-add sensor listeners if required;
|
||||
if (!mAmbientKeyboardBacklightListeners.isEmpty()) {
|
||||
addSensorListener(mLightSensor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Sensor getAmbientLightSensor(
|
||||
DisplayManagerInternal.AmbientLightSensorData ambientSensor) {
|
||||
SensorManager sensorManager = Objects.requireNonNull(
|
||||
mContext.getSystemService(SensorManager.class));
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Ambient Light sensor data: " + ambientSensor);
|
||||
}
|
||||
return SensorUtils.findSensor(sensorManager, ambientSensor.sensorType,
|
||||
ambientSensor.sensorName, Sensor.TYPE_LIGHT);
|
||||
}
|
||||
|
||||
private void updateSmoothedLux(float rawLux) {
|
||||
// For the first sensor reading, use raw lux value directly without smoothing.
|
||||
if (mHysteresisState == HysteresisState.IMMEDIATE) {
|
||||
mSmoothedLux = (int) rawLux;
|
||||
} else {
|
||||
mSmoothedLux =
|
||||
(int) (mSmoothingConstant * rawLux + (1 - mSmoothingConstant) * mSmoothedLux);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Current smoothed lux from ALS = " + mSmoothedLux);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void addSensorListener(@Nullable Sensor sensor) {
|
||||
SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
|
||||
if (sensorManager == null || sensor == null) {
|
||||
return;
|
||||
}
|
||||
// Reset values before registering listener
|
||||
reset();
|
||||
sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, mHandler);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Registering ALS listener");
|
||||
}
|
||||
}
|
||||
|
||||
private void removeSensorListener(@Nullable Sensor sensor) {
|
||||
SensorManager sensorManager = mContext.getSystemService(SensorManager.class);
|
||||
if (sensorManager == null || sensor == null) {
|
||||
return;
|
||||
}
|
||||
sensorManager.unregisterListener(this, sensor);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Unregistering ALS listener");
|
||||
}
|
||||
}
|
||||
|
||||
private void initConfiguration() {
|
||||
Resources res = mContext.getResources();
|
||||
int[] brightnessValueArray = res.getIntArray(
|
||||
com.android.internal.R.array.config_autoKeyboardBacklightBrightnessValues);
|
||||
int[] decreaseThresholdArray = res.getIntArray(
|
||||
com.android.internal.R.array.config_autoKeyboardBacklightDecreaseLuxThreshold);
|
||||
int[] increaseThresholdArray = res.getIntArray(
|
||||
com.android.internal.R.array.config_autoKeyboardBacklightIncreaseLuxThreshold);
|
||||
if (brightnessValueArray.length != decreaseThresholdArray.length
|
||||
|| decreaseThresholdArray.length != increaseThresholdArray.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"The config files for auto keyboard backlight brightness must contain arrays "
|
||||
+ "of equal lengths");
|
||||
}
|
||||
final int size = brightnessValueArray.length;
|
||||
mBrightnessSteps = new BrightnessStep[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
int increaseThreshold =
|
||||
increaseThresholdArray[i] < 0 ? Integer.MAX_VALUE : increaseThresholdArray[i];
|
||||
int decreaseThreshold =
|
||||
decreaseThresholdArray[i] < 0 ? Integer.MIN_VALUE : decreaseThresholdArray[i];
|
||||
mBrightnessSteps[i] = new BrightnessStep(brightnessValueArray[i], increaseThreshold,
|
||||
decreaseThreshold);
|
||||
}
|
||||
|
||||
int numSteps = mBrightnessSteps.length;
|
||||
if (numSteps == 0 || mBrightnessSteps[0].mDecreaseLuxThreshold != Integer.MIN_VALUE
|
||||
|| mBrightnessSteps[numSteps - 1].mIncreaseLuxThreshold != Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException(
|
||||
"The config files for auto keyboard backlight brightness must contain arrays "
|
||||
+ "of length > 0 and have -1 or Integer.MIN_VALUE as lower bound for "
|
||||
+ "decrease thresholds and -1 or Integer.MAX_VALUE as upper bound for "
|
||||
+ "increase thresholds");
|
||||
}
|
||||
|
||||
final TypedValue smoothingConstantValue = new TypedValue();
|
||||
res.getValue(
|
||||
com.android.internal.R.dimen.config_autoKeyboardBrightnessSmoothingConstant,
|
||||
smoothingConstantValue,
|
||||
true /*resolveRefs*/);
|
||||
mSmoothingConstant = smoothingConstantValue.getFloat();
|
||||
if (mSmoothingConstant <= 0.0 || mSmoothingConstant > 1.0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The config files for auto keyboard backlight brightness must contain "
|
||||
+ "smoothing constant in range (0.0, 1.0].");
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Brightness steps: " + Arrays.toString(mBrightnessSteps)
|
||||
+ " Smoothing constant = " + mSmoothingConstant);
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
mHysteresisState = HysteresisState.IMMEDIATE;
|
||||
mSmoothedLux = 0;
|
||||
mSmoothedLuxAtLastAdjustment = 0;
|
||||
mCurrentBrightnessStepIndex = -1;
|
||||
}
|
||||
|
||||
private boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_BRIGHTNESS_CALLBACK:
|
||||
handleBrightnessCallback((int) msg.obj);
|
||||
return true;
|
||||
case MSG_SETUP_DISPLAY_AND_SENSOR:
|
||||
handleDisplayChange();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
handleAmbientLuxChange(event.values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
handleDisplayChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayRemoved(int displayId) {
|
||||
handleDisplayChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
handleDisplayChange();
|
||||
}
|
||||
|
||||
public interface AmbientKeyboardBacklightListener {
|
||||
/**
|
||||
* @param value between [0, 255] to which keyboard backlight needs to be set according
|
||||
* to Ambient light sensor.
|
||||
*/
|
||||
void onKeyboardBacklightValueChanged(int value);
|
||||
}
|
||||
|
||||
private static class BrightnessStep {
|
||||
private final int mBrightnessValue;
|
||||
private final int mIncreaseLuxThreshold;
|
||||
private final int mDecreaseLuxThreshold;
|
||||
|
||||
private BrightnessStep(int brightnessValue, int increaseLuxThreshold,
|
||||
int decreaseLuxThreshold) {
|
||||
mBrightnessValue = brightnessValue;
|
||||
mIncreaseLuxThreshold = increaseLuxThreshold;
|
||||
mDecreaseLuxThreshold = decreaseLuxThreshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BrightnessStep{" + "mBrightnessValue=" + mBrightnessValue
|
||||
+ ", mIncreaseThreshold=" + mIncreaseLuxThreshold + ", mDecreaseThreshold="
|
||||
+ mDecreaseLuxThreshold + '}';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,14 +39,21 @@ public final class InputFeatureFlagProvider {
|
||||
InputProperties.enable_keyboard_backlight_animation().orElse(false);
|
||||
|
||||
// To disable Custom keyboard backlight levels support via IDC files run:
|
||||
// adb shell setprop persist.input.keyboard_backlight_custom_levels.enabled false (requires
|
||||
// adb shell setprop persist.input.keyboard.backlight_custom_levels.enabled false (requires
|
||||
// restart)
|
||||
private static final boolean KEYBOARD_BACKLIGHT_CUSTOM_LEVELS_ENABLED =
|
||||
InputProperties.enable_keyboard_backlight_custom_levels().orElse(true);
|
||||
|
||||
// To disable als based ambient keyboard backlight control run:
|
||||
// adb shell setprop persist.input.keyboard.ambient_backlight_control.enabled false (requires
|
||||
// restart)
|
||||
private static final boolean AMBIENT_KEYBOARD_BACKLIGHT_CONTROL_ENABLED =
|
||||
InputProperties.enable_ambient_keyboard_backlight_control().orElse(true);
|
||||
|
||||
private static Optional<Boolean> sKeyboardBacklightControlOverride = Optional.empty();
|
||||
private static Optional<Boolean> sKeyboardBacklightAnimationOverride = Optional.empty();
|
||||
private static Optional<Boolean> sKeyboardBacklightCustomLevelsOverride = Optional.empty();
|
||||
private static Optional<Boolean> sAmbientKeyboardBacklightControlOverride = Optional.empty();
|
||||
|
||||
public static boolean isKeyboardBacklightControlEnabled() {
|
||||
return sKeyboardBacklightControlOverride.orElse(KEYBOARD_BACKLIGHT_CONTROL_ENABLED);
|
||||
@@ -61,6 +68,11 @@ public final class InputFeatureFlagProvider {
|
||||
KEYBOARD_BACKLIGHT_CUSTOM_LEVELS_ENABLED);
|
||||
}
|
||||
|
||||
public static boolean isAmbientKeyboardBacklightControlEnabled() {
|
||||
return sAmbientKeyboardBacklightControlOverride.orElse(
|
||||
AMBIENT_KEYBOARD_BACKLIGHT_CONTROL_ENABLED);
|
||||
}
|
||||
|
||||
public static void setKeyboardBacklightControlEnabled(boolean enabled) {
|
||||
sKeyboardBacklightControlOverride = Optional.of(enabled);
|
||||
}
|
||||
@@ -73,6 +85,10 @@ public final class InputFeatureFlagProvider {
|
||||
sKeyboardBacklightCustomLevelsOverride = Optional.of(enabled);
|
||||
}
|
||||
|
||||
public static void setAmbientKeyboardBacklightControlEnabled(boolean enabled) {
|
||||
sAmbientKeyboardBacklightControlOverride = Optional.of(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all input feature flag overrides.
|
||||
*/
|
||||
@@ -80,5 +96,6 @@ public final class InputFeatureFlagProvider {
|
||||
sKeyboardBacklightControlOverride = Optional.empty();
|
||||
sKeyboardBacklightAnimationOverride = Optional.empty();
|
||||
sKeyboardBacklightCustomLevelsOverride = Optional.empty();
|
||||
sAmbientKeyboardBacklightControlOverride = Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.input;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.BinderThread;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.hardware.input.IKeyboardBacklightListener;
|
||||
@@ -105,6 +106,12 @@ final class KeyboardBacklightController implements
|
||||
private final SparseArray<KeyboardBacklightListenerRecord> mKeyboardBacklightListenerRecords =
|
||||
new SparseArray<>();
|
||||
|
||||
private final AmbientKeyboardBacklightController mAmbientController;
|
||||
@Nullable
|
||||
private AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener mAmbientListener;
|
||||
|
||||
private int mAmbientBacklightValue = 0;
|
||||
|
||||
static {
|
||||
// Fixed brightness levels to avoid issues when converting back and forth from the
|
||||
// device brightness range to [0-255]
|
||||
@@ -128,6 +135,7 @@ final class KeyboardBacklightController implements
|
||||
mDataStore = dataStore;
|
||||
mHandler = new Handler(looper, this::handleMessage);
|
||||
mAnimatorFactory = animatorFactory;
|
||||
mAmbientController = new AmbientKeyboardBacklightController(context, looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,6 +159,11 @@ final class KeyboardBacklightController implements
|
||||
}
|
||||
};
|
||||
observer.startObserving(UEVENT_KEYBOARD_BACKLIGHT_TAG);
|
||||
|
||||
if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) {
|
||||
// Start ambient backlight controller
|
||||
mAmbientController.systemRunning();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -183,9 +196,21 @@ final class KeyboardBacklightController implements
|
||||
if (inputDevice == null || state == null) {
|
||||
return;
|
||||
}
|
||||
Light keyboardBacklight = state.mLight;
|
||||
// Follow preset levels of brightness defined in BRIGHTNESS_LEVELS
|
||||
final int currBrightnessLevel = state.mBrightnessLevel;
|
||||
final int currBrightnessLevel;
|
||||
if (state.mUseAmbientController) {
|
||||
int index = Arrays.binarySearch(state.mBrightnessValueForLevel, mAmbientBacklightValue);
|
||||
// Set current level to the lower bound of the ambient value in the brightness array.
|
||||
if (index < 0) {
|
||||
int lowerBound = Math.max(0, -(index + 1) - 1);
|
||||
currBrightnessLevel =
|
||||
direction == Direction.DIRECTION_UP ? lowerBound : lowerBound + 1;
|
||||
} else {
|
||||
currBrightnessLevel = index;
|
||||
}
|
||||
} else {
|
||||
currBrightnessLevel = state.mBrightnessLevel;
|
||||
}
|
||||
final int newBrightnessLevel;
|
||||
if (direction == Direction.DIRECTION_UP) {
|
||||
newBrightnessLevel = Math.min(currBrightnessLevel + 1,
|
||||
@@ -193,33 +218,67 @@ final class KeyboardBacklightController implements
|
||||
} else {
|
||||
newBrightnessLevel = Math.max(currBrightnessLevel - 1, 0);
|
||||
}
|
||||
updateBacklightState(deviceId, newBrightnessLevel, true /* isTriggeredByKeyPress */);
|
||||
|
||||
state.setBrightnessLevel(newBrightnessLevel);
|
||||
|
||||
// Might need to stop listening to ALS since user has manually selected backlight
|
||||
// level through keyboard up/down button
|
||||
updateAmbientLightListener();
|
||||
|
||||
maybeBackupBacklightBrightness(inputDevice, state.mLight,
|
||||
state.mBrightnessValueForLevel[newBrightnessLevel]);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"Changing state from " + state.mBrightnessLevel + " to " + newBrightnessLevel);
|
||||
}
|
||||
|
||||
synchronized (mKeyboardBacklightListenerRecords) {
|
||||
for (int i = 0; i < mKeyboardBacklightListenerRecords.size(); i++) {
|
||||
IKeyboardBacklightState callbackState = new IKeyboardBacklightState();
|
||||
callbackState.brightnessLevel = newBrightnessLevel;
|
||||
callbackState.maxBrightnessLevel = state.getNumBrightnessChangeSteps();
|
||||
mKeyboardBacklightListenerRecords.valueAt(i).notifyKeyboardBacklightChanged(
|
||||
deviceId, callbackState, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeBackupBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight,
|
||||
int brightnessValue) {
|
||||
// Don't back up or restore when ALS based keyboard backlight is enabled
|
||||
if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) {
|
||||
return;
|
||||
}
|
||||
synchronized (mDataStore) {
|
||||
try {
|
||||
mDataStore.setKeyboardBacklightBrightness(inputDevice.getDescriptor(),
|
||||
keyboardBacklight.getId(),
|
||||
state.mBrightnessValueForLevel[newBrightnessLevel]);
|
||||
brightnessValue);
|
||||
} finally {
|
||||
mDataStore.saveIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight) {
|
||||
private void maybeRestoreBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight) {
|
||||
// Don't back up or restore when ALS based keyboard backlight is enabled
|
||||
if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) {
|
||||
return;
|
||||
}
|
||||
KeyboardBacklightState state = mKeyboardBacklights.get(inputDevice.getId());
|
||||
OptionalInt brightness;
|
||||
synchronized (mDataStore) {
|
||||
brightness = mDataStore.getKeyboardBacklightBrightness(
|
||||
inputDevice.getDescriptor(), keyboardBacklight.getId());
|
||||
}
|
||||
if (brightness.isPresent()) {
|
||||
if (state != null && brightness.isPresent()) {
|
||||
int brightnessValue = Math.max(0, Math.min(MAX_BRIGHTNESS, brightness.getAsInt()));
|
||||
int index = Arrays.binarySearch(state.mBrightnessValueForLevel, brightnessValue);
|
||||
if (index < 0) {
|
||||
index = Math.min(state.getNumBrightnessChangeSteps(), -(index + 1));
|
||||
int newLevel = Arrays.binarySearch(state.mBrightnessValueForLevel, brightnessValue);
|
||||
if (newLevel < 0) {
|
||||
newLevel = Math.min(state.getNumBrightnessChangeSteps(), -(newLevel + 1));
|
||||
}
|
||||
updateBacklightState(inputDevice.getId(), index, false /* isTriggeredByKeyPress */);
|
||||
state.setBrightnessLevel(newLevel);
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Restoring brightness level " + brightness.getAsInt());
|
||||
}
|
||||
@@ -260,6 +319,16 @@ final class KeyboardBacklightController implements
|
||||
} else {
|
||||
handleUserInactivity();
|
||||
}
|
||||
updateAmbientLightListener();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void handleAmbientLightValueChanged(int brightnessValue) {
|
||||
mAmbientBacklightValue = brightnessValue;
|
||||
for (int i = 0; i < mKeyboardBacklights.size(); i++) {
|
||||
KeyboardBacklightState state = mKeyboardBacklights.valueAt(i);
|
||||
state.onAmbientBacklightValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleMessage(Message msg) {
|
||||
@@ -292,12 +361,14 @@ final class KeyboardBacklightController implements
|
||||
@Override
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
onInputDeviceChanged(deviceId);
|
||||
updateAmbientLightListener();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void onInputDeviceRemoved(int deviceId) {
|
||||
mKeyboardBacklights.remove(deviceId);
|
||||
updateAmbientLightListener();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -318,7 +389,7 @@ final class KeyboardBacklightController implements
|
||||
}
|
||||
// The keyboard backlight was added or changed.
|
||||
mKeyboardBacklights.put(deviceId, new KeyboardBacklightState(deviceId, keyboardBacklight));
|
||||
restoreBacklightBrightness(inputDevice, keyboardBacklight);
|
||||
maybeRestoreBacklightBrightness(inputDevice, keyboardBacklight);
|
||||
}
|
||||
|
||||
private InputDevice getInputDevice(int deviceId) {
|
||||
@@ -379,30 +450,6 @@ final class KeyboardBacklightController implements
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBacklightState(int deviceId, int brightnessLevel,
|
||||
boolean isTriggeredByKeyPress) {
|
||||
KeyboardBacklightState state = mKeyboardBacklights.get(deviceId);
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.setBrightnessLevel(brightnessLevel);
|
||||
|
||||
synchronized (mKeyboardBacklightListenerRecords) {
|
||||
for (int i = 0; i < mKeyboardBacklightListenerRecords.size(); i++) {
|
||||
IKeyboardBacklightState callbackState = new IKeyboardBacklightState();
|
||||
callbackState.brightnessLevel = brightnessLevel;
|
||||
callbackState.maxBrightnessLevel = state.getNumBrightnessChangeSteps();
|
||||
mKeyboardBacklightListenerRecords.valueAt(i).notifyKeyboardBacklightChanged(
|
||||
deviceId, callbackState, isTriggeredByKeyPress);
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Changing state from " + state.mBrightnessLevel + " to " + brightnessLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private void onKeyboardBacklightListenerDied(int pid) {
|
||||
synchronized (mKeyboardBacklightListenerRecords) {
|
||||
mKeyboardBacklightListenerRecords.remove(pid);
|
||||
@@ -420,6 +467,25 @@ final class KeyboardBacklightController implements
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAmbientLightListener() {
|
||||
if (!InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) {
|
||||
return;
|
||||
}
|
||||
boolean needToListenAmbientLightSensor = false;
|
||||
for (int i = 0; i < mKeyboardBacklights.size(); i++) {
|
||||
needToListenAmbientLightSensor |= mKeyboardBacklights.valueAt(i).mUseAmbientController;
|
||||
}
|
||||
needToListenAmbientLightSensor &= mIsInteractive;
|
||||
if (needToListenAmbientLightSensor && mAmbientListener == null) {
|
||||
mAmbientListener = this::handleAmbientLightValueChanged;
|
||||
mAmbientController.registerAmbientBacklightListener(mAmbientListener);
|
||||
}
|
||||
if (!needToListenAmbientLightSensor && mAmbientListener != null) {
|
||||
mAmbientController.unregisterAmbientBacklightListener(mAmbientListener);
|
||||
mAmbientListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidBacklightNodePath(String devPath) {
|
||||
if (TextUtils.isEmpty(devPath)) {
|
||||
return false;
|
||||
@@ -485,6 +551,8 @@ final class KeyboardBacklightController implements
|
||||
private int mBrightnessLevel;
|
||||
private ValueAnimator mAnimator;
|
||||
private final int[] mBrightnessValueForLevel;
|
||||
private boolean mUseAmbientController =
|
||||
InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled();
|
||||
|
||||
KeyboardBacklightState(int deviceId, Light light) {
|
||||
mDeviceId = deviceId;
|
||||
@@ -525,15 +593,25 @@ final class KeyboardBacklightController implements
|
||||
}
|
||||
|
||||
private void onBacklightStateChanged() {
|
||||
setBacklightValue(mIsBacklightOn ? mBrightnessValueForLevel[mBrightnessLevel] : 0);
|
||||
int toValue = mUseAmbientController ? mAmbientBacklightValue
|
||||
: mBrightnessValueForLevel[mBrightnessLevel];
|
||||
setBacklightValue(mIsBacklightOn ? toValue : 0);
|
||||
}
|
||||
private void setBrightnessLevel(int brightnessLevel) {
|
||||
// Once we manually set level, disregard ambient light controller
|
||||
mUseAmbientController = false;
|
||||
if (mIsBacklightOn) {
|
||||
setBacklightValue(mBrightnessValueForLevel[brightnessLevel]);
|
||||
}
|
||||
mBrightnessLevel = brightnessLevel;
|
||||
}
|
||||
|
||||
private void onAmbientBacklightValueChanged() {
|
||||
if (mIsBacklightOn && mUseAmbientController) {
|
||||
setBacklightValue(mAmbientBacklightValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelAnimation() {
|
||||
if (mAnimator != null && mAnimator.isRunning()) {
|
||||
mAnimator.cancel();
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright 2023 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.server.input
|
||||
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.res.Resources
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import android.hardware.display.DisplayManagerInternal
|
||||
import android.hardware.input.InputSensorInfo
|
||||
import android.os.Handler
|
||||
import android.os.test.TestLooper
|
||||
import android.platform.test.annotations.Presubmit
|
||||
import android.util.TypedValue
|
||||
import android.view.Display
|
||||
import android.view.DisplayInfo
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import com.android.internal.R
|
||||
import com.android.server.LocalServices
|
||||
import com.android.server.input.AmbientKeyboardBacklightController.HYSTERESIS_THRESHOLD
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.anyBoolean
|
||||
import org.mockito.Mockito.anyInt
|
||||
import org.mockito.Mockito.eq
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
/**
|
||||
* Tests for {@link AmbientKeyboardBacklightController}.
|
||||
*
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksServicesTests:AmbientKeyboardBacklightControllerTests
|
||||
*/
|
||||
@Presubmit
|
||||
class AmbientKeyboardBacklightControllerTests {
|
||||
|
||||
companion object {
|
||||
const val DEFAULT_DISPLAY_UNIQUE_ID = "uniqueId_1"
|
||||
const val SENSOR_NAME = "test_sensor_name"
|
||||
const val SENSOR_TYPE = "test_sensor_type"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val rule = MockitoJUnit.rule()!!
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var testLooper: TestLooper
|
||||
private lateinit var ambientController: AmbientKeyboardBacklightController
|
||||
|
||||
@Mock
|
||||
private lateinit var resources: Resources
|
||||
|
||||
@Mock
|
||||
private lateinit var lightSensorInfo: InputSensorInfo
|
||||
|
||||
@Mock
|
||||
private lateinit var sensorManager: SensorManager
|
||||
|
||||
@Mock
|
||||
private lateinit var displayManagerInternal: DisplayManagerInternal
|
||||
private lateinit var lightSensor: Sensor
|
||||
|
||||
private var currentDisplayInfo = DisplayInfo()
|
||||
private var lastBrightnessCallback: Int = 0
|
||||
private var listenerRegistered: Boolean = false
|
||||
private var listenerRegistrationCount: Int = 0
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
|
||||
`when`(context.resources).thenReturn(resources)
|
||||
setupBrightnessSteps()
|
||||
setupSensor()
|
||||
testLooper = TestLooper()
|
||||
ambientController = AmbientKeyboardBacklightController(context, testLooper.looper)
|
||||
ambientController.systemRunning()
|
||||
testLooper.dispatchAll()
|
||||
}
|
||||
|
||||
private fun setupBrightnessSteps() {
|
||||
val brightnessValues = intArrayOf(100, 200, 0)
|
||||
val decreaseThresholds = intArrayOf(-1, 900, 1900)
|
||||
val increaseThresholds = intArrayOf(1000, 2000, -1)
|
||||
`when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues))
|
||||
.thenReturn(brightnessValues)
|
||||
`when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold))
|
||||
.thenReturn(decreaseThresholds)
|
||||
`when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold))
|
||||
.thenReturn(increaseThresholds)
|
||||
`when`(
|
||||
resources.getValue(
|
||||
eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant),
|
||||
any(TypedValue::class.java),
|
||||
anyBoolean()
|
||||
)
|
||||
).then {
|
||||
val args = it.arguments
|
||||
val outValue = args[1] as TypedValue
|
||||
outValue.data = java.lang.Float.floatToRawIntBits(1.0f)
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSensor() {
|
||||
LocalServices.removeServiceForTest(DisplayManagerInternal::class.java)
|
||||
LocalServices.addService(DisplayManagerInternal::class.java, displayManagerInternal)
|
||||
currentDisplayInfo.uniqueId = DEFAULT_DISPLAY_UNIQUE_ID
|
||||
`when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(
|
||||
currentDisplayInfo
|
||||
)
|
||||
val sensorData = DisplayManagerInternal.AmbientLightSensorData(SENSOR_NAME, SENSOR_TYPE)
|
||||
`when`(displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY))
|
||||
.thenReturn(sensorData)
|
||||
|
||||
`when`(lightSensorInfo.name).thenReturn(SENSOR_NAME)
|
||||
`when`(lightSensorInfo.stringType).thenReturn(SENSOR_TYPE)
|
||||
lightSensor = Sensor(lightSensorInfo)
|
||||
`when`(context.getSystemService(eq(Context.SENSOR_SERVICE))).thenReturn(sensorManager)
|
||||
`when`(sensorManager.getSensorList(anyInt())).thenReturn(listOf(lightSensor))
|
||||
`when`(
|
||||
sensorManager.registerListener(
|
||||
any(),
|
||||
eq(lightSensor),
|
||||
anyInt(),
|
||||
any(Handler::class.java)
|
||||
)
|
||||
).then {
|
||||
listenerRegistered = true
|
||||
listenerRegistrationCount++
|
||||
true
|
||||
}
|
||||
`when`(
|
||||
sensorManager.unregisterListener(
|
||||
any(SensorEventListener::class.java),
|
||||
eq(lightSensor)
|
||||
)
|
||||
).then {
|
||||
listenerRegistered = false
|
||||
Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupSensorWithInitialLux(luxValue: Float) {
|
||||
ambientController.registerAmbientBacklightListener { brightnessValue: Int ->
|
||||
lastBrightnessCallback = brightnessValue
|
||||
}
|
||||
sendAmbientLuxValue(luxValue)
|
||||
testLooper.dispatchAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInitialAmbientLux_sendsCallbackImmediately() {
|
||||
setupSensorWithInitialLux(500F)
|
||||
|
||||
assertEquals(
|
||||
"Should receive immediate callback for first lux change",
|
||||
100,
|
||||
lastBrightnessCallback
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBrightnessIncrease_afterInitialLuxChanges() {
|
||||
setupSensorWithInitialLux(500F)
|
||||
|
||||
// Current state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
|
||||
repeat(HYSTERESIS_THRESHOLD) {
|
||||
sendAmbientLuxValue(1500F)
|
||||
}
|
||||
testLooper.dispatchAll()
|
||||
|
||||
assertEquals(
|
||||
"Should receive brightness change callback for increasing lux change",
|
||||
200,
|
||||
lastBrightnessCallback
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBrightnessDecrease_afterInitialLuxChanges() {
|
||||
setupSensorWithInitialLux(1500F)
|
||||
|
||||
// Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
|
||||
repeat(HYSTERESIS_THRESHOLD) {
|
||||
sendAmbientLuxValue(500F)
|
||||
}
|
||||
testLooper.dispatchAll()
|
||||
|
||||
assertEquals(
|
||||
"Should receive brightness change callback for decreasing lux change",
|
||||
100,
|
||||
lastBrightnessCallback
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRegisterAmbientListener_throwsExceptionOnRegisteringDuplicate() {
|
||||
val ambientListener =
|
||||
AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
|
||||
ambientController.registerAmbientBacklightListener(ambientListener)
|
||||
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
ambientController.registerAmbientBacklightListener(
|
||||
ambientListener
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUnregisterAmbientListener_throwsExceptionOnUnregisteringNonExistent() {
|
||||
val ambientListener =
|
||||
AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
|
||||
assertThrows(IllegalStateException::class.java) {
|
||||
ambientController.unregisterAmbientBacklightListener(
|
||||
ambientListener
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSensorListenerRegistered_onRegisterUnregisterAmbientListener() {
|
||||
assertEquals(
|
||||
"Should not have a sensor listener registered at init",
|
||||
0,
|
||||
listenerRegistrationCount
|
||||
)
|
||||
assertFalse("Should not have a sensor listener registered at init", listenerRegistered)
|
||||
|
||||
val ambientListener1 =
|
||||
AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
|
||||
ambientController.registerAmbientBacklightListener(ambientListener1)
|
||||
assertEquals(
|
||||
"Should register a new sensor listener", 1, listenerRegistrationCount
|
||||
)
|
||||
assertTrue("Should have sensor listener registered", listenerRegistered)
|
||||
|
||||
val ambientListener2 =
|
||||
AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { }
|
||||
ambientController.registerAmbientBacklightListener(ambientListener2)
|
||||
assertEquals(
|
||||
"Should not register a new sensor listener when adding a second ambient listener",
|
||||
1,
|
||||
listenerRegistrationCount
|
||||
)
|
||||
assertTrue("Should have sensor listener registered", listenerRegistered)
|
||||
|
||||
ambientController.unregisterAmbientBacklightListener(ambientListener1)
|
||||
assertTrue("Should have sensor listener registered", listenerRegistered)
|
||||
|
||||
ambientController.unregisterAmbientBacklightListener(ambientListener2)
|
||||
assertFalse(
|
||||
"Should not have sensor listener registered if there are no ambient listeners",
|
||||
listenerRegistered
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisplayChange_shouldNotReRegisterListener_ifUniqueIdSame() {
|
||||
setupSensorWithInitialLux(0F)
|
||||
|
||||
val count = listenerRegistrationCount
|
||||
ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY)
|
||||
testLooper.dispatchAll()
|
||||
|
||||
assertEquals(
|
||||
"Should not re-register listener on display change if unique is same",
|
||||
count,
|
||||
listenerRegistrationCount
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDisplayChange_shouldReRegisterListener_ifUniqueIdChanges() {
|
||||
setupSensorWithInitialLux(0F)
|
||||
|
||||
val count = listenerRegistrationCount
|
||||
currentDisplayInfo.uniqueId = "xyz"
|
||||
ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY)
|
||||
testLooper.dispatchAll()
|
||||
|
||||
assertEquals(
|
||||
"Should re-register listener on display change if unique id changed",
|
||||
count + 1,
|
||||
listenerRegistrationCount
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBrightnessDoesntChange_betweenIncreaseAndDecreaseThresholds() {
|
||||
setupSensorWithInitialLux(1001F)
|
||||
|
||||
// Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
|
||||
// Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
|
||||
lastBrightnessCallback = -1
|
||||
repeat(HYSTERESIS_THRESHOLD) {
|
||||
sendAmbientLuxValue(999F)
|
||||
}
|
||||
testLooper.dispatchAll()
|
||||
|
||||
assertEquals(
|
||||
"Should not receive any callback for brightness change",
|
||||
-1,
|
||||
lastBrightnessCallback
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBrightnessDoesntChange_onChangeOccurringLessThanHysteresisThreshold() {
|
||||
setupSensorWithInitialLux(1001F)
|
||||
|
||||
// Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1]
|
||||
// Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900]
|
||||
lastBrightnessCallback = -1
|
||||
repeat(HYSTERESIS_THRESHOLD - 1) {
|
||||
sendAmbientLuxValue(2001F)
|
||||
}
|
||||
testLooper.dispatchAll()
|
||||
|
||||
assertEquals(
|
||||
"Should not receive any callback for brightness change",
|
||||
-1,
|
||||
lastBrightnessCallback
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendAmbientLuxValue(luxValue: Float) {
|
||||
ambientController.onSensorChanged(SensorEvent(lightSensor, 0, 0, floatArrayOf(luxValue)))
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,8 @@ import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANG
|
||||
import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -154,7 +156,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklightIncrementDecrement() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
|
||||
@@ -168,7 +174,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardWithoutBacklight() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithoutBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight)
|
||||
@@ -182,7 +192,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardWithMultipleLight() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT)
|
||||
@@ -204,7 +218,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testRestoreBacklightOnInputDeviceAdded() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
|
||||
@@ -233,7 +251,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testRestoreBacklightOnInputDeviceChanged() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
|
||||
@@ -265,7 +287,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklight_registerUnregisterListener() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1
|
||||
@@ -314,7 +340,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklight_userActivity() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
|
||||
@@ -346,7 +376,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklight_displayOnOff() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
|
||||
@@ -436,7 +470,11 @@ class KeyboardBacklightControllerTests {
|
||||
@Test
|
||||
@UiThreadTest
|
||||
fun testKeyboardBacklightAnimation_onChangeLevels() {
|
||||
KeyboardBacklightFlags(animationEnabled = true, customLevelsEnabled = false).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = true,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT)
|
||||
`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight)
|
||||
@@ -459,7 +497,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklightPreferredLevels() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = true).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = true,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
|
||||
@@ -475,7 +517,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklightPreferredLevels_moreThanMax_shouldUseDefault() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = true).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = true,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) }
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
|
||||
@@ -491,7 +537,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklightPreferredLevels_mustHaveZeroAndMaxBrightnessAsBounds() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = true).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = true,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val suggestedLevels = intArrayOf(22, 63, 135, 196)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
|
||||
@@ -508,7 +558,11 @@ class KeyboardBacklightControllerTests {
|
||||
|
||||
@Test
|
||||
fun testKeyboardBacklightPreferredLevels_dropsOutOfBoundsLevels() {
|
||||
KeyboardBacklightFlags(animationEnabled = false, customLevelsEnabled = true).use {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = true,
|
||||
ambientControlEnabled = false
|
||||
).use {
|
||||
val keyboardWithBacklight = createKeyboard(DEVICE_ID)
|
||||
val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000)
|
||||
val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT,
|
||||
@@ -523,10 +577,148 @@ class KeyboardBacklightControllerTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAmbientBacklightControl_doesntRestoreBacklightLevel() {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = true
|
||||
).use {
|
||||
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,
|
||||
DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1]
|
||||
)
|
||||
|
||||
keyboardBacklightController.onInputDeviceAdded(DEVICE_ID)
|
||||
keyboardBacklightController.notifyUserActivity()
|
||||
testLooper.dispatchNext()
|
||||
assertNull(
|
||||
"Keyboard backlight level should not be restored to the saved level",
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAmbientBacklightControl_doesntBackupBacklightLevel() {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = true
|
||||
).use {
|
||||
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)
|
||||
incrementKeyboardBacklight(DEVICE_ID)
|
||||
assertFalse(
|
||||
"Light value should not be backed up if ambient control is enabled",
|
||||
dataStore.getKeyboardBacklightBrightness(
|
||||
keyboardWithBacklight.descriptor, LIGHT_ID
|
||||
).isPresent
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAmbientBacklightControl_incrementLevel_afterAmbientChange() {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = true
|
||||
).use {
|
||||
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)
|
||||
sendAmbientBacklightValue(1)
|
||||
assertEquals(
|
||||
"Light value should be changed to ambient provided value",
|
||||
Color.argb(1, 0, 0, 0),
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
|
||||
incrementKeyboardBacklight(DEVICE_ID)
|
||||
|
||||
assertEquals(
|
||||
"Light value for level after increment post Ambient change is mismatched",
|
||||
Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0),
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAmbientBacklightControl_decrementLevel_afterAmbientChange() {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = true
|
||||
).use {
|
||||
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)
|
||||
sendAmbientBacklightValue(254)
|
||||
assertEquals(
|
||||
"Light value should be changed to ambient provided value",
|
||||
Color.argb(254, 0, 0, 0),
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
|
||||
decrementKeyboardBacklight(DEVICE_ID)
|
||||
|
||||
val numLevels = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size
|
||||
assertEquals(
|
||||
"Light value for level after decrement post Ambient change is mismatched",
|
||||
Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[numLevels - 2], 0, 0, 0),
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAmbientBacklightControl_ambientChanges_afterManualChange() {
|
||||
KeyboardBacklightFlags(
|
||||
animationEnabled = false,
|
||||
customLevelsEnabled = false,
|
||||
ambientControlEnabled = true
|
||||
).use {
|
||||
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)
|
||||
incrementKeyboardBacklight(DEVICE_ID)
|
||||
assertEquals(
|
||||
"Light value should be changed to the first level",
|
||||
Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0),
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
|
||||
sendAmbientBacklightValue(100)
|
||||
assertNotEquals(
|
||||
"Light value should not change based on ambient changes after manual changes",
|
||||
Color.argb(100, 0, 0, 0),
|
||||
lightColorMap[LIGHT_ID]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertIncrementDecrementForLevels(
|
||||
device: InputDevice,
|
||||
light: Light,
|
||||
expectedLevels: IntArray
|
||||
device: InputDevice,
|
||||
light: Light,
|
||||
expectedLevels: IntArray
|
||||
) {
|
||||
val deviceId = device.id
|
||||
val lightId = light.id
|
||||
@@ -612,6 +804,12 @@ class KeyboardBacklightControllerTests {
|
||||
testLooper.dispatchAll()
|
||||
}
|
||||
|
||||
private fun sendAmbientBacklightValue(brightnessValue: Int) {
|
||||
keyboardBacklightController.handleAmbientLightValueChanged(brightnessValue)
|
||||
keyboardBacklightController.notifyUserActivity()
|
||||
testLooper.dispatchAll()
|
||||
}
|
||||
|
||||
class KeyboardBacklightState(
|
||||
val deviceId: Int,
|
||||
val brightnessLevel: Int,
|
||||
@@ -620,12 +818,15 @@ class KeyboardBacklightControllerTests {
|
||||
)
|
||||
|
||||
private inner class KeyboardBacklightFlags constructor(
|
||||
animationEnabled: Boolean,
|
||||
customLevelsEnabled: Boolean
|
||||
animationEnabled: Boolean,
|
||||
customLevelsEnabled: Boolean,
|
||||
ambientControlEnabled: Boolean
|
||||
) : AutoCloseable {
|
||||
init {
|
||||
InputFeatureFlagProvider.setKeyboardBacklightAnimationEnabled(animationEnabled)
|
||||
InputFeatureFlagProvider.setKeyboardBacklightCustomLevelsEnabled(customLevelsEnabled)
|
||||
InputFeatureFlagProvider
|
||||
.setAmbientKeyboardBacklightControlEnabled(ambientControlEnabled)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
||||
Reference in New Issue
Block a user