mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Convert common.ShakeDetector (#45744)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/45744 Changelog: [Internal] Reviewed By: rshest Differential Revision: D60311462 fbshipit-source-id: 142649a18b365b0954dd138c895030fad4780ed1
This commit is contained in:
committed by
Facebook GitHub Bot
parent
2962d69b67
commit
2f651c8783
@@ -1830,13 +1830,14 @@ public final class com/facebook/react/common/ReactConstants {
|
||||
public static final field UNSET I
|
||||
}
|
||||
|
||||
public class com/facebook/react/common/ShakeDetector : android/hardware/SensorEventListener {
|
||||
public final class com/facebook/react/common/ShakeDetector : android/hardware/SensorEventListener {
|
||||
public fun <init> (Lcom/facebook/react/common/ShakeDetector$ShakeListener;)V
|
||||
public fun <init> (Lcom/facebook/react/common/ShakeDetector$ShakeListener;I)V
|
||||
public synthetic fun <init> (Lcom/facebook/react/common/ShakeDetector$ShakeListener;IILkotlin/jvm/internal/DefaultConstructorMarker;)V
|
||||
public fun onAccuracyChanged (Landroid/hardware/Sensor;I)V
|
||||
public fun onSensorChanged (Landroid/hardware/SensorEvent;)V
|
||||
public fun start (Landroid/hardware/SensorManager;)V
|
||||
public fun stop ()V
|
||||
public final fun start (Landroid/hardware/SensorManager;)V
|
||||
public final fun stop ()V
|
||||
}
|
||||
|
||||
public abstract interface class com/facebook/react/common/ShakeDetector$ShakeListener {
|
||||
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.common;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.Nullsafe;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/** Listens for the user shaking their phone. Allocation-less once it starts listening. */
|
||||
@Nullsafe(Nullsafe.Mode.LOCAL)
|
||||
public class ShakeDetector implements SensorEventListener {
|
||||
// Collect sensor data in this interval (nanoseconds)
|
||||
private static final long MIN_TIME_BETWEEN_SAMPLES_NS =
|
||||
TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS);
|
||||
// Number of nanoseconds to listen for and count shakes (nanoseconds)
|
||||
private static final float SHAKING_WINDOW_NS = TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS);
|
||||
// Required force to constitute a rage shake. Need to multiply gravity by 1.33 because a rage
|
||||
// shake in one direction should have more force than just the magnitude of free fall.
|
||||
private static final float REQUIRED_FORCE = SensorManager.GRAVITY_EARTH * 1.33f;
|
||||
|
||||
private float mAccelerationX, mAccelerationY, mAccelerationZ;
|
||||
|
||||
public interface ShakeListener {
|
||||
void onShake();
|
||||
}
|
||||
|
||||
private final ShakeListener mShakeListener;
|
||||
|
||||
@Nullable private SensorManager mSensorManager;
|
||||
private long mLastTimestamp;
|
||||
private int mNumShakes;
|
||||
private long mLastShakeTimestamp;
|
||||
// number of shakes required to trigger onShake()
|
||||
private int mMinNumShakes;
|
||||
|
||||
public ShakeDetector(ShakeListener listener) {
|
||||
this(listener, 1);
|
||||
}
|
||||
|
||||
public ShakeDetector(ShakeListener listener, int minNumShakes) {
|
||||
mShakeListener = listener;
|
||||
mMinNumShakes = minNumShakes;
|
||||
}
|
||||
|
||||
/** Start listening for shakes. */
|
||||
public void start(SensorManager manager) {
|
||||
Assertions.assertNotNull(manager);
|
||||
Sensor accelerometer = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
if (accelerometer != null) {
|
||||
mSensorManager = manager;
|
||||
mLastTimestamp = -1;
|
||||
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
|
||||
mLastShakeTimestamp = 0;
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
/** Stop listening for shakes. */
|
||||
public void stop() {
|
||||
if (mSensorManager != null) {
|
||||
mSensorManager.unregisterListener(this);
|
||||
mSensorManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset all variables used to keep track of number of shakes recorded. */
|
||||
private void reset() {
|
||||
mNumShakes = 0;
|
||||
mAccelerationX = 0;
|
||||
mAccelerationY = 0;
|
||||
mAccelerationZ = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if acceleration applied to sensor is large enough to count as a rage shake.
|
||||
*
|
||||
* @param a acceleration in x, y, or z applied to the sensor
|
||||
* @return true if the magnitude of the force exceeds the minimum required amount of force. false
|
||||
* otherwise.
|
||||
*/
|
||||
private boolean atLeastRequiredForce(float a) {
|
||||
return Math.abs(a) > REQUIRED_FORCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data about last shake
|
||||
*
|
||||
* @param timestamp (ns) of last sensor event
|
||||
*/
|
||||
private void recordShake(long timestamp) {
|
||||
mLastShakeTimestamp = timestamp;
|
||||
mNumShakes++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent sensorEvent) {
|
||||
if (sensorEvent.timestamp - mLastTimestamp < MIN_TIME_BETWEEN_SAMPLES_NS) {
|
||||
return;
|
||||
}
|
||||
|
||||
float ax = sensorEvent.values[0];
|
||||
float ay = sensorEvent.values[1];
|
||||
float az = sensorEvent.values[2] - SensorManager.GRAVITY_EARTH;
|
||||
|
||||
mLastTimestamp = sensorEvent.timestamp;
|
||||
|
||||
if (atLeastRequiredForce(ax) && ax * mAccelerationX <= 0) {
|
||||
recordShake(sensorEvent.timestamp);
|
||||
mAccelerationX = ax;
|
||||
} else if (atLeastRequiredForce(ay) && ay * mAccelerationY <= 0) {
|
||||
recordShake(sensorEvent.timestamp);
|
||||
mAccelerationY = ay;
|
||||
} else if (atLeastRequiredForce(az) && az * mAccelerationZ <= 0) {
|
||||
recordShake(sensorEvent.timestamp);
|
||||
mAccelerationZ = az;
|
||||
}
|
||||
|
||||
maybeDispatchShake(sensorEvent.timestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int i) {}
|
||||
|
||||
private void maybeDispatchShake(long currentTimestamp) {
|
||||
if (mNumShakes >= 8 * mMinNumShakes) {
|
||||
reset();
|
||||
mShakeListener.onShake();
|
||||
}
|
||||
|
||||
if (currentTimestamp - mLastShakeTimestamp > SHAKING_WINDOW_NS) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.common
|
||||
|
||||
import android.hardware.Sensor
|
||||
import android.hardware.SensorEvent
|
||||
import android.hardware.SensorEventListener
|
||||
import android.hardware.SensorManager
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/** Listens for the user shaking their phone. Allocation-less once it starts listening. */
|
||||
public class ShakeDetector
|
||||
@JvmOverloads
|
||||
constructor(private val shakeListener: ShakeListener, private val minNumShakes: Int = 1) :
|
||||
SensorEventListener {
|
||||
|
||||
private var accelerationX = 0f
|
||||
private var accelerationY = 0f
|
||||
private var accelerationZ = 0f
|
||||
|
||||
public fun interface ShakeListener {
|
||||
public fun onShake()
|
||||
}
|
||||
|
||||
private var sensorManager: SensorManager? = null
|
||||
private var lastTimestamp: Long = 0
|
||||
private var numShakes = 0
|
||||
private var lastShakeTimestamp: Long = 0
|
||||
|
||||
/** Start listening for shakes. */
|
||||
public fun start(manager: SensorManager): Unit {
|
||||
val accelerometer = manager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) ?: return
|
||||
sensorManager = manager
|
||||
lastTimestamp = -1
|
||||
manager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI)
|
||||
lastShakeTimestamp = 0
|
||||
reset()
|
||||
}
|
||||
|
||||
/** Stop listening for shakes. */
|
||||
public fun stop(): Unit {
|
||||
sensorManager?.unregisterListener(this)
|
||||
sensorManager = null
|
||||
}
|
||||
|
||||
/** Reset all variables used to keep track of number of shakes recorded. */
|
||||
private fun reset() {
|
||||
numShakes = 0
|
||||
accelerationX = 0f
|
||||
accelerationY = 0f
|
||||
accelerationZ = 0f
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if acceleration applied to sensor is large enough to count as a rage shake.
|
||||
*
|
||||
* @param a acceleration in x, y, or z applied to the sensor
|
||||
* @return true if the magnitude of the force exceeds the minimum required amount of force. false
|
||||
* otherwise.
|
||||
*/
|
||||
private fun atLeastRequiredForce(a: Float): Boolean = Math.abs(a) > REQUIRED_FORCE
|
||||
|
||||
/**
|
||||
* Save data about last shake
|
||||
*
|
||||
* @param timestamp (ns) of last sensor event
|
||||
*/
|
||||
private fun recordShake(timestamp: Long) {
|
||||
lastShakeTimestamp = timestamp
|
||||
numShakes++
|
||||
}
|
||||
|
||||
override fun onSensorChanged(sensorEvent: SensorEvent) {
|
||||
if (sensorEvent.timestamp - lastTimestamp < MIN_TIME_BETWEEN_SAMPLES_NS) {
|
||||
return
|
||||
}
|
||||
val ax = sensorEvent.values[0]
|
||||
val ay = sensorEvent.values[1]
|
||||
val az = sensorEvent.values[2] - SensorManager.GRAVITY_EARTH
|
||||
lastTimestamp = sensorEvent.timestamp
|
||||
when {
|
||||
atLeastRequiredForce(ax) && ax * accelerationX <= 0 -> {
|
||||
recordShake(sensorEvent.timestamp)
|
||||
accelerationX = ax
|
||||
}
|
||||
atLeastRequiredForce(ay) && ay * accelerationY <= 0 -> {
|
||||
recordShake(sensorEvent.timestamp)
|
||||
accelerationY = ay
|
||||
}
|
||||
atLeastRequiredForce(az) && az * accelerationZ <= 0 -> {
|
||||
recordShake(sensorEvent.timestamp)
|
||||
accelerationZ = az
|
||||
}
|
||||
}
|
||||
maybeDispatchShake(sensorEvent.timestamp)
|
||||
}
|
||||
|
||||
override fun onAccuracyChanged(sensor: Sensor, i: Int): Unit = Unit
|
||||
|
||||
private fun maybeDispatchShake(currentTimestamp: Long) {
|
||||
if (numShakes >= 8 * minNumShakes) {
|
||||
reset()
|
||||
shakeListener.onShake()
|
||||
}
|
||||
if (currentTimestamp - lastShakeTimestamp > SHAKING_WINDOW_NS) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collect sensor data in this interval (nanoseconds)
|
||||
private val MIN_TIME_BETWEEN_SAMPLES_NS = TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS)
|
||||
|
||||
// Number of nanoseconds to listen for and count shakes (nanoseconds)
|
||||
private val SHAKING_WINDOW_NS = TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS).toFloat()
|
||||
|
||||
// Required force to constitute a rage shake. Need to multiply gravity by 1.33 because a rage
|
||||
// shake in one direction should have more force than just the magnitude of free fall.
|
||||
private const val REQUIRED_FORCE = SensorManager.GRAVITY_EARTH * 1.33f
|
||||
Reference in New Issue
Block a user