diff options
6 files changed, 376 insertions, 28 deletions
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c3bca6f9bead..55d80ac065c3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4034,9 +4034,28 @@ </array> <!-- See DisplayWhiteBalanceController. - The ambient brightness threshold (in lux) beneath which we fall back to a fixed ambient - color temperature. --> - <item name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" format="float" type="dimen">10.0</item> + A float array containing a list of ambient brightnesses, in Lux. This array, + together with config_displayWhiteBalanceLowLightAmbientBiases, is used to generate a + lookup table used in DisplayWhiteBalanceController. This lookup table is used to map + ambient brightness readings to a bias, where the bias is used to linearly interpolate + between ambient color temperature and + config_displayWhiteBalanceLowLightAmbientColorTemperature. + This table is optional. If used, this array must, + 1) Contain at least two entries + 2) Be the same length as config_displayWhiteBalanceLowLightAmbientBiases. --> + <array name ="config_displayWhiteBalanceLowLightAmbientBrightnesses"> + <item>10.0</item> + <item>10.0</item> + </array> + + <!-- See DisplayWhiteBalanceController. + An array containing a list of biases. See + config_displayWhiteBalanceLowLightAmbientBrightnesses for additional details. + This array must be in the range of [0.0, 1.0]. --> + <array name ="config_displayWhiteBalanceLowLightAmbientBiases"> + <item>0.0</item> + <item>1.0</item> + </array> <!-- See DisplayWhiteBalanceController. The ambient color temperature (in cct) to which we fall back when the ambient brightness diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ff9e379d4c4d..63d9eba45b8a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3750,7 +3750,8 @@ <java-symbol type="array" name="config_displayWhiteBalanceBaseThresholds" /> <java-symbol type="array" name="config_displayWhiteBalanceIncreaseThresholds" /> <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" /> - <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" /> + <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBrightnesses" /> + <java-symbol type="array" name="config_displayWhiteBalanceLowLightAmbientBiases" /> <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" /> <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" /> <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" /> diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java index 532bbed9588e..123cd73a3ff3 100644 --- a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java +++ b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java @@ -18,6 +18,7 @@ package com.android.server.display.whitebalance; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.utils.RollingBuffer; import java.io.PrintWriter; @@ -155,7 +156,8 @@ abstract class AmbientFilter { /** * A weighted average prioritising recent changes. */ - static class WeightedMovingAverageAmbientFilter extends AmbientFilter { + @VisibleForTesting + public static class WeightedMovingAverageAmbientFilter extends AmbientFilter { // How long the latest ambient value change is predicted to last. private static final int PREDICTION_TIME = 100; // Milliseconds diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index 0f86b478468f..395319d897ad 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.util.Slog; import android.util.Spline; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; @@ -53,18 +54,21 @@ public class DisplayWhiteBalanceController implements private Callbacks mCallbacks; private AmbientSensor.AmbientBrightnessSensor mBrightnessSensor; - private AmbientFilter mBrightnessFilter; + + @VisibleForTesting + AmbientFilter mBrightnessFilter; private AmbientSensor.AmbientColorTemperatureSensor mColorTemperatureSensor; - private AmbientFilter mColorTemperatureFilter; + + @VisibleForTesting + AmbientFilter mColorTemperatureFilter; private DisplayWhiteBalanceThrottler mThrottler; - // When the brightness drops below a certain threshold, it affects the color temperature - // accuracy, so we fall back to a fixed ambient color temperature. - private final float mLowLightAmbientBrightnessThreshold; private final float mLowLightAmbientColorTemperature; private float mAmbientColorTemperature; - private float mPendingAmbientColorTemperature; + + @VisibleForTesting + float mPendingAmbientColorTemperature; private float mLastAmbientColorTemperature; private ColorDisplayServiceInternal mColorDisplayServiceInternal; @@ -79,6 +83,9 @@ public class DisplayWhiteBalanceController implements // A piecewise linear relationship between ambient and display color temperatures. private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline; + // A piecewise linear relationship between low light brightness and low light bias. + private Spline.LinearSpline mLowLightAmbientBrightnessToBiasSpline; + /** * @param brightnessSensor * The sensor used to detect changes in the ambient brightness. @@ -119,7 +126,8 @@ public class DisplayWhiteBalanceController implements @NonNull AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor, @NonNull AmbientFilter colorTemperatureFilter, @NonNull DisplayWhiteBalanceThrottler throttler, - float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature, + float[] lowLightAmbientBrightnesses, float[] lowLightAmbientBiases, + float lowLightAmbientColorTemperature, float[] ambientColorTemperatures, float[] displayColorTemperatures) { validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, throttler); @@ -131,7 +139,6 @@ public class DisplayWhiteBalanceController implements mColorTemperatureSensor = colorTemperatureSensor; mColorTemperatureFilter = colorTemperatureFilter; mThrottler = throttler; - mLowLightAmbientBrightnessThreshold = lowLightAmbientBrightnessThreshold; mLowLightAmbientColorTemperature = lowLightAmbientColorTemperature; mAmbientColorTemperature = -1.0f; mPendingAmbientColorTemperature = -1.0f; @@ -140,9 +147,27 @@ public class DisplayWhiteBalanceController implements mAmbientColorTemperatureOverride = -1.0f; try { + mLowLightAmbientBrightnessToBiasSpline = new Spline.LinearSpline( + lowLightAmbientBrightnesses, lowLightAmbientBiases); + } catch (Exception e) { + Slog.e(TAG, "failed to create low light ambient brightness to bias spline.", e); + mLowLightAmbientBrightnessToBiasSpline = null; + } + if (mLowLightAmbientBrightnessToBiasSpline != null) { + if (mLowLightAmbientBrightnessToBiasSpline.interpolate(0.0f) != 0.0f || + mLowLightAmbientBrightnessToBiasSpline.interpolate(Float.POSITIVE_INFINITY) + != 1.0f) { + Slog.d(TAG, "invalid low light ambient brightness to bias spline, " + + "bias must begin at 0.0 and end at 1.0"); + mLowLightAmbientBrightnessToBiasSpline = null; + } + } + + try { mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline( ambientColorTemperatures, displayColorTemperatures); } catch (Exception e) { + Slog.e(TAG, "failed to create ambient to display color temperature spline.", e); mAmbientToDisplayColorTemperatureSpline = null; } @@ -238,8 +263,6 @@ public class DisplayWhiteBalanceController implements mColorTemperatureSensor.dump(writer); mColorTemperatureFilter.dump(writer); mThrottler.dump(writer); - writer.println(" mLowLightAmbientBrightnessThreshold=" - + mLowLightAmbientBrightnessThreshold); writer.println(" mLowLightAmbientColorTemperature=" + mLowLightAmbientColorTemperature); writer.println(" mAmbientColorTemperature=" + mAmbientColorTemperature); writer.println(" mPendingAmbientColorTemperature=" + mPendingAmbientColorTemperature); @@ -248,6 +271,8 @@ public class DisplayWhiteBalanceController implements writer.println(" mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride); writer.println(" mAmbientToDisplayColorTemperatureSpline=" + mAmbientToDisplayColorTemperatureSpline); + writer.println(" mLowLightAmbientBrightnessToBiasSpline=" + + mLowLightAmbientBrightnessToBiasSpline); } @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks @@ -276,15 +301,13 @@ public class DisplayWhiteBalanceController implements mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature); } - final float ambientBrightness = mBrightnessFilter.getEstimate(time); - if (ambientBrightness < mLowLightAmbientBrightnessThreshold) { - if (mLoggingEnabled) { - Slog.d(TAG, "low light ambient brightness: " + ambientBrightness + " < " - + mLowLightAmbientBrightnessThreshold - + ", falling back to fixed ambient color temperature: " - + ambientColorTemperature + " => " + mLowLightAmbientColorTemperature); - } - ambientColorTemperature = mLowLightAmbientColorTemperature; + float ambientBrightness = mBrightnessFilter.getEstimate(time); + + if (mLowLightAmbientBrightnessToBiasSpline != null) { + float bias = mLowLightAmbientBrightnessToBiasSpline.interpolate(ambientBrightness); + ambientColorTemperature = + bias * ambientColorTemperature + (1.0f - bias) + * mLowLightAmbientColorTemperature; } if (mAmbientColorTemperatureOverride != -1.0f) { diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index 6ff2b09988e6..b1b465e3b359 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -63,9 +63,12 @@ public class DisplayWhiteBalanceFactory { createColorTemperatureSensor(handler, sensorManager, resources); final AmbientFilter colorTemperatureFilter = createColorTemperatureFilter(resources); final DisplayWhiteBalanceThrottler throttler = createThrottler(resources); - final float lowLightAmbientBrightnessThreshold = getFloat(resources, - com.android.internal.R.dimen - .config_displayWhiteBalanceLowLightAmbientBrightnessThreshold); + final float[] displayWhiteBalanceLowLightAmbientBrightnesses = getFloatArray(resources, + com.android.internal.R.array + .config_displayWhiteBalanceLowLightAmbientBrightnesses); + final float[] displayWhiteBalanceLowLightAmbientBiases = getFloatArray(resources, + com.android.internal.R.array + .config_displayWhiteBalanceLowLightAmbientBiases); final float lowLightAmbientColorTemperature = getFloat(resources, com.android.internal.R.dimen .config_displayWhiteBalanceLowLightAmbientColorTemperature); @@ -75,7 +78,8 @@ public class DisplayWhiteBalanceFactory { com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures); final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController( brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter, - throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature, + throttler, displayWhiteBalanceLowLightAmbientBrightnesses, + displayWhiteBalanceLowLightAmbientBiases, lowLightAmbientColorTemperature, ambientColorTemperatures, displayColorTempeartures); brightnessSensor.setCallbacks(controller); colorTemperatureSensor.setCallbacks(controller); diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java new file mode 100644 index 000000000000..de68036d2d4c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2019 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.display.whitebalance; + +import com.android.internal.R; +import com.google.common.collect.ImmutableList; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.mockito.Matchers.anyLong; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; + +import android.content.ContextWrapper; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Handler; +import android.os.Looper; +import android.util.TypedValue; + +import androidx.test.InstrumentationRegistry; + +import org.junit.Before; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; + +@RunWith(JUnit4.class) +public final class AmbientLuxTest { + private static final int AMBIENT_COLOR_TYPE = 20705; + private static final String AMBIENT_COLOR_TYPE_STR = "colorSensoryDensoryDoc"; + private static final float LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE = 5700; + + private Handler mHandler = new Handler(Looper.getMainLooper()); + private Sensor mLightSensor; + private Sensor mAmbientColorSensor; + private ContextWrapper mContextSpy; + private Resources mResourcesSpy; + + @Mock private SensorManager mSensorManagerMock; + + @Mock private TypedArray mBrightnesses; + @Mock private TypedArray mBiases; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mLightSensor = createSensor(Sensor.TYPE_LIGHT, null); + mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR); + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + mResourcesSpy = spy(mContextSpy.getResources()); + when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + when(mSensorManagerMock.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn(mLightSensor); + final List<Sensor> sensorList = ImmutableList.of(mLightSensor, mAmbientColorSensor); + when(mSensorManagerMock.getSensorList(Sensor.TYPE_ALL)).thenReturn(sensorList); + when(mResourcesSpy.getString( + R.string.config_displayWhiteBalanceColorTemperatureSensorName)) + .thenReturn(AMBIENT_COLOR_TYPE_STR); + when(mResourcesSpy.getInteger( + R.integer.config_displayWhiteBalanceDecreaseDebounce)) + .thenReturn(0); + when(mResourcesSpy.getInteger( + R.integer.config_displayWhiteBalanceIncreaseDebounce)) + .thenReturn(0); + when(mResourcesSpy.getFloat( + R.dimen.config_displayWhiteBalanceLowLightAmbientColorTemperature)) + .thenReturn(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceAmbientColorTemperatures)) + .thenReturn(createTypedArray()); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceDisplayColorTemperatures)) + .thenReturn(createTypedArray()); + + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceLowLightAmbientBrightnesses)) + .thenReturn(mBrightnesses); + when(mResourcesSpy.obtainTypedArray( + R.array.config_displayWhiteBalanceLowLightAmbientBiases)) + .thenReturn(mBiases); + } + + @Test + public void testNoSpline() throws Exception { + setBrightnesses(); + setBiases(); + + DisplayWhiteBalanceController controller = + DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy); + final float ambientColorTemperature = 8000.0f; + setEstimatedColorTemperature(controller, ambientColorTemperature); + controller.mBrightnessFilter = spy(controller.mBrightnessFilter); + + for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) { + setEstimatedBrightnessAndUpdate(controller, luxOverride); + assertEquals(controller.mPendingAmbientColorTemperature, + ambientColorTemperature, 0.001); + } + } + + @Test + public void testSpline_OneSegment() throws Exception { + final float lowerBrightness = 10.0f; + final float upperBrightness = 50.0f; + setBrightnesses(lowerBrightness, upperBrightness); + setBiases(0.0f, 1.0f); + + DisplayWhiteBalanceController controller = + DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy); + final float ambientColorTemperature = 8000.0f; + setEstimatedColorTemperature(controller, ambientColorTemperature); + controller.mBrightnessFilter = spy(controller.mBrightnessFilter); + + for (float t = 0.0f; t <= 1.0f; t += 0.1f) { + setEstimatedBrightnessAndUpdate(controller, + mix(lowerBrightness, upperBrightness, t)); + assertEquals(controller.mPendingAmbientColorTemperature, + mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, t), 0.001); + } + + setEstimatedBrightnessAndUpdate(controller, 0.0f); + assertEquals(controller.mPendingAmbientColorTemperature, + LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001); + + setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f); + assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001); + } + + @Test + public void testSpline_TwoSegments() throws Exception { + final float brightness0 = 10.0f; + final float brightness1 = 50.0f; + final float brightness2 = 60.0f; + setBrightnesses(brightness0, brightness1, brightness2); + final float bias0 = 0.0f; + final float bias1 = 0.25f; + final float bias2 = 1.0f; + setBiases(bias0, bias1, bias2); + + DisplayWhiteBalanceController controller = + DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy); + final float ambientColorTemperature = 8000.0f; + setEstimatedColorTemperature(controller, ambientColorTemperature); + controller.mBrightnessFilter = spy(controller.mBrightnessFilter); + + for (float t = 0.0f; t <= 1.0f; t += 0.1f) { + float luxOverride = mix(brightness0, brightness1, t); + setEstimatedBrightnessAndUpdate(controller, luxOverride); + float bias = mix(bias0, bias1, t); + assertEquals(controller.mPendingAmbientColorTemperature, + mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, bias), 0.001); + } + + for (float t = 0.0f; t <= 1.0f; t += 0.1f) { + float luxOverride = mix(brightness1, brightness2, t); + setEstimatedBrightnessAndUpdate(controller, luxOverride); + float bias = mix(bias1, bias2, t); + assertEquals(controller.mPendingAmbientColorTemperature, + mix(LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, ambientColorTemperature, bias), 0.001); + } + + setEstimatedBrightnessAndUpdate(controller, 0.0f); + assertEquals(controller.mPendingAmbientColorTemperature, + LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001); + + setEstimatedBrightnessAndUpdate(controller, brightness2 + 1.0f); + assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001); + } + + @Test + public void testSpline_VerticalSegment() throws Exception { + final float lowerBrightness = 10.0f; + final float upperBrightness = 10.0f; + setBrightnesses(lowerBrightness, upperBrightness); + setBiases(0.0f, 1.0f); + + DisplayWhiteBalanceController controller = + DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy); + final float ambientColorTemperature = 8000.0f; + setEstimatedColorTemperature(controller, ambientColorTemperature); + controller.mBrightnessFilter = spy(controller.mBrightnessFilter); + + setEstimatedBrightnessAndUpdate(controller, 0.0f); + assertEquals(controller.mPendingAmbientColorTemperature, + LOW_LIGHT_AMBIENT_COLOR_TEMPERATURE, 0.001); + + setEstimatedBrightnessAndUpdate(controller, upperBrightness + 1.0f); + assertEquals(controller.mPendingAmbientColorTemperature, ambientColorTemperature, 0.001); + } + + @Test + public void testSpline_InvalidBiases() throws Exception { + float[][] invalidBrightnesses = + {{10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f}, {10.0f, 1000.0f}, + {10.0f, 1000.0f}, {-1.0f, 1.0f}, {-1.0f, 1.0f}}; + float[][] invalidBiases = + {{0.0f, 2.0f}, {0.0f, 0.9f}, {0.1f, 1.0f}, {-0.1f, 1.0f}, + {0.1f, 1.1f}, {0.0f, 1.0f}, {-2.0f, 1.0f}}; + for (int i = 0; i < invalidBrightnesses.length; ++i) { + setBrightnesses(invalidBrightnesses[i][0], invalidBrightnesses[i][1]); + setBiases(invalidBiases[i][0], invalidBiases[i][1]); + + DisplayWhiteBalanceController controller = + DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy); + final float ambientColorTemperature = 8000.0f; + setEstimatedColorTemperature(controller, ambientColorTemperature); + controller.mBrightnessFilter = spy(controller.mBrightnessFilter); + + for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) { + setEstimatedBrightnessAndUpdate(controller, luxOverride); + assertEquals(controller.mPendingAmbientColorTemperature, + ambientColorTemperature, 0.001); + } + } + } + + private void setEstimatedColorTemperature(DisplayWhiteBalanceController controller, + float ambientColorTemperature) { + AmbientFilter colorTemperatureFilter = spy(controller.mColorTemperatureFilter); + controller.mColorTemperatureFilter = colorTemperatureFilter; + when(colorTemperatureFilter.getEstimate(anyLong())).thenReturn(ambientColorTemperature); + } + + private void setEstimatedBrightnessAndUpdate(DisplayWhiteBalanceController controller, + float brightness) { + when(controller.mBrightnessFilter.getEstimate(anyLong())).thenReturn(brightness); + controller.updateAmbientColorTemperature(); + } + + private void setBrightnesses(float... vals) { + setFloatArrayResource(mBrightnesses, vals); + } + + private void setBiases(float... vals) { + setFloatArrayResource(mBiases, vals); + } + + private void setFloatArrayResource(TypedArray array, float[] vals) { + when(array.length()).thenReturn(vals.length); + for (int i = 0; i < vals.length; i++) { + when(array.getFloat(i, Float.NaN)).thenReturn(vals[i]); + } + } + + private void setSensorType(Sensor sensor, int type, String strType) throws Exception { + Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE); + setter.setAccessible(true); + setter.invoke(sensor, type); + if (strType != null) { + Field f = sensor.getClass().getDeclaredField("mStringType"); + f.setAccessible(true); + f.set(sensor, strType); + } + } + + private Sensor createSensor(int type, String strType) throws Exception { + Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); + constr.setAccessible(true); + Sensor sensor = constr.newInstance(); + setSensorType(sensor, type, strType); + return sensor; + } + + private TypedArray createTypedArray() throws Exception { + TypedArray mockArray = mock(TypedArray.class); + return mockArray; + } + + private static float mix(float a, float b, float t) { + return (1.0f - t) * a + t * b; + } +} |