mirror of
https://github.com/PenumbraOS/sdk.git
synced 2026-02-03 17:26:48 -06:00
API for accessing hand gestures
This commit is contained in:
parent
549e30278a
commit
f113487937
@ -16,6 +16,7 @@ class BridgeService {
|
||||
|
||||
private var touchpadProvider: ITouchpadProvider? = null
|
||||
private var ledProvider: ILedProvider? = null
|
||||
private var handGestureProvider: IHandGestureProvider? = null
|
||||
private var handTrackingProvider: IHandTrackingProvider? = null
|
||||
|
||||
private var esimProvider: IEsimProvider? = null
|
||||
@ -48,6 +49,10 @@ class BridgeService {
|
||||
return this@BridgeService.ledProvider?.asBinder()
|
||||
}
|
||||
|
||||
override fun getHandGestureProvider(): IBinder? {
|
||||
return this@BridgeService.handGestureProvider?.asBinder()
|
||||
}
|
||||
|
||||
override fun getHandTrackingProvider(): IBinder? {
|
||||
return this@BridgeService.handTrackingProvider?.asBinder()
|
||||
}
|
||||
@ -71,6 +76,7 @@ class BridgeService {
|
||||
sttProvider: ISttProvider?,
|
||||
touchpadProvider: ITouchpadProvider?,
|
||||
ledProvider: ILedProvider?,
|
||||
handGestureProvider: IHandGestureProvider?,
|
||||
handTrackingProvider: IHandTrackingProvider?,
|
||||
esimProvider: IEsimProvider?
|
||||
) {
|
||||
@ -83,6 +89,7 @@ class BridgeService {
|
||||
|
||||
this@BridgeService.touchpadProvider = touchpadProvider
|
||||
this@BridgeService.ledProvider = ledProvider
|
||||
this@BridgeService.handGestureProvider = handGestureProvider
|
||||
this@BridgeService.handTrackingProvider = handTrackingProvider
|
||||
|
||||
this@BridgeService.esimProvider = esimProvider
|
||||
|
||||
@ -6,6 +6,7 @@ import com.penumbraos.bridge.IDnsProvider;
|
||||
import com.penumbraos.bridge.ISttProvider;
|
||||
import com.penumbraos.bridge.ITouchpadProvider;
|
||||
import com.penumbraos.bridge.ILedProvider;
|
||||
import com.penumbraos.bridge.IHandGestureProvider;
|
||||
import com.penumbraos.bridge.IHandTrackingProvider;
|
||||
import com.penumbraos.bridge.IEsimProvider;
|
||||
import com.penumbraos.bridge.ISettingsProvider;
|
||||
@ -20,13 +21,14 @@ interface IBridge {
|
||||
|
||||
IBinder getTouchpadProvider();
|
||||
IBinder getLedProvider();
|
||||
IBinder getHandGestureProvider();
|
||||
IBinder getHandTrackingProvider();
|
||||
|
||||
IBinder getEsimProvider();
|
||||
|
||||
IBinder getSettingsProvider();
|
||||
IBinder getShellProvider();
|
||||
void registerSystemService(IHttpProvider httpProvider, IWebSocketProvider webSocketProvider, IDnsProvider dnsProvider, ISttProvider sttProvider, ITouchpadProvider touchpadProvider, ILedProvider ledProvider, IHandTrackingProvider handTrackingProvider, IEsimProvider esimProvider);
|
||||
void registerSystemService(IHttpProvider httpProvider, IWebSocketProvider webSocketProvider, IDnsProvider dnsProvider, ISttProvider sttProvider, ITouchpadProvider touchpadProvider, ILedProvider ledProvider, IHandGestureProvider handGestureProvider, IHandTrackingProvider handTrackingProvider, IEsimProvider esimProvider);
|
||||
void registerSettingsService(ISettingsProvider settingsProvider);
|
||||
void registerShellService(IShellProvider shellProvider);
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
package com.penumbraos.bridge;
|
||||
|
||||
import com.penumbraos.bridge.callback.IHandGestureCallback;
|
||||
|
||||
interface IHandGestureProvider {
|
||||
void registerCallback(IHandGestureCallback callback);
|
||||
void deregisterCallback(IHandGestureCallback callback);
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.penumbraos.bridge.callback;
|
||||
|
||||
oneway interface IHandGestureCallback {
|
||||
void onHandClose();
|
||||
void onHandPush();
|
||||
}
|
||||
@ -11,6 +11,7 @@ import com.penumbraos.bridge_system.esim.LPA_APK_PATH
|
||||
import com.penumbraos.bridge_system.esim.MockFactoryService
|
||||
import com.penumbraos.bridge_system.provider.DnsProvider
|
||||
import com.penumbraos.bridge_system.provider.EsimProvider
|
||||
import com.penumbraos.bridge_system.provider.HandGestureProvider
|
||||
import com.penumbraos.bridge_system.provider.HandTrackingProvider
|
||||
import com.penumbraos.bridge_system.provider.HttpProvider
|
||||
import com.penumbraos.bridge_system.provider.LedProvider
|
||||
@ -71,6 +72,7 @@ class Entrypoint {
|
||||
SttProvider(context, looper),
|
||||
TouchpadProvider(looper),
|
||||
LedProvider(context),
|
||||
HandGestureProvider(looper),
|
||||
HandTrackingProvider(context),
|
||||
EsimProvider(classLoader, context)
|
||||
)
|
||||
|
||||
@ -0,0 +1,128 @@
|
||||
package com.penumbraos.bridge_system.provider
|
||||
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.InputChannel
|
||||
import android.view.InputEvent
|
||||
import android.view.InputEventReceiver
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import com.penumbraos.bridge.IHandGestureProvider
|
||||
import com.penumbraos.bridge.callback.IHandGestureCallback
|
||||
import com.penumbraos.bridge_system.util.registerTouchpadInputChannel
|
||||
|
||||
private const val TAG = "HandGestureProvider"
|
||||
|
||||
private const val VELOCITY_THRESHOLD = 0.5f
|
||||
private const val VELOCITY_MIN_MOVING = 0.3f
|
||||
private const val GESTURE_COOLDOWN_MS = 500L
|
||||
|
||||
class HandGestureProvider(private val looper: Looper) : IHandGestureProvider.Stub() {
|
||||
private val callbacks = mutableListOf<IHandGestureCallback>()
|
||||
private var listener: EventListener? = null
|
||||
|
||||
inner class PushListener {
|
||||
|
||||
private var gestureActive = false
|
||||
|
||||
private var lastDepth = Float.NaN
|
||||
private var lastTimestamp = 0L
|
||||
private var gestureEndTime = 0L
|
||||
|
||||
fun processMotionEvent(event: MotionEvent) {
|
||||
val depth = event.getAxisValue(MotionEvent.AXIS_PRESSURE)
|
||||
val timestamp = event.eventTime
|
||||
|
||||
if (lastDepth.isNaN()) {
|
||||
lastDepth = depth
|
||||
lastTimestamp = timestamp
|
||||
return
|
||||
}
|
||||
|
||||
val timeDelta = timestamp - lastTimestamp
|
||||
if (timeDelta > 0) {
|
||||
val depthDelta = depth - lastDepth
|
||||
val velocity = kotlin.math.abs(depthDelta * 1000.0f / timeDelta)
|
||||
|
||||
when {
|
||||
!gestureActive && velocity >= VELOCITY_THRESHOLD && (timestamp - gestureEndTime) > GESTURE_COOLDOWN_MS -> {
|
||||
gestureActive = true
|
||||
onHandPush()
|
||||
}
|
||||
|
||||
gestureActive && velocity < VELOCITY_MIN_MOVING -> {
|
||||
gestureActive = false
|
||||
gestureEndTime = timestamp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastDepth = depth
|
||||
lastTimestamp = timestamp
|
||||
}
|
||||
}
|
||||
|
||||
inner class EventListener(inputChannel: InputChannel) :
|
||||
InputEventReceiver(inputChannel, looper) {
|
||||
private val pushListener = PushListener()
|
||||
|
||||
override fun onInputEvent(event: InputEvent?) {
|
||||
if (event != null) {
|
||||
if (event is MotionEvent) {
|
||||
pushListener.processMotionEvent(event)
|
||||
} else if (event is KeyEvent && event.keyCode == KeyEvent.KEYCODE_H) {
|
||||
onHandClose()
|
||||
}
|
||||
}
|
||||
super.onInputEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerCallback(callback: IHandGestureCallback) {
|
||||
callbacks.add(callback)
|
||||
registerListenerIfNecessary()
|
||||
}
|
||||
|
||||
override fun deregisterCallback(callback: IHandGestureCallback) {
|
||||
callbacks.remove(callback)
|
||||
if (callbacks.count() < 1) {
|
||||
Log.w(TAG, "Deregistering hand gesture listener")
|
||||
listener?.dispose()
|
||||
listener = null
|
||||
}
|
||||
}
|
||||
|
||||
fun onHandPush() {
|
||||
callCallback { callback -> callback.onHandPush() }
|
||||
}
|
||||
|
||||
fun onHandClose() {
|
||||
callCallback { callback -> callback.onHandClose() }
|
||||
}
|
||||
|
||||
private fun callCallback(withCallback: (IHandGestureCallback) -> Unit) {
|
||||
val callbacksToRemove = mutableListOf<IHandGestureCallback>()
|
||||
callbacks.forEach { callback ->
|
||||
safeCallback(TAG, {
|
||||
withCallback(callback)
|
||||
}, onDeadObject = {
|
||||
callbacksToRemove.add(callback)
|
||||
})
|
||||
}
|
||||
callbacksToRemove.forEach { callback -> deregisterCallback(callback) }
|
||||
}
|
||||
|
||||
private fun registerListenerIfNecessary() {
|
||||
if (listener != null) {
|
||||
return
|
||||
}
|
||||
|
||||
Log.w(TAG, "Registering touchpad listener")
|
||||
|
||||
val inputChannel = registerTouchpadInputChannel(TAG)
|
||||
|
||||
if (inputChannel != null) {
|
||||
listener = EventListener(inputChannel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,14 @@
|
||||
package com.penumbraos.bridge_system.provider
|
||||
|
||||
import android.hardware.input.IInputManager
|
||||
import android.os.Looper
|
||||
import android.os.ServiceManager
|
||||
import android.util.Log
|
||||
import android.view.InputChannel
|
||||
import android.view.InputEvent
|
||||
import android.view.InputEventReceiver
|
||||
import com.penumbraos.bridge.ITouchpadProvider
|
||||
import com.penumbraos.bridge.callback.ITouchpadCallback
|
||||
import com.penumbraos.bridge_system.util.registerTouchpadInputChannel
|
||||
|
||||
private const val TOUCHPAD_MONITOR_NAME = "Humane Touchpad Monitor"
|
||||
private const val TOUCHPAD_DISPLAY_ID = 3344
|
||||
private const val TOUCHPAD_EVENT_SOURCE = 0x100008
|
||||
|
||||
private const val TAG = "TouchpadProvider"
|
||||
@ -19,11 +16,12 @@ private const val TAG = "TouchpadProvider"
|
||||
class TouchpadProvider(private val looper: Looper) :
|
||||
ITouchpadProvider.Stub() {
|
||||
private val callbacks = mutableListOf<ITouchpadCallback>()
|
||||
private var listener: EventListener? = null
|
||||
|
||||
inner class EventListener(inputChannel: InputChannel) :
|
||||
InputEventReceiver(inputChannel, looper) {
|
||||
override fun onInputEvent(event: InputEvent?) {
|
||||
if (event != null) {
|
||||
if (event != null && event.isFromSource(TOUCHPAD_EVENT_SOURCE)) {
|
||||
val callbacksToRemove = mutableListOf<ITouchpadCallback>()
|
||||
callbacks.forEach { callback ->
|
||||
safeCallback(TAG, {
|
||||
@ -38,8 +36,6 @@ class TouchpadProvider(private val looper: Looper) :
|
||||
}
|
||||
}
|
||||
|
||||
private var listener: EventListener? = null
|
||||
|
||||
override fun registerCallback(callback: ITouchpadCallback) {
|
||||
callbacks.add(callback)
|
||||
registerListenerIfNecessary()
|
||||
@ -61,15 +57,10 @@ class TouchpadProvider(private val looper: Looper) :
|
||||
|
||||
Log.w(TAG, "Registering touchpad listener")
|
||||
|
||||
try {
|
||||
val inputManagerBinder = ServiceManager.getService("input")
|
||||
val inputManager = IInputManager.Stub.asInterface(inputManagerBinder)
|
||||
val inputChannel = registerTouchpadInputChannel(TAG)
|
||||
|
||||
val inputMonitor =
|
||||
inputManager.monitorGestureInput(TOUCHPAD_MONITOR_NAME, TOUCHPAD_DISPLAY_ID)
|
||||
listener = EventListener(inputMonitor.inputChannel)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to register touchpad listener", e)
|
||||
if (inputChannel != null) {
|
||||
listener = EventListener(inputChannel)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.penumbraos.bridge_system.util
|
||||
|
||||
import android.hardware.input.IInputManager
|
||||
import android.os.ServiceManager
|
||||
import android.util.Log
|
||||
import android.view.InputChannel
|
||||
|
||||
private const val TOUCHPAD_MONITOR_NAME = "Humane Touchpad Monitor"
|
||||
private const val TOUCHPAD_DISPLAY_ID = 3344
|
||||
|
||||
fun registerTouchpadInputChannel(tag: String): InputChannel? {
|
||||
return try {
|
||||
val inputManagerBinder = ServiceManager.getService("input")
|
||||
val inputManager = IInputManager.Stub.asInterface(inputManagerBinder)
|
||||
|
||||
inputManager.monitorGestureInput(TOUCHPAD_MONITOR_NAME, TOUCHPAD_DISPLAY_ID).inputChannel
|
||||
} catch (e: Exception) {
|
||||
Log.e(tag, "Failed to register touchpad listener", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,8 @@ import android.os.IBinder
|
||||
import android.util.Log
|
||||
import com.penumbraos.bridge.IBridge
|
||||
import com.penumbraos.bridge.IDnsProvider
|
||||
import com.penumbraos.bridge.IEsimProvider
|
||||
import com.penumbraos.bridge.IHandGestureProvider
|
||||
import com.penumbraos.bridge.IHandTrackingProvider
|
||||
import com.penumbraos.bridge.IHttpProvider
|
||||
import com.penumbraos.bridge.ILedProvider
|
||||
@ -17,10 +19,10 @@ import com.penumbraos.bridge.IShellProvider
|
||||
import com.penumbraos.bridge.ISttProvider
|
||||
import com.penumbraos.bridge.ITouchpadProvider
|
||||
import com.penumbraos.bridge.IWebSocketProvider
|
||||
import com.penumbraos.bridge.IEsimProvider
|
||||
import com.penumbraos.bridge.external.BRIDGE_SERVICE_READY
|
||||
import com.penumbraos.sdk.api.DnsClient
|
||||
import com.penumbraos.sdk.api.EsimClient
|
||||
import com.penumbraos.sdk.api.HandGestureClient
|
||||
import com.penumbraos.sdk.api.HandTrackingClient
|
||||
import com.penumbraos.sdk.api.HttpClient
|
||||
import com.penumbraos.sdk.api.LedClient
|
||||
@ -49,6 +51,7 @@ class PenumbraClient {
|
||||
|
||||
lateinit var touchpad: TouchpadClient
|
||||
lateinit var led: LedClient
|
||||
lateinit var handGesture: HandGestureClient
|
||||
lateinit var handTracking: HandTrackingClient
|
||||
|
||||
lateinit var esim: EsimClient
|
||||
@ -125,10 +128,12 @@ class PenumbraClient {
|
||||
val touchpadProvider =
|
||||
ITouchpadProvider.Stub.asInterface(service!!.getTouchpadProvider())
|
||||
val ledProvider = ILedProvider.Stub.asInterface(service!!.getLedProvider())
|
||||
val handGestureProvider =
|
||||
IHandGestureProvider.Stub.asInterface(service!!.getHandGestureProvider())
|
||||
val handTrackingProvider =
|
||||
IHandTrackingProvider.Stub.asInterface(service!!.getHandTrackingProvider())
|
||||
|
||||
val esimProvider =
|
||||
val esimProvider =
|
||||
IEsimProvider.Stub.asInterface(service!!.getEsimProvider())
|
||||
|
||||
val settingsProvider =
|
||||
@ -145,6 +150,7 @@ class PenumbraClient {
|
||||
|
||||
touchpad = TouchpadClient(touchpadProvider)
|
||||
led = LedClient(ledProvider)
|
||||
handGesture = HandGestureClient(handGestureProvider)
|
||||
handTracking = HandTrackingClient(handTrackingProvider)
|
||||
|
||||
esim = EsimClient(esimProvider)
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
package com.penumbraos.sdk.api
|
||||
|
||||
import com.penumbraos.bridge.IHandGestureProvider
|
||||
import com.penumbraos.bridge.callback.IHandGestureCallback
|
||||
import com.penumbraos.sdk.api.types.HandGestureReceiver
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class HandGestureClient(private val handGestureProvider: IHandGestureProvider) {
|
||||
private val registeredCallbacks =
|
||||
ConcurrentHashMap<HandGestureReceiver, IHandGestureCallback.Stub>()
|
||||
|
||||
fun register(receiver: HandGestureReceiver) {
|
||||
val callbackStub = object : IHandGestureCallback.Stub() {
|
||||
override fun onHandClose() {
|
||||
receiver.onHandClose()
|
||||
}
|
||||
|
||||
override fun onHandPush() {
|
||||
receiver.onHandPush()
|
||||
}
|
||||
}
|
||||
registeredCallbacks[receiver] = callbackStub
|
||||
handGestureProvider.registerCallback(callbackStub)
|
||||
}
|
||||
|
||||
fun remove(receiver: HandGestureReceiver) {
|
||||
val callbackStub = registeredCallbacks.remove(receiver)
|
||||
if (callbackStub != null) {
|
||||
handGestureProvider.deregisterCallback(callbackStub)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
package com.penumbraos.sdk.api.types
|
||||
|
||||
interface HandGestureReceiver {
|
||||
fun onHandClose()
|
||||
fun onHandPush()
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user