Merge "Add handling for disabling flip to screen off feature." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6d33ff7ff4
@@ -30,6 +30,7 @@ import android.hardware.SensorEventListener;
|
|||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.PowerManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
@@ -66,7 +67,7 @@ public class FaceDownDetector implements SensorEventListener {
|
|||||||
private static final float MOVING_AVERAGE_WEIGHT = 0.5f;
|
private static final float MOVING_AVERAGE_WEIGHT = 0.5f;
|
||||||
|
|
||||||
/** DeviceConfig flag name, if {@code true}, enables Face Down features. */
|
/** DeviceConfig flag name, if {@code true}, enables Face Down features. */
|
||||||
private static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
|
static final String KEY_FEATURE_ENABLED = "enable_flip_to_screen_off";
|
||||||
|
|
||||||
/** Default value in absence of {@link DeviceConfig} override. */
|
/** Default value in absence of {@link DeviceConfig} override. */
|
||||||
private static final boolean DEFAULT_FEATURE_ENABLED = true;
|
private static final boolean DEFAULT_FEATURE_ENABLED = true;
|
||||||
@@ -139,6 +140,7 @@ public class FaceDownDetector implements SensorEventListener {
|
|||||||
new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT);
|
new ExponentialMovingAverage(MOVING_AVERAGE_WEIGHT);
|
||||||
|
|
||||||
private boolean mFaceDown = false;
|
private boolean mFaceDown = false;
|
||||||
|
private boolean mInteractive = false;
|
||||||
private boolean mActive = false;
|
private boolean mActive = false;
|
||||||
|
|
||||||
private float mPrevAcceleration = 0;
|
private float mPrevAcceleration = 0;
|
||||||
@@ -149,62 +151,64 @@ public class FaceDownDetector implements SensorEventListener {
|
|||||||
|
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final Runnable mUserActivityRunnable;
|
private final Runnable mUserActivityRunnable;
|
||||||
|
private final BroadcastReceiver mScreenReceiver;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
public FaceDownDetector(@NonNull Consumer<Boolean> onFlip) {
|
public FaceDownDetector(@NonNull Consumer<Boolean> onFlip) {
|
||||||
mOnFlip = Objects.requireNonNull(onFlip);
|
mOnFlip = Objects.requireNonNull(onFlip);
|
||||||
mHandler = new Handler(Looper.getMainLooper());
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
mScreenReceiver = new ScreenStateReceiver();
|
||||||
mUserActivityRunnable = () -> {
|
mUserActivityRunnable = () -> {
|
||||||
if (mFaceDown) {
|
if (mFaceDown) {
|
||||||
exitFaceDown(USER_INTERACTION, SystemClock.uptimeMillis() - mLastFlipTime);
|
exitFaceDown(USER_INTERACTION, SystemClock.uptimeMillis() - mLastFlipTime);
|
||||||
checkAndUpdateActiveState(false);
|
updateActiveState();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initializes the FaceDownDetector and all necessary listeners. */
|
/** Initializes the FaceDownDetector and all necessary listeners. */
|
||||||
public void systemReady(Context context) {
|
public void systemReady(Context context) {
|
||||||
|
mContext = context;
|
||||||
mSensorManager = context.getSystemService(SensorManager.class);
|
mSensorManager = context.getSystemService(SensorManager.class);
|
||||||
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||||
readValuesFromDeviceConfig();
|
readValuesFromDeviceConfig();
|
||||||
checkAndUpdateActiveState(true);
|
|
||||||
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
|
DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_ATTENTION_MANAGER_SERVICE,
|
||||||
ActivityThread.currentApplication().getMainExecutor(),
|
ActivityThread.currentApplication().getMainExecutor(),
|
||||||
(properties) -> onDeviceConfigChange(properties.getKeyset()));
|
(properties) -> onDeviceConfigChange(properties.getKeyset()));
|
||||||
|
updateActiveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerScreenReceiver(Context context) {
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||||
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
|
||||||
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
|
||||||
context.registerReceiver(new ScreenStateReceiver(), intentFilter);
|
context.registerReceiver(mScreenReceiver, intentFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the active state of the detector. If false, we will not process accelerometer changes.
|
* Sets the active state of the detector. If false, we will not process accelerometer changes.
|
||||||
*/
|
*/
|
||||||
private void checkAndUpdateActiveState(boolean active) {
|
private void updateActiveState() {
|
||||||
if (mIsEnabled && mActive != active) {
|
|
||||||
final long currentTime = SystemClock.uptimeMillis();
|
final long currentTime = SystemClock.uptimeMillis();
|
||||||
// Don't make active if there was recently a user interaction while face down.
|
final boolean sawRecentInteraction = mPreviousResultType == USER_INTERACTION
|
||||||
if (active && mPreviousResultType == USER_INTERACTION
|
&& currentTime - mPreviousResultTime < mUserInteractionBackoffMillis;
|
||||||
&& currentTime - mPreviousResultTime < mUserInteractionBackoffMillis) {
|
final boolean shouldBeActive = mInteractive && mIsEnabled && !sawRecentInteraction;
|
||||||
return;
|
if (mActive != shouldBeActive) {
|
||||||
}
|
if (shouldBeActive) {
|
||||||
if (DEBUG) Slog.d(TAG, "Update active - " + active);
|
mSensorManager.registerListener(
|
||||||
mActive = active;
|
this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
|
||||||
if (!active) {
|
|
||||||
if (mFaceDown && mPreviousResultTime != USER_INTERACTION) {
|
|
||||||
mPreviousResultType = SCREEN_OFF_RESULT;
|
|
||||||
mPreviousResultTime = currentTime;
|
|
||||||
}
|
|
||||||
mSensorManager.unregisterListener(this);
|
|
||||||
mFaceDown = false;
|
|
||||||
mOnFlip.accept(false);
|
|
||||||
} else {
|
|
||||||
if (mPreviousResultType == SCREEN_OFF_RESULT) {
|
if (mPreviousResultType == SCREEN_OFF_RESULT) {
|
||||||
logScreenOff();
|
logScreenOff();
|
||||||
}
|
}
|
||||||
mSensorManager.registerListener(
|
} else {
|
||||||
this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
|
mSensorManager.unregisterListener(this);
|
||||||
|
mFaceDown = false;
|
||||||
|
mOnFlip.accept(false);
|
||||||
}
|
}
|
||||||
|
mActive = shouldBeActive;
|
||||||
|
if (DEBUG) Slog.d(TAG, "Update active - " + shouldBeActive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,6 +393,7 @@ public class FaceDownDetector implements SensorEventListener {
|
|||||||
case KEY_TIME_THRESHOLD_MILLIS:
|
case KEY_TIME_THRESHOLD_MILLIS:
|
||||||
case KEY_FEATURE_ENABLED:
|
case KEY_FEATURE_ENABLED:
|
||||||
readValuesFromDeviceConfig();
|
readValuesFromDeviceConfig();
|
||||||
|
updateActiveState();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
Slog.i(TAG, "Ignoring change on " + key);
|
Slog.i(TAG, "Ignoring change on " + key);
|
||||||
@@ -401,8 +406,18 @@ public class FaceDownDetector implements SensorEventListener {
|
|||||||
mZAccelerationThreshold = getZAccelerationThreshold();
|
mZAccelerationThreshold = getZAccelerationThreshold();
|
||||||
mZAccelerationThresholdLenient = mZAccelerationThreshold + 1.0f;
|
mZAccelerationThresholdLenient = mZAccelerationThreshold + 1.0f;
|
||||||
mTimeThreshold = getTimeThreshold();
|
mTimeThreshold = getTimeThreshold();
|
||||||
mIsEnabled = isEnabled();
|
|
||||||
mUserInteractionBackoffMillis = getUserInteractionBackoffMillis();
|
mUserInteractionBackoffMillis = getUserInteractionBackoffMillis();
|
||||||
|
final boolean oldEnabled = mIsEnabled;
|
||||||
|
mIsEnabled = isEnabled();
|
||||||
|
if (oldEnabled != mIsEnabled) {
|
||||||
|
if (!mIsEnabled) {
|
||||||
|
mContext.unregisterReceiver(mScreenReceiver);
|
||||||
|
mInteractive = false;
|
||||||
|
} else {
|
||||||
|
registerScreenReceiver(mContext);
|
||||||
|
mInteractive = mContext.getSystemService(PowerManager.class).isInteractive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Slog.i(TAG, "readValuesFromDeviceConfig():"
|
Slog.i(TAG, "readValuesFromDeviceConfig():"
|
||||||
+ "\nmAccelerationThreshold=" + mAccelerationThreshold
|
+ "\nmAccelerationThreshold=" + mAccelerationThreshold
|
||||||
@@ -423,9 +438,11 @@ public class FaceDownDetector implements SensorEventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
|
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
|
||||||
checkAndUpdateActiveState(false);
|
mInteractive = false;
|
||||||
|
updateActiveState();
|
||||||
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
|
} else if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
|
||||||
checkAndUpdateActiveState(true);
|
mInteractive = true;
|
||||||
|
updateActiveState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,59 +16,74 @@
|
|||||||
|
|
||||||
package com.android.server.power;
|
package com.android.server.power;
|
||||||
|
|
||||||
|
import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
|
||||||
|
|
||||||
|
import static com.android.server.power.FaceDownDetector.KEY_FEATURE_ENABLED;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
|
||||||
import android.hardware.Sensor;
|
import android.hardware.Sensor;
|
||||||
import android.hardware.SensorEvent;
|
import android.hardware.SensorEvent;
|
||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
import android.testing.TestableContext;
|
import android.testing.TestableContext;
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
||||||
import com.android.server.display.TestUtils;
|
import com.android.server.testables.TestableDeviceConfig;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class FaceDownDetectorTest {
|
public class FaceDownDetectorTest {
|
||||||
@ClassRule
|
@ClassRule
|
||||||
public static final TestableContext sContext = new TestableContext(
|
public static final TestableContext sContext = new TestableContext(
|
||||||
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
|
InstrumentationRegistry.getInstrumentation().getTargetContext(), null);
|
||||||
|
@Rule
|
||||||
|
public TestableDeviceConfig.TestableDeviceConfigRule
|
||||||
|
mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
|
||||||
|
|
||||||
private final FaceDownDetector mFaceDownDetector =
|
private final FaceDownDetector mFaceDownDetector = new FaceDownDetector(this::onFlip);
|
||||||
new FaceDownDetector(this::onFlip);
|
|
||||||
|
|
||||||
@Mock private SensorManager mSensorManager;
|
@Mock private SensorManager mSensorManager;
|
||||||
|
@Mock private PowerManager mPowerManager;
|
||||||
|
|
||||||
private long mCurrentTime;
|
private Duration mCurrentTime;
|
||||||
private int mOnFaceDownCalls = 0;
|
private int mOnFaceDownCalls;
|
||||||
private int mOnFaceDownExitCalls = 0;
|
private int mOnFaceDownExitCalls;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
sContext.addMockSystemService(SensorManager.class, mSensorManager);
|
sContext.addMockSystemService(SensorManager.class, mSensorManager);
|
||||||
mCurrentTime = 0;
|
sContext.addMockSystemService(PowerManager.class, mPowerManager);
|
||||||
|
doReturn(true).when(mPowerManager).isInteractive();
|
||||||
|
DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
|
||||||
|
KEY_FEATURE_ENABLED, "true", false);
|
||||||
|
mCurrentTime = Duration.ZERO;
|
||||||
|
mOnFaceDownCalls = 0;
|
||||||
|
mOnFaceDownExitCalls = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void faceDownFor2Seconds_triggersFaceDown() throws Exception {
|
public void faceDownFor2Seconds_triggersFaceDown() throws Exception {
|
||||||
mFaceDownDetector.systemReady(sContext);
|
mFaceDownDetector.systemReady(sContext);
|
||||||
|
|
||||||
// Face up
|
triggerFaceDown();
|
||||||
// Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
|
|
||||||
mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
|
|
||||||
|
|
||||||
for (int i = 0; i < 200; i++) {
|
|
||||||
advanceTime(Duration.ofMillis(20));
|
|
||||||
mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
assertThat(mOnFaceDownCalls).isEqualTo(1);
|
assertThat(mOnFaceDownCalls).isEqualTo(1);
|
||||||
assertThat(mOnFaceDownExitCalls).isEqualTo(0);
|
assertThat(mOnFaceDownExitCalls).isEqualTo(0);
|
||||||
@@ -111,15 +126,7 @@ public class FaceDownDetectorTest {
|
|||||||
public void faceDownFor2Seconds_followedByFaceUp_triggersFaceDownExit() throws Exception {
|
public void faceDownFor2Seconds_followedByFaceUp_triggersFaceDownExit() throws Exception {
|
||||||
mFaceDownDetector.systemReady(sContext);
|
mFaceDownDetector.systemReady(sContext);
|
||||||
|
|
||||||
// Face up
|
triggerFaceDown();
|
||||||
// Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
|
|
||||||
mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
|
|
||||||
|
|
||||||
// Trigger face down
|
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
advanceTime(Duration.ofMillis(20));
|
|
||||||
mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phone flips
|
// Phone flips
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
@@ -131,8 +138,71 @@ public class FaceDownDetectorTest {
|
|||||||
assertThat(mOnFaceDownExitCalls).isEqualTo(1);
|
assertThat(mOnFaceDownExitCalls).isEqualTo(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notInteractive_doesNotTriggerFaceDown() throws Exception {
|
||||||
|
doReturn(false).when(mPowerManager).isInteractive();
|
||||||
|
mFaceDownDetector.systemReady(sContext);
|
||||||
|
|
||||||
|
triggerFaceDown();
|
||||||
|
|
||||||
|
assertThat(mOnFaceDownCalls).isEqualTo(0);
|
||||||
|
assertThat(mOnFaceDownExitCalls).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void afterDisablingFeature_doesNotTriggerFaceDown() throws Exception {
|
||||||
|
mFaceDownDetector.systemReady(sContext);
|
||||||
|
setEnabled(false);
|
||||||
|
|
||||||
|
triggerFaceDown();
|
||||||
|
|
||||||
|
assertThat(mOnFaceDownCalls).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void afterReenablingWhileNonInteractive_doesNotTriggerFaceDown() throws Exception {
|
||||||
|
mFaceDownDetector.systemReady(sContext);
|
||||||
|
setEnabled(false);
|
||||||
|
|
||||||
|
doReturn(false).when(mPowerManager).isInteractive();
|
||||||
|
setEnabled(true);
|
||||||
|
|
||||||
|
triggerFaceDown();
|
||||||
|
|
||||||
|
assertThat(mOnFaceDownCalls).isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void afterReenablingWhileInteractive_doesTriggerFaceDown() throws Exception {
|
||||||
|
mFaceDownDetector.systemReady(sContext);
|
||||||
|
setEnabled(false);
|
||||||
|
|
||||||
|
setEnabled(true);
|
||||||
|
|
||||||
|
triggerFaceDown();
|
||||||
|
|
||||||
|
assertThat(mOnFaceDownCalls).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerFaceDown() throws Exception {
|
||||||
|
// Face up
|
||||||
|
// Using 0.5 on x to simulate constant acceleration, such as a sloped surface.
|
||||||
|
mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, 10.0f));
|
||||||
|
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
advanceTime(Duration.ofMillis(20));
|
||||||
|
mFaceDownDetector.onSensorChanged(createTestEvent(0.5f, 0.0f, -10.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setEnabled(Boolean enabled) throws Exception {
|
||||||
|
DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
|
||||||
|
KEY_FEATURE_ENABLED, enabled.toString(), false);
|
||||||
|
waitForListenerToHandle();
|
||||||
|
}
|
||||||
|
|
||||||
private void advanceTime(Duration duration) {
|
private void advanceTime(Duration duration) {
|
||||||
mCurrentTime += duration.toNanos();
|
mCurrentTime = mCurrentTime.plus(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,12 +216,11 @@ public class FaceDownDetectorTest {
|
|||||||
SensorEvent.class.getDeclaredConstructor(int.class);
|
SensorEvent.class.getDeclaredConstructor(int.class);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
final SensorEvent event = constructor.newInstance(3);
|
final SensorEvent event = constructor.newInstance(3);
|
||||||
event.sensor =
|
event.sensor = createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
|
||||||
TestUtils.createSensor(Sensor.TYPE_ACCELEROMETER, Sensor.STRING_TYPE_ACCELEROMETER);
|
|
||||||
event.values[0] = x;
|
event.values[0] = x;
|
||||||
event.values[1] = y;
|
event.values[1] = y;
|
||||||
event.values[2] = gravity;
|
event.values[2] = gravity;
|
||||||
event.timestamp = mCurrentTime;
|
event.timestamp = mCurrentTime.toNanos();
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,4 +231,25 @@ public class FaceDownDetectorTest {
|
|||||||
mOnFaceDownExitCalls++;
|
mOnFaceDownExitCalls++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Sensor createSensor(int type, String strType) throws Exception {
|
||||||
|
Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
|
||||||
|
constr.setAccessible(true);
|
||||||
|
Sensor sensor = constr.newInstance();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return sensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void waitForListenerToHandle() throws Exception {
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
sContext.getMainExecutor().execute(latch::countDown);
|
||||||
|
assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user