Merge pull request #12863 from JosJuice/android-gamepad-sensors
Android: Fix and enable input device sensor input
This commit is contained in:
commit
10f06a48ef
|
@ -19,19 +19,31 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
private class AxisSetDetails(val firstAxisOfSet: Int, val axisSetType: Int)
|
private class AxisSetDetails(val firstAxisOfSet: Int, val axisSetType: Int)
|
||||||
|
|
||||||
private class SensorDetails(
|
private class SensorDetails(
|
||||||
|
val sensor: Sensor,
|
||||||
val sensorType: Int,
|
val sensorType: Int,
|
||||||
val axisNames: Array<String>,
|
val axisNames: Array<String>,
|
||||||
val axisSetDetails: Array<AxisSetDetails>
|
val axisSetDetails: Array<AxisSetDetails>
|
||||||
) {
|
) {
|
||||||
var isSuspended = true
|
var isSuspended = true
|
||||||
|
var hasRegisteredListener = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sensorManager: SensorManager?
|
private val sensorManager: SensorManager?
|
||||||
|
|
||||||
private val sensorDetails = HashMap<Sensor, SensorDetails>()
|
private val sensorDetails = ArrayList<SensorDetails>()
|
||||||
|
|
||||||
private val rotateCoordinatesForScreenOrientation: Boolean
|
private val rotateCoordinatesForScreenOrientation: Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AOSP has a bug in InputDeviceSensorManager where
|
||||||
|
* InputSensorEventListenerDelegate.removeSensor attempts to modify an ArrayList it's iterating
|
||||||
|
* through in a way that throws a ConcurrentModificationException. Because of this, we can't
|
||||||
|
* suspend individual sensors for InputDevices, but we can suspend all sensors at once.
|
||||||
|
*/
|
||||||
|
private val canSuspendSensorsIndividually: Boolean
|
||||||
|
|
||||||
|
private var unsuspendedSensors = 0
|
||||||
|
|
||||||
private var deviceQualifier = ""
|
private var deviceQualifier = ""
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
|
@ -39,25 +51,22 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
sensorManager = DolphinApplication.getAppContext()
|
sensorManager = DolphinApplication.getAppContext()
|
||||||
.getSystemService(Context.SENSOR_SERVICE) as SensorManager?
|
.getSystemService(Context.SENSOR_SERVICE) as SensorManager?
|
||||||
rotateCoordinatesForScreenOrientation = true
|
rotateCoordinatesForScreenOrientation = true
|
||||||
|
canSuspendSensorsIndividually = true
|
||||||
addSensors()
|
addSensors()
|
||||||
|
sortSensorDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
constructor(inputDevice: InputDevice) {
|
constructor(inputDevice: InputDevice) {
|
||||||
rotateCoordinatesForScreenOrientation = false
|
rotateCoordinatesForScreenOrientation = false
|
||||||
sensorManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
canSuspendSensorsIndividually = false
|
||||||
inputDevice.sensorManager
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
sensorManager = inputDevice.sensorManager
|
||||||
// TODO: There is a bug where after suspending sensors, onSensorChanged can get called for
|
addSensors()
|
||||||
// a sensor that we never registered as a listener for. The way our code is currently written,
|
|
||||||
// this causes a NullPointerException, but if we checked for null we would instead have the
|
|
||||||
// problem of being spammed with onSensorChanged calls even though the sensor shouldn't be
|
|
||||||
// enabled. For now, let's comment out the ability to use InputDevice sensors.
|
|
||||||
|
|
||||||
//addSensors();
|
|
||||||
} else {
|
} else {
|
||||||
null
|
sensorManager = null
|
||||||
}
|
}
|
||||||
|
sortSensorDetails()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSensors() {
|
private fun addSensors() {
|
||||||
|
@ -254,15 +263,22 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
) {
|
) {
|
||||||
val sensor = sensorManager!!.getDefaultSensor(sensorType)
|
val sensor = sensorManager!!.getDefaultSensor(sensorType)
|
||||||
if (sensor != null) {
|
if (sensor != null) {
|
||||||
sensorDetails[sensor] = SensorDetails(sensorType, axisNames, axisSetDetails)
|
sensorDetails.add(SensorDetails(sensor, sensorType, axisNames, axisSetDetails))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sortSensorDetails() {
|
||||||
|
Collections.sort(
|
||||||
|
sensorDetails,
|
||||||
|
Comparator.comparingInt { s: SensorDetails -> s.sensorType }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSensorChanged(sensorEvent: SensorEvent) {
|
override fun onSensorChanged(sensorEvent: SensorEvent) {
|
||||||
val sensorDetails = sensorDetails[sensorEvent.sensor]
|
val sensorDetails = sensorDetails.first{s -> sensorsAreEqual(s.sensor, sensorEvent.sensor)}
|
||||||
|
|
||||||
val values = sensorEvent.values
|
val values = sensorEvent.values
|
||||||
val axisNames = sensorDetails!!.axisNames
|
val axisNames = sensorDetails.axisNames
|
||||||
val axisSetDetails = sensorDetails.axisSetDetails
|
val axisSetDetails = sensorDetails.axisSetDetails
|
||||||
|
|
||||||
var eventAxisIndex = 0
|
var eventAxisIndex = 0
|
||||||
|
@ -356,7 +372,7 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!keepSensorAlive) {
|
if (!keepSensorAlive) {
|
||||||
setSensorSuspended(sensorEvent.sensor, sensorDetails, true)
|
setSensorSuspended(sensorDetails, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,18 +397,14 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
fun requestUnsuspendSensor(axisName: String) {
|
fun requestUnsuspendSensor(axisName: String) {
|
||||||
for ((key, value) in sensorDetails) {
|
for (sd in sensorDetails) {
|
||||||
if (listOf(*value.axisNames).contains(axisName)) {
|
if (listOf(*sd.axisNames).contains(axisName)) {
|
||||||
setSensorSuspended(key, value, false)
|
setSensorSuspended(sd, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setSensorSuspended(
|
private fun setSensorSuspended(sensorDetails: SensorDetails, suspend: Boolean) {
|
||||||
sensor: Sensor,
|
|
||||||
sensorDetails: SensorDetails,
|
|
||||||
suspend: Boolean
|
|
||||||
) {
|
|
||||||
var changeOccurred = false
|
var changeOccurred = false
|
||||||
|
|
||||||
synchronized(sensorDetails) {
|
synchronized(sensorDetails) {
|
||||||
|
@ -403,10 +415,46 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
suspend
|
suspend
|
||||||
)
|
)
|
||||||
|
|
||||||
if (suspend)
|
if (suspend) {
|
||||||
sensorManager!!.unregisterListener(this, sensor)
|
unsuspendedSensors -= 1
|
||||||
else
|
} else {
|
||||||
sensorManager!!.registerListener(this, sensor, SAMPLING_PERIOD_US)
|
unsuspendedSensors += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canSuspendSensorsIndividually) {
|
||||||
|
if (suspend) {
|
||||||
|
sensorManager!!.unregisterListener(this, sensorDetails.sensor)
|
||||||
|
} else {
|
||||||
|
sensorManager!!.registerListener(
|
||||||
|
this,
|
||||||
|
sensorDetails.sensor,
|
||||||
|
SAMPLING_PERIOD_US
|
||||||
|
)
|
||||||
|
}
|
||||||
|
sensorDetails.hasRegisteredListener = !suspend
|
||||||
|
} else {
|
||||||
|
if (suspend) {
|
||||||
|
// If there are no unsuspended sensors left, unregister them all.
|
||||||
|
// Otherwise, leave unregistering for later. A possible alternative could be
|
||||||
|
// to unregister everything and then re-register the sensors we still want,
|
||||||
|
// but I fear this could lead to dropped inputs.
|
||||||
|
if (unsuspendedSensors == 0) {
|
||||||
|
sensorManager!!.unregisterListener(this)
|
||||||
|
for (sd in this.sensorDetails) {
|
||||||
|
sd.hasRegisteredListener = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!sensorDetails.hasRegisteredListener) {
|
||||||
|
sensorManager!!.registerListener(
|
||||||
|
this,
|
||||||
|
sensorDetails.sensor,
|
||||||
|
SAMPLING_PERIOD_US
|
||||||
|
)
|
||||||
|
sensorDetails.hasRegisteredListener = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sensorDetails.isSuspended = suspend
|
sensorDetails.isSuspended = suspend
|
||||||
|
|
||||||
|
@ -415,14 +463,14 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changeOccurred) {
|
if (changeOccurred) {
|
||||||
Log.info((if (suspend) "Suspended sensor " else "Unsuspended sensor ") + sensor.name)
|
Log.info((if (suspend) "Suspended sensor " else "Unsuspended sensor ") + sensorDetails.sensor.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
fun getAxisNames(): Array<String> {
|
fun getAxisNames(): Array<String> {
|
||||||
val axisNames = ArrayList<String>()
|
val axisNames = ArrayList<String>()
|
||||||
for (sensorDetails in sensorDetailsSorted) {
|
for (sensorDetails in sensorDetails) {
|
||||||
sensorDetails.axisNames.forEach { axisNames.add(it) }
|
sensorDetails.axisNames.forEach { axisNames.add(it) }
|
||||||
}
|
}
|
||||||
return axisNames.toArray(arrayOf())
|
return axisNames.toArray(arrayOf())
|
||||||
|
@ -432,7 +480,7 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
fun getNegativeAxes(): BooleanArray {
|
fun getNegativeAxes(): BooleanArray {
|
||||||
val negativeAxes = ArrayList<Boolean>()
|
val negativeAxes = ArrayList<Boolean>()
|
||||||
|
|
||||||
for (sensorDetails in sensorDetailsSorted) {
|
for (sensorDetails in sensorDetails) {
|
||||||
var eventAxisIndex = 0
|
var eventAxisIndex = 0
|
||||||
var detailsAxisIndex = 0
|
var detailsAxisIndex = 0
|
||||||
var detailsAxisSetIndex = 0
|
var detailsAxisSetIndex = 0
|
||||||
|
@ -467,22 +515,13 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sensorDetailsSorted: List<SensorDetails>
|
|
||||||
get() {
|
|
||||||
val sensorDetails = ArrayList(sensorDetails.values)
|
|
||||||
Collections.sort(
|
|
||||||
sensorDetails,
|
|
||||||
Comparator.comparingInt { s: SensorDetails -> s.sensorType }
|
|
||||||
)
|
|
||||||
return sensorDetails
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// Set of three axes. Creates a negative companion to each axis, and corrects for device rotation.
|
// Set of three axes. Creates a negative companion to each axis, and corrects for device rotation.
|
||||||
private const val AXIS_SET_TYPE_DEVICE_COORDINATES = 0
|
private const val AXIS_SET_TYPE_DEVICE_COORDINATES = 0
|
||||||
|
|
||||||
// Set of three axes. Creates a negative companion to each axis.
|
// Set of three axes. Creates a negative companion to each axis.
|
||||||
private const val AXIS_SET_TYPE_OTHER_COORDINATES = 1
|
private const val AXIS_SET_TYPE_OTHER_COORDINATES = 1
|
||||||
|
|
||||||
private var deviceRotation = Surface.ROTATION_0
|
private var deviceRotation = Surface.ROTATION_0
|
||||||
|
|
||||||
// The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
|
// The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
|
||||||
|
@ -500,5 +539,13 @@ class DolphinSensorEventListener : SensorEventListener {
|
||||||
fun setDeviceRotation(deviceRotation: Int) {
|
fun setDeviceRotation(deviceRotation: Int) {
|
||||||
this.deviceRotation = deviceRotation
|
this.deviceRotation = deviceRotation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sensorsAreEqual(s1: Sensor, s2: Sensor): Boolean {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && s1.id > 0 && s2.id > 0) {
|
||||||
|
s1.type == s2.type && s1.id == s2.id
|
||||||
|
} else {
|
||||||
|
s1.type == s2.type && s1.name == s2.name
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue