./gradlew assembleDebugThis project uses ktlint via the ktlint-gradle plugin to enforce consistent Kotlin code style.
# Check for violations
./gradlew ktlintCheck
# Auto-format all Kotlin files
./gradlew ktlintFormatConfiguration is in .editorconfig at the project root.
API reference documentation is generated with Dokka 2.2.0 and deployed to GitHub Pages.
./gradlew :sensey:dokkaGeneratePublicationHtmlOpen sensey/build/dokka/html/index.html in a browser.
./gradlew publishDocsOutput goes to build/dokka/{version}/ with a latest/ symlink.
The docs workflow runs on pushes to master/
develop and version tags (v*). It generates docs with the Dokka versioning
plugin, organizes output into versioned directories, and deploys to GitHub
Pages at nisrulz.github.io/sensey.
-
To release library to MavenLocal (~/.m2/):
./gradlew releaseToMavenLocal
-
To release library to MavenCentral:
./gradlew releaseToMavenCentral
- AGP 9.2.1
- Gradle 9.5.1
- Kotlin 2.3.21
- Java 21
- compileSdk 36 / minSdk 23 / targetSdk 35
- Configuration cache enabled
Sensey's plugin system lets you define custom gesture detection by implementing
the GesturePlugin interface. Plugins are registered via Sensey.register()
inside a senseyRegister {} or SenseyGestureEffect {} block.
import com.github.nisrulz.sensey.Sensey
import com.github.nisrulz.sensey.contract.GesturePlugin
class MyAppPlugin(
private val dispatcher: (String) -> Unit,
) : GesturePlugin {
override val key = "MyAppPlugin"
override fun onRegister(sensey: Sensey) {
dispatcher("Plugin registered")
}
override fun onUnregister(sensey: Sensey) {
// cleanup
}
}Register:
senseyRegister(lifecycle) {
shakePlugin { ... }
Sensey.register(MyAppPlugin { msg -> println(msg) })
}For custom sensor processing, implement GesturePlugin and manage the sensor
listener directly using Android's SensorManager:
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import com.github.nisrulz.sensey.Sensey
import com.github.nisrulz.sensey.contract.GesturePlugin
class PressurePlugin(
private val context: android.content.Context,
private val onPressure: (Float) -> Unit,
) : GesturePlugin {
override val key = "PressurePlugin"
private var listener: SensorEventListener? = null
override fun onRegister(sensey: Sensey) {
val sm = context.getSystemService(android.content.Context.SENSOR_SERVICE) as SensorManager
val sensor = sm.getDefaultSensor(Sensor.TYPE_PRESSURE) ?: return
listener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
onPressure(event.values[0])
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL)
}
override fun onUnregister(sensey: Sensey) {
val sm = context.getSystemService(android.content.Context.SENSOR_SERVICE) as SensorManager
listener?.let { sm.unregisterListener(it) }
listener = null
}
}For custom touch gesture handling in Compose, provide a ComposeGestureProvider
that gets attached via Modifier.senseyGestures():
import androidx.compose.ui.input.pointer.PointerInputScope
import androidx.compose.foundation.gestures.detectTapGestures
import com.github.nisrulz.sensey.Sensey
import com.github.nisrulz.sensey.contract.GesturePlugin
import com.github.nisrulz.sensey.gesture.compose.ComposeGestureProvider
class DoubleTapPlugin(
private val onDoubleTap: () -> Unit,
) : GesturePlugin {
override val key = "DoubleTapPlugin"
override fun onRegister(sensey: Sensey) {
sensey.registerComposeGestureProvider(
ComposeGestureProvider { installGestures() }
)
}
override fun onUnregister(sensey: Sensey) {}
private suspend fun PointerInputScope.installGestures() {
detectTapGestures(onDoubleTap = { onDoubleTap() })
}
}