diff --git a/README.md b/README.md
index 3798c75..559bda2 100644
--- a/README.md
+++ b/README.md
@@ -10,9 +10,6 @@ This library provides a convenience wrapper over the [Sensor APIs](https://devel
## Demo ❤️
![Screen Recording 2023-04-03 at 1 00 08 PM](https://user-images.githubusercontent.com/89389061/229441943-6339d18f-c704-4d92-9fe8-28c2fd94fdeb.gif)
-## WIP 🚧
-This library is a work-in-progress and is subject to major changes. Our team is working hard to get it stable as soon as possible. Thank you for your patience 🌺
-
## Usage 🚀
### Install dependency 📲
#### Kotlin `build.gradle.kts (:module-name)`
@@ -65,15 +62,15 @@ Ambient Temperature | ✅️ | rememberAmbientTemperatureSensorState()
Magnetic Field (Uncalibrated) | ✅️️ | rememberUncalibratedMagneticFieldSensorState()
GameRotation Vector | ✅️ | rememberGameRotationVectorSensorState()
Gyroscope (Uncalibrated) | ✅️ | rememberUncalibratedGyroscopeSensorState()
-Significant Motion | — | N/A
+Significant Motion | ✅ | rememberSignificantMotionSensorState(onMotionEvent = {})
Step Detector | ✅️ | rememberStepDetectorSensorState()
Step Counter | ✅️ | rememberStepCounterSensorState()
Geomagnetic Rotation Vector | ✅️️ | rememberGeomagneticRotationVectorSensorState()
Heart Rate | ✅️ | rememberHeartRateSensorState()
Pose6DOF | — | N/A
-Stationary Detect | ⚠️ | WIP
-Motion Detect | ⚠️ | WIP
-Heart Beat | — | N/A
+Stationary Detect | ✅️ | rememberStationaryDetectSensorState()
+Motion Detect | ✅️ | rememberMotionDetectSensorState()
+Heart Beat | ✅ | rememberHeartBeatSensorState()
Low Latency Off-Body Detect | ✅️ | rememberLowLatencyOffBodyDetectSensorState()
Accelerometer (Uncalibrated) | ✅️ | rememberUncalibratedAccelerometerSensorState()
Hinge Angle | ✅️ | rememberHingeAngleSensorState()
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt
index 42c8758..38150c6 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt
@@ -22,7 +22,9 @@ class AccelerometerSensorState internal constructor(
val zForce: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is AccelerometerSensorState) return false
@@ -32,6 +34,8 @@ class AccelerometerSensorState internal constructor(
if (zForce != other.zForce) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -42,30 +46,44 @@ class AccelerometerSensorState internal constructor(
result = 31 * result + zForce.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
return "AccelerometerSensorState(xForce=$xForce, yForce=$yForce, zForce=$zForce, " +
- "isAvailable=$isAvailable, accuracy=$accuracy)"
+ "isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [AccelerometerSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberAccelerometerSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): AccelerometerSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.Accelerometer,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val accelerometerSensorState = remember { mutableStateOf(AccelerometerSensorState()) }
@@ -79,7 +97,9 @@ fun rememberAccelerometerSensorState(
yForce = sensorStateValues[1],
zForce = sensorStateValues[2],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/AmbientTemperatureSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/AmbientTemperatureSensorState.kt
index 0f9f63c..96f9ebd 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/AmbientTemperatureSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/AmbientTemperatureSensorState.kt
@@ -14,63 +14,84 @@ import androidx.compose.runtime.remember
*/
@Immutable
class AmbientTemperatureSensorState internal constructor(
- val temperature: Float = 0f,
- val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
- override fun toString(): String {
- return "AmbientTemperatureSensorState(temperature=$temperature, " +
- "isAvailable=$isAvailable, accuracy=$accuracy)"
- }
+ val temperature: Float = 0f,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ other as AmbientTemperatureSensorState
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- other as AmbientTemperatureSensorState
+ if (temperature != other.temperature) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+ return true
+ }
- if (temperature != other.temperature) return false
- if (isAvailable != other.isAvailable) return false
- if (accuracy != other.accuracy) return false
- return true
- }
+ override fun hashCode(): Int {
+ var result = temperature.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
- override fun hashCode(): Int {
- var result = temperature.hashCode()
- result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
- return result
- }
-}
+ override fun toString(): String {
+ return "AmbientTemperatureSensorState(temperature=$temperature, " +
+ "isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
/**
* Creates and [remember]s instance of [AmbientTemperatureSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberAmbientTemperatureSensorState(
- sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
): AmbientTemperatureSensorState {
- val sensorState = rememberSensorState(
- sensorType = SensorType.AmbientTemperature,
- sensorDelay = sensorDelay,
- onError = onError,
- )
-
- val ambientTemperatureSensorState = remember { mutableStateOf(AmbientTemperatureSensorState()) }
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.AmbientTemperature,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
- LaunchedEffect(key1 = sensorState, block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- ambientTemperatureSensorState.value = AmbientTemperatureSensorState(
- temperature = sensorStateValues[0],
- isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
- )
+ val ambientTemperatureSensorState = remember {
+ mutableStateOf(AmbientTemperatureSensorState())
}
- })
- return ambientTemperatureSensorState.value
-}
\ No newline at end of file
+ LaunchedEffect(key1 = sensorState, block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ ambientTemperatureSensorState.value = AmbientTemperatureSensorState(
+ temperature = sensorStateValues[0],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ })
+
+ return ambientTemperatureSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GameRotationVectorSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GameRotationVectorSensorState.kt
index 6d5c83f..db1f1f9 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/GameRotationVectorSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GameRotationVectorSensorState.kt
@@ -16,72 +16,93 @@ import androidx.compose.runtime.remember
*/
@Immutable
class GameRotationVectorSensorState internal constructor(
- val vectorX: Float = 0f,
- val vectorY: Float = 0f,
- val vectorZ: Float = 0f,
- val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
- override fun toString(): String {
- return "GameRotationVectorSensorState(vectorX=$vectorX, " +
- "vectorY=$vectorY, vectorZ=$vectorZ, isAvailable=$isAvailable, " +
- "accuracy=$accuracy)"
- }
+ val vectorX: Float = 0f,
+ val vectorY: Float = 0f,
+ val vectorZ: Float = 0f,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ other as GameRotationVectorSensorState
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- other as GameRotationVectorSensorState
+ if (vectorX != other.vectorX) return false
+ if (vectorY != other.vectorY) return false
+ if (vectorZ != other.vectorZ) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+ return true
+ }
- if (vectorX != other.vectorX) return false
- if (vectorY != other.vectorY) return false
- if (vectorZ != other.vectorZ) return false
- if (isAvailable != other.isAvailable) return false
- if (accuracy != other.accuracy) return false
- return true
- }
+ override fun hashCode(): Int {
+ var result = vectorX.hashCode()
+ result = 31 * result + vectorY.hashCode()
+ result = 31 * result + vectorZ.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
- override fun hashCode(): Int {
- var result = vectorX.hashCode()
- result = 31 * result + vectorY.hashCode()
- result = 31 * result + vectorZ.hashCode()
- result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
- return result
- }
-}
+ override fun toString(): String {
+ return "GameRotationVectorSensorState(vectorX=$vectorX, " +
+ "vectorY=$vectorY, vectorZ=$vectorZ, isAvailable=$isAvailable, " +
+ "accuracy=$accuracy)"
+ }
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
/**
* Creates and [remember]s instance of [GameRotationVectorSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Game].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberGameRotationVectorSensorState(
- sensorDelay: SensorDelay = SensorDelay.Game,
- onError: (throwable: Throwable) -> Unit = {},
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Game,
+ onError: (throwable: Throwable) -> Unit = {}
): GameRotationVectorSensorState {
- val sensorState = rememberSensorState(
- sensorType = SensorType.GameRotationVector,
- sensorDelay = sensorDelay,
- onError = onError,
- )
-
- val gameRotationVectorSensorState = remember { mutableStateOf(GameRotationVectorSensorState()) }
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.GameRotationVector,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
- LaunchedEffect(key1 = sensorState, block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- gameRotationVectorSensorState.value = GameRotationVectorSensorState(
- vectorX = sensorStateValues[0],
- vectorY = sensorStateValues[1],
- vectorZ = sensorStateValues[2],
- isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
- )
+ val gameRotationVectorSensorState = remember {
+ mutableStateOf(GameRotationVectorSensorState())
}
- })
- return gameRotationVectorSensorState.value
-}
\ No newline at end of file
+ LaunchedEffect(key1 = sensorState, block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ gameRotationVectorSensorState.value = GameRotationVectorSensorState(
+ vectorX = sensorStateValues[0],
+ vectorY = sensorStateValues[1],
+ vectorZ = sensorStateValues[2],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ })
+
+ return gameRotationVectorSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GeomagneticRotationVectorSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GeomagneticRotationVectorSensorState.kt
index 4f6c8b8..bb99d6e 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/GeomagneticRotationVectorSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GeomagneticRotationVectorSensorState.kt
@@ -16,73 +16,93 @@ import androidx.compose.runtime.remember
*/
@Immutable
class GeomagneticRotationVectorSensorState internal constructor(
- val vectorX: Float = 0f,
- val vectorY: Float = 0f,
- val vectorZ: Float = 0f,
- val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
- override fun toString(): String {
- return "GeomagneticRotationVectorSensorState(vectorX=$vectorX, " +
- "vectorY=$vectorY, vectorZ=$vectorZ, isAvailable=$isAvailable, " +
- "accuracy=$accuracy)"
- }
+ val vectorX: Float = 0f,
+ val vectorY: Float = 0f,
+ val vectorZ: Float = 0f,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ other as GeomagneticRotationVectorSensorState
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- other as GeomagneticRotationVectorSensorState
+ if (vectorX != other.vectorX) return false
+ if (vectorY != other.vectorY) return false
+ if (vectorZ != other.vectorZ) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
- if (vectorX != other.vectorX) return false
- if (vectorY != other.vectorY) return false
- if (vectorZ != other.vectorZ) return false
- if (isAvailable != other.isAvailable) return false
- if (accuracy != other.accuracy) return false
+ return true
+ }
- return true
- }
+ override fun hashCode(): Int {
+ var result = vectorX.hashCode()
+ result = 31 * result + vectorY.hashCode()
+ result = 31 * result + vectorZ.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
- override fun hashCode(): Int {
- var result = vectorX.hashCode()
- result = 31 * result + vectorY.hashCode()
- result = 31 * result + vectorZ.hashCode()
- result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
- return result
- }
+ override fun toString(): String {
+ return "GeomagneticRotationVectorSensorState(vectorX=$vectorX, " +
+ "vectorY=$vectorY, vectorZ=$vectorZ, isAvailable=$isAvailable, " +
+ "accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s instance of [GeomagneticRotationVectorSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberGeomagneticRotationVectorSensorState(
- sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
): GeomagneticRotationVectorSensorState {
- val sensorState = rememberSensorState(
- sensorType = SensorType.GeomagneticRotationVector,
- sensorDelay = sensorDelay,
- onError = onError,
- )
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.GeomagneticRotationVector,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
- val geomagneticRotationVectorSensorState =
- remember { mutableStateOf(GeomagneticRotationVectorSensorState()) }
+ val geomagneticRotationVectorSensorState =
+ remember { mutableStateOf(GeomagneticRotationVectorSensorState()) }
- LaunchedEffect(key1 = sensorState, block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- geomagneticRotationVectorSensorState.value = GeomagneticRotationVectorSensorState(
- vectorX = sensorStateValues[0],
- vectorY = sensorStateValues[1],
- vectorZ = sensorStateValues[2],
- isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
- )
- }
- })
+ LaunchedEffect(key1 = sensorState, block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ geomagneticRotationVectorSensorState.value = GeomagneticRotationVectorSensorState(
+ vectorX = sensorStateValues[0],
+ vectorY = sensorStateValues[1],
+ vectorZ = sensorStateValues[2],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ })
- return geomagneticRotationVectorSensorState.value
-}
\ No newline at end of file
+ return geomagneticRotationVectorSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt
index 96e2017..5a71b1b 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt
@@ -22,7 +22,9 @@ class GravitySensorState internal constructor(
val zForce: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is GravitySensorState) return false
@@ -31,6 +33,9 @@ class GravitySensorState internal constructor(
if (yForce != other.yForce) return false
if (zForce != other.zForce) return false
if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -40,30 +45,45 @@ class GravitySensorState internal constructor(
result = 31 * result + yForce.hashCode()
result = 31 * result + zForce.hashCode()
result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
return "GravitySensorState(xForce=$xForce, yForce=$yForce, " +
- "zForce=$zForce, isAvailable=$isAvailable, accuracy=$accuracy)"
+ "zForce=$zForce, isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [GravitySensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberGravitySensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): GravitySensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.Gravity,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val gravitySensorState = remember { mutableStateOf(GravitySensorState()) }
@@ -77,7 +97,9 @@ fun rememberGravitySensorState(
yForce = sensorStateValues[1],
zForce = sensorStateValues[2],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt
index d6d268e..fde8d26 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt
@@ -22,7 +22,9 @@ class GyroscopeSensorState internal constructor(
val zRotation: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is GyroscopeSensorState) return false
@@ -31,6 +33,8 @@ class GyroscopeSensorState internal constructor(
if (yRotation != other.yRotation) return false
if (zRotation != other.zRotation) return false
if (isAvailable != other.isAvailable) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -40,30 +44,44 @@ class GyroscopeSensorState internal constructor(
result = 31 * result + yRotation.hashCode()
result = 31 * result + zRotation.hashCode()
result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
return "GyroscopeSensorState(xRotation=$xRotation, yRotation=$yRotation, " +
- "zRotation=$zRotation, isAvailable=$isAvailable, accuracy=$accuracy)"
+ "zRotation=$zRotation, isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [GyroscopeSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberGyroscopeSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): GyroscopeSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.Gyroscope,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val gyroscopeSensorState = remember { mutableStateOf(GyroscopeSensorState()) }
@@ -77,7 +95,9 @@ fun rememberGyroscopeSensorState(
yRotation = sensorStateValues[1],
zRotation = sensorStateValues[2],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/HeadTrackerSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/HeadTrackerSensorState.kt
index 507caef..0529503 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/HeadTrackerSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/HeadTrackerSensorState.kt
@@ -9,13 +9,17 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
/**
- * The HeadTracker sensor measures the orientation of a user's head relative to an arbitrary reference frame, as well as the rate of rotation.
+ * The HeadTracker sensor measures the orientation of a user's head relative to an arbitrary
+ * reference frame, as well as the rate of rotation.
* @param xRotation X component of Euler vector representing rotation. Defaults to 0f.
* @param yRotation Y component of Euler vector representing rotation. Defaults to 0f.
* @param zRotation Z component of Euler vector representing rotation. Defaults to 0f.
- * @param xAngularVelocity X component of Euler vector representing angular velocity (if supported, otherwise 0). Defaults to 0f.
- * @param yAngularVelocity Y component of Euler vector representing angular velocity (if supported, otherwise 0). Defaults to 0f.
- * @param zAngularVelocity Z component of Euler vector representing angular velocity (if supported, otherwise 0). Defaults to 0f.
+ * @param xAngularVelocity X component of Euler vector representing angular velocity (if supported,
+ * otherwise 0). Defaults to 0f.
+ * @param yAngularVelocity Y component of Euler vector representing angular velocity (if supported,
+ * otherwise 0). Defaults to 0f.
+ * @param zAngularVelocity Z component of Euler vector representing angular velocity (if supported,
+ * otherwise 0). Defaults to 0f.
* @param isAvailable Whether the current device has a gyroscope sensor. Defaults to false.
* @param accuracy Accuracy factor of the gyroscope sensor. Defaults to 0.
**/
@@ -28,8 +32,10 @@ class HeadTrackerSensorState internal constructor(
val yAngularVelocity: Float = 0f,
val zAngularVelocity: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -45,6 +51,8 @@ class HeadTrackerSensorState internal constructor(
if (zAngularVelocity != other.zAngularVelocity) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -57,17 +65,32 @@ class HeadTrackerSensorState internal constructor(
result = 31 * result + yAngularVelocity.hashCode()
result = 31 * result + zAngularVelocity.hashCode()
result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "HeadTrackerSensorState(xRotation=$xRotation, yRotation=$yRotation, zRotation=$zRotation, xAngularVelocity=$xAngularVelocity, yAngularVelocity=$yAngularVelocity, zAngularVelocity=$zAngularVelocity, isAvailable=$isAvailable, accuracy=$accuracy)"
+ return "HeadTrackerSensorState(xRotation=$xRotation, yRotation=$yRotation," +
+ " zRotation=$zRotation, xAngularVelocity=$xAngularVelocity," +
+ " yAngularVelocity=$yAngularVelocity, zAngularVelocity=$zAngularVelocity," +
+ " isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [HeadTrackerSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
@@ -75,12 +98,14 @@ class HeadTrackerSensorState internal constructor(
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun rememberHeadTrackerSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): HeadTrackerSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.HeadTracker,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
@@ -97,7 +122,9 @@ fun rememberHeadTrackerSensorState(
yAngularVelocity = sensorStateValues[4],
zAngularVelocity = sensorStateValues[5],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
})
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/HeadingSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/HeadingSensorState.kt
index bbebb33..e1a9c50 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/HeadingSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/HeadingSensorState.kt
@@ -9,73 +9,99 @@ import androidx.compose.runtime.remember
/**
* Measures a device's heading value in degrees (anti-clockwise),
*
- * @param degrees Indicates direction in which the device is pointing
- * relative to true north in degrees. Defaults to 0f.
- * @param accuracy Indicates the confidence of prediction, under Gaussian
- * standard normal distribution. Defaults to 0f.
- * @param isAvailable Whether the current device has a heading sensor.
- * Defaults to false.
+ * @param degrees Indicates direction in which the device is pointing relative to true north in
+ * degrees. Defaults to 0f.
+ * @param headingAccuracy Indicates the confidence of prediction, under Gaussian standard normal
+ * distribution. Defaults to 0f.
+ * @param isAvailable Whether the current device has a heading sensor. Defaults to false.
+ * @param accuracy Accuracy factor of the heading sensor. Defaults to 0.
*/
@Immutable
class HeadingSensorState internal constructor(
val degrees: Float = 0f,
- val accuracy: Float = 0f,
- val isAvailable: Boolean = false
-) {
+ val headingAccuracy: Float = 0f,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is HeadingSensorState) return false
+
+ if (degrees != other.degrees) return false
+ if (headingAccuracy != other.headingAccuracy) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+
+ return true
+ }
+
override fun hashCode(): Int {
var result = degrees.hashCode()
- result = 31 * result + accuracy.hashCode()
+ result = 31 * result + headingAccuracy.hashCode()
result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "HeadingSensorState(degrees=$degrees, accuracy=$accuracy, " +
- "isAvailable=$isAvailable)"
+ return "HeadingSensorState(degrees=$degrees, headingAccuracy=$headingAccuracy, " +
+ "isAvailable=$isAvailable, accuracy=$accuracy)"
}
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is HeadingSensorState) return false
-
- if (degrees != other.degrees) return false
- if (accuracy != other.accuracy) return false
- if (isAvailable != other.isAvailable) return false
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
- return true
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [HeadingSensorState].
- *
- * @param sensorDelay The rate at which the raw sensor data should be
- * received. Defaults to [SensorDelay.Normal].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received. Defaults to
+ * [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberHeadingSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): HeadingSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.Heading,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val headingSensorState = remember { mutableStateOf(HeadingSensorState()) }
- LaunchedEffect(key1 = sensorState, block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- headingSensorState.value = HeadingSensorState(
- degrees = sensorStateValues[0],
- accuracy = sensorStateValues[1],
- isAvailable = sensorState.isAvailable
- )
+ LaunchedEffect(
+ key1 = sensorState,
+ block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ headingSensorState.value = HeadingSensorState(
+ degrees = sensorStateValues[0],
+ headingAccuracy = sensorStateValues[1],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
}
- })
+ )
return headingSensorState.value
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/HeartBeatSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/HeartBeatSensorState.kt
new file mode 100644
index 0000000..0b28145
--- /dev/null
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/HeartBeatSensorState.kt
@@ -0,0 +1,109 @@
+package com.mutualmobile.composesensors
+
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+
+/**
+ * The HeartBeat sensor returns an event everytime a heart beat peak is detected. Peak here ideally
+ * corresponds to the positive peak in the QRS complex of an ECG signal.
+ *
+ * For more info, please refer the [Android Documentation Reference](https://developer.android.com/reference/android/hardware/SensorEvent#sensor.type_heart_beat:)
+ *
+ * @param isConfidentPeak A confidence value of 0.0 (false) indicates complete uncertainty - that a peak
+ * is as likely to be at the indicated timestamp as anywhere else. A confidence value of 1.0 (true)
+ * indicates complete certainly - that a peak is completely unlikely to be anywhere else on the QRS
+ * complex. Defaults to false.
+ * @param isAvailable Whether the current device has a heart beat sensor. Defaults to false.
+ * @param accuracy Accuracy factor of the heart beat sensor. Defaults to 0.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+@Immutable
+class HeartBeatSensorState internal constructor(
+ val isConfidentPeak: Boolean = false,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is HeartBeatSensorState) return false
+
+ if (isConfidentPeak != other.isConfidentPeak) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = isConfidentPeak.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "HeartBeatSensorState(isConfidentPeak=$isConfidentPeak isAvailable=$isAvailable, " +
+ "accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
+
+/**
+ * Creates and [remember]s an instance of [HeartBeatSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received.
+ * Defaults to [SensorDelay.Normal].
+ * @param onError Callback invoked on every error state.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+@Composable
+fun rememberHeartBeatSensorState(
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
+): HeartBeatSensorState {
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.HeartBeat,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
+ val confidenceSensorState = remember { mutableStateOf(HeartBeatSensorState()) }
+
+ LaunchedEffect(
+ key1 = sensorState,
+ block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ confidenceSensorState.value = HeartBeatSensorState(
+ isConfidentPeak = sensorStateValues[0] == 1f,
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ }
+ )
+
+ return confidenceSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/HeartRateSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/HeartRateSensorState.kt
index 5f8f80d..572c811 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/HeartRateSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/HeartRateSensorState.kt
@@ -17,7 +17,9 @@ class HeartRateSensorState internal constructor(
val heartRate: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is HeartRateSensorState) return false
@@ -25,6 +27,8 @@ class HeartRateSensorState internal constructor(
if (heartRate != other.heartRate) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -33,29 +37,44 @@ class HeartRateSensorState internal constructor(
var result = heartRate.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "HeartRateSensorState(heartRate=$heartRate isAvailable=$isAvailable, accuracy=$accuracy)"
+ return "HeartRateSensorState(heartRate=$heartRate isAvailable=$isAvailable," +
+ " accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [HeartRateSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberHeartRateSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): HeartRateSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.HeartRate,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val heartRateSensorState = remember { mutableStateOf(HeartRateSensorState()) }
@@ -68,9 +87,11 @@ fun rememberHeartRateSensorState(
heartRate = sensorStateValues[0],
isAvailable = sensorState.isAvailable,
accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
- },
+ }
)
return heartRateSensorState.value
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/HingeAngleSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/HingeAngleSensorState.kt
index b3c2882..503b344 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/HingeAngleSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/HingeAngleSensorState.kt
@@ -21,8 +21,10 @@ import androidx.compose.runtime.remember
class HingeAngleSensorState internal constructor(
val angle: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is HingeAngleSensorState) return false
@@ -30,6 +32,8 @@ class HingeAngleSensorState internal constructor(
if (angle != other.angle) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -38,16 +42,28 @@ class HingeAngleSensorState internal constructor(
var result = angle.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
return "HingeAngleSensorState(angle=$angle, isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [HingeAngleSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
@@ -55,12 +71,14 @@ class HingeAngleSensorState internal constructor(
@RequiresApi(Build.VERSION_CODES.R)
@Composable
fun rememberHingeAngleSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): HingeAngleSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.HingeAngle,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val hingeAngleSensorState = remember { mutableStateOf(HingeAngleSensorState()) }
@@ -73,7 +91,9 @@ fun rememberHingeAngleSensorState(
hingeAngleSensorState.value = HingeAngleSensorState(
angle = sensorStateValues[0],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt
index 04343da..f44718e 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt
@@ -17,7 +17,9 @@ class LightSensorState internal constructor(
val illuminance: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is LightSensorState) return false
@@ -25,6 +27,8 @@ class LightSensorState internal constructor(
if (illuminance != other.illuminance) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -33,30 +37,44 @@ class LightSensorState internal constructor(
var result = illuminance.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
return "LightSensorState(illuminance=$illuminance, isAvailable=$isAvailable, " +
- "accuracy=$accuracy)"
+ "accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [LightSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberLightSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): LightSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.Light,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val lightSensorState = remember { mutableStateOf(LightSensorState()) }
@@ -68,7 +86,9 @@ fun rememberLightSensorState(
lightSensorState.value = LightSensorState(
illuminance = sensorStateValues[0],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesAccelerometerSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesAccelerometerSensorState.kt
index 0909840..088d371 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesAccelerometerSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesAccelerometerSensorState.kt
@@ -27,8 +27,10 @@ class LimitedAxesAccelerometerSensorState internal constructor(
val yAxisSupported: Boolean = false,
val zAxisSupported: Boolean = false,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -42,6 +44,8 @@ class LimitedAxesAccelerometerSensorState internal constructor(
if (zAxisSupported != other.zAxisSupported) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -54,7 +58,9 @@ class LimitedAxesAccelerometerSensorState internal constructor(
result = 31 * result + yAxisSupported.hashCode()
result = 31 * result + zAxisSupported.hashCode()
result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -63,25 +69,38 @@ class LimitedAxesAccelerometerSensorState internal constructor(
"zForce=$zForce, xAxisSupported=$xAxisSupported, yAxisSupported=$yAxisSupported, " +
"zAxisSupported=$zAxisSupported, isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [LimitedAxesAccelerometerSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberLimitedAxesAccelerometerSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): LimitedAxesAccelerometerSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.AccelerometerLimitedAxes,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
- val accelerometerSensorState = remember { mutableStateOf(LimitedAxesAccelerometerSensorState()) }
+ val accelerometerSensorState =
+ remember { mutableStateOf(LimitedAxesAccelerometerSensorState()) }
LaunchedEffect(
key1 = sensorState,
@@ -96,7 +115,9 @@ fun rememberLimitedAxesAccelerometerSensorState(
yAxisSupported = sensorStateValues[4] != 0f,
zAxisSupported = sensorStateValues[5] != 0f,
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesGyroscopeSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesGyroscopeSensorState.kt
index ed9a334..42110dd 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesGyroscopeSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/LimitedAxesGyroscopeSensorState.kt
@@ -28,8 +28,10 @@ class LimitedAxesGyroscopeSensorState internal constructor(
val yAxisSupported: Boolean = false,
val zAxisSupported: Boolean = false,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
@@ -45,6 +47,8 @@ class LimitedAxesGyroscopeSensorState internal constructor(
if (zAxisSupported != other.zAxisSupported) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -57,7 +61,9 @@ class LimitedAxesGyroscopeSensorState internal constructor(
result = 31 * result + yAxisSupported.hashCode()
result = 31 * result + zAxisSupported.hashCode()
result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -67,10 +73,20 @@ class LimitedAxesGyroscopeSensorState internal constructor(
"yAxisSupported=$yAxisSupported, zAxisSupported=$zAxisSupported, " +
"isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [LimitedAxesGyroscopeSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
@@ -78,12 +94,14 @@ class LimitedAxesGyroscopeSensorState internal constructor(
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun rememberLimitedAxesGyroscopeSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): LimitedAxesGyroscopeSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.GyroscopeLimitedAxes,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
@@ -100,7 +118,9 @@ fun rememberLimitedAxesGyroscopeSensorState(
yAxisSupported = sensorStateValues[4] != 0f,
zAxisSupported = sensorStateValues[5] != 0f,
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
})
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LinearAccelerationSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LinearAccelerationSensorState.kt
index dfb90fb..7f10cff 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/LinearAccelerationSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/LinearAccelerationSensorState.kt
@@ -23,8 +23,10 @@ class LinearAccelerationSensorState internal constructor(
val yForce: Float = 0f,
val zForce: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is LinearAccelerationSensorState) return false
@@ -34,6 +36,8 @@ class LinearAccelerationSensorState internal constructor(
if (zForce != other.zForce) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -44,6 +48,8 @@ class LinearAccelerationSensorState internal constructor(
result = 31 * result + zForce.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -51,10 +57,20 @@ class LinearAccelerationSensorState internal constructor(
return "LinearAccelerationSensorState(xForce=$xForce, yForce=$yForce, zForce=$zForce, " +
"isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [LinearAccelerationSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
@@ -62,12 +78,14 @@ class LinearAccelerationSensorState internal constructor(
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
@Composable
fun rememberLinearAccelerationSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): LinearAccelerationSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.LinearAcceleration,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val linearAccelerationSensorState = remember { mutableStateOf(LinearAccelerationSensorState()) }
@@ -82,7 +100,9 @@ fun rememberLinearAccelerationSensorState(
yForce = sensorStateValues[1],
zForce = sensorStateValues[2],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LowLatencyOffBodyDetectSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LowLatencyOffBodyDetectSensorState.kt
index bd05424..d8af958 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/LowLatencyOffBodyDetectSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/LowLatencyOffBodyDetectSensorState.kt
@@ -20,8 +20,10 @@ import androidx.compose.runtime.remember
class LowLatencyOffBodyDetectSensorState internal constructor(
val isDeviceOnBody: Boolean = false,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is LowLatencyOffBodyDetectSensorState) return false
@@ -29,6 +31,8 @@ class LowLatencyOffBodyDetectSensorState internal constructor(
if (isDeviceOnBody != other.isDeviceOnBody) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -37,16 +41,29 @@ class LowLatencyOffBodyDetectSensorState internal constructor(
var result = isDeviceOnBody.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "LowLatencyOffBodyDetectSensorState(isDeviceOnBody=$isDeviceOnBody, isAvailable=$isAvailable, accuracy=$accuracy)"
+ return "LowLatencyOffBodyDetectSensorState(isDeviceOnBody=$isDeviceOnBody," +
+ " isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [LowLatencyOffBodyDetectSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
@@ -54,12 +71,14 @@ class LowLatencyOffBodyDetectSensorState internal constructor(
@RequiresApi(Build.VERSION_CODES.O)
@Composable
fun rememberLowLatencyOffBodyDetectSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): LowLatencyOffBodyDetectSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.LowLatencyOffBodyDetect,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val lowLatencyOffBodyDetectSensorState =
@@ -73,7 +92,9 @@ fun rememberLowLatencyOffBodyDetectSensorState(
lowLatencyOffBodyDetectSensorState.value = LowLatencyOffBodyDetectSensorState(
isDeviceOnBody = sensorStateValues[0].toInt() == 1,
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt
index 722e1e0..69eeb76 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt
@@ -21,7 +21,9 @@ class MagneticFieldSensorState internal constructor(
val zStrength: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is MagneticFieldSensorState) return false
@@ -31,6 +33,8 @@ class MagneticFieldSensorState internal constructor(
if (zStrength != other.zStrength) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -41,30 +45,44 @@ class MagneticFieldSensorState internal constructor(
result = 31 * result + zStrength.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
return "MagneticFieldSensorState(xStrength=$xStrength, yStrength=$yStrength, " +
- "zStrength=$zStrength, isAvailable=$isAvailable, accuracy=$accuracy)"
+ "zStrength=$zStrength, isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [MagneticFieldSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberMagneticFieldSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): MagneticFieldSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.MagneticField,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val magneticFieldSensorState = remember { mutableStateOf(MagneticFieldSensorState()) }
@@ -78,7 +96,9 @@ fun rememberMagneticFieldSensorState(
yStrength = sensorStateValues[1],
zStrength = sensorStateValues[2],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/MotionDetectSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/MotionDetectSensorState.kt
new file mode 100644
index 0000000..15724c4
--- /dev/null
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/MotionDetectSensorState.kt
@@ -0,0 +1,110 @@
+package com.mutualmobile.composesensors
+
+import android.hardware.Sensor
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+
+/**
+ * The Stationary Detect sensor ([Sensor.TYPE_MOTION_DETECT]) is used to detect whether the
+ * device is stationary or in motion. This sensor produces an event if the device has been in motion
+ * for at least 5 seconds with a maximal latency of 5 additional seconds. ie: it may take up
+ * anywhere from 5 to 10 seconds after the device has been at rest to trigger this event. The only
+ * allowed value is 1.0 (true).
+ *
+ * For more info, please refer the [Android Documentation Reference](https://developer.android.com/reference/android/hardware/SensorEvent#sensor.type_motion_detect:)
+ *
+ * @param isDeviceInMotion Whether the device is in motion or not. Defaults to false.
+ * @param isAvailable Whether the current device has a heart beat sensor. Defaults to false.
+ * @param accuracy Accuracy factor of the heart beat sensor. Defaults to 0.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+@Immutable
+class MotionDetectSensorState internal constructor(
+ val isDeviceInMotion: Boolean = false,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is MotionDetectSensorState) return false
+
+ if (isDeviceInMotion != other.isDeviceInMotion) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = isDeviceInMotion.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "MotionDetectSensorState(isDeviceInMotion=$isDeviceInMotion," +
+ " isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
+
+/**
+ * Creates and [remember]s an instance of [MotionDetectSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received.
+ * Defaults to [SensorDelay.Normal].
+ * @param onError Callback invoked on every error state.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+@Composable
+fun rememberMotionDetectSensorState(
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
+): MotionDetectSensorState {
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.MotionDetect,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
+ val confidenceSensorState = remember { mutableStateOf(MotionDetectSensorState()) }
+
+ LaunchedEffect(
+ key1 = sensorState,
+ block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ confidenceSensorState.value = MotionDetectSensorState(
+ isDeviceInMotion = sensorStateValues[0] == 1f,
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ }
+ )
+
+ return confidenceSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt
index 598cfb2..b601193 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt
@@ -16,8 +16,10 @@ import androidx.compose.runtime.remember
class PressureSensorState internal constructor(
val pressure: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is PressureSensorState) return false
@@ -25,6 +27,8 @@ class PressureSensorState internal constructor(
if (pressure != other.pressure) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -33,28 +37,43 @@ class PressureSensorState internal constructor(
var result = pressure.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "PressureSensorState(pressure=$pressure, isAvailable=$isAvailable, accuracy=$accuracy)"
+ return "PressureSensorState(pressure=$pressure, isAvailable=$isAvailable," +
+ " accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [PressureSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberPressureSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): PressureSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.Pressure,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val pressureSensorState = remember { mutableStateOf(PressureSensorState()) }
@@ -67,7 +86,9 @@ fun rememberPressureSensorState(
pressureSensorState.value = PressureSensorState(
pressure = sensorStateValues[0],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/ProximitySensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/ProximitySensorState.kt
index 40224db..1bc319c 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/ProximitySensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/ProximitySensorState.kt
@@ -14,63 +14,82 @@ import androidx.compose.runtime.remember
*/
@Immutable
class ProximitySensorState internal constructor(
- val sensorDistance: Float = 0f,
- val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
- override fun toString(): String {
- return "ProximitySensorState(sensorDistance=$sensorDistance, " +
- "isAvailable=$isAvailable, accuracy=$accuracy)"
- }
+ val sensorDistance: Float = 0f,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is ProximitySensorState) return false
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other !is ProximitySensorState) return false
+ if (sensorDistance != other.sensorDistance) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+ return true
+ }
- if (isAvailable != other.isAvailable) return false
- if (sensorDistance != other.sensorDistance) return false
- if (accuracy != other.accuracy) return false
- return true
- }
+ override fun hashCode(): Int {
+ var result = isAvailable.hashCode()
+ result = 31 * result + sensorDistance.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
- override fun hashCode(): Int {
- var result = isAvailable.hashCode()
- result = 31 * result + sensorDistance.hashCode()
- result = 31 * result + accuracy.hashCode()
- return result
- }
-}
+ override fun toString(): String {
+ return "ProximitySensorState(sensorDistance=$sensorDistance, " +
+ "isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
/**
* Creates and [remember]s instance of [ProximitySensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberProximitySensorState(
- sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
): ProximitySensorState {
- val sensorState = rememberSensorState(
- sensorType = SensorType.Proximity,
- sensorDelay = sensorDelay,
- onError = onError,
- )
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.Proximity,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
- val proximitySensorState = remember { mutableStateOf(ProximitySensorState()) }
+ val proximitySensorState = remember { mutableStateOf(ProximitySensorState()) }
- LaunchedEffect(key1 = sensorState, block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- proximitySensorState.value = ProximitySensorState(
- sensorDistance = sensorStateValues[0],
- isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
- )
- }
- })
+ LaunchedEffect(key1 = sensorState, block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ proximitySensorState.value = ProximitySensorState(
+ sensorDistance = sensorStateValues[0],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ })
- return proximitySensorState.value
-}
\ No newline at end of file
+ return proximitySensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/RelativeHumiditySensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/RelativeHumiditySensorState.kt
index 4eb2631..cff89a5 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/RelativeHumiditySensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/RelativeHumiditySensorState.kt
@@ -25,15 +25,21 @@ class RelativeHumiditySensorState internal constructor(
val relativeHumidity: Float = 0f,
val isAvailable: Boolean = false,
val actualTemp: Float = 0f,
- val accuracy: Int = 0
-) {
- val absoluteHumidity = 216.7 *
- (
- relativeHumidity / 100.0 * 6.112 * exp(
- 17.62 * actualTemp /
- (243.12 + actualTemp)
- ) / (273.15 + actualTemp)
- )
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ val absoluteHumidity = 216.7
+ .times(relativeHumidity)
+ .div(100.0)
+ .times(6.112)
+ .times(
+ exp(
+ 17.62
+ .times(actualTemp)
+ .div(243.12.plus(actualTemp))
+ ).div(273.15.plus(actualTemp))
+ )
private val humidity = ln(relativeHumidity / 100.0) +
(17.62 * actualTemp) / (243.12 + actualTemp)
@@ -47,6 +53,8 @@ class RelativeHumiditySensorState internal constructor(
if (relativeHumidity != other.relativeHumidity) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -55,6 +63,8 @@ class RelativeHumiditySensorState internal constructor(
var result = relativeHumidity.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -64,42 +74,57 @@ class RelativeHumiditySensorState internal constructor(
"accuracy=$accuracy)"
}
- /**
- * Creates and [remember]s an instance of [RelativeHumiditySensorState].
- * @param sensorDelay The rate at which the raw sensor data should be received.
- * Defaults to [SensorDelay.Normal].
- * @param actualTemp is passed into the function only if User requires calculations on Dew Point and absolute Humidity
- * This can be accessed using the dot operator. (e.g -> rememberRelativeHumiditySensorState().absoluteHumidity)
- * @param onError Callback invoked on every error state.
- */
- @Composable
- fun rememberRelativeHumiditySensorState(
- sensorDelay: SensorDelay = SensorDelay.Normal,
- actualTemp: Float = 0F,
- onError: (throwable: Throwable) -> Unit = {}
- ): RelativeHumiditySensorState {
- val sensorState = rememberSensorState(
- sensorType = SensorType.RelativeHumidity,
- sensorDelay = sensorDelay,
- onError = onError
- )
- val relativeHumiditySensorState = remember { mutableStateOf(RelativeHumiditySensorState()) }
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
- LaunchedEffect(
- key1 = sensorState,
- block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- relativeHumiditySensorState.value = RelativeHumiditySensorState(
- relativeHumidity = sensorStateValues[0],
- isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy,
- actualTemp = actualTemp
- )
- }
+/**
+ * Creates and [remember]s an instance of [RelativeHumiditySensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received.
+ * Defaults to [SensorDelay.Normal].
+ * @param actualTemp is passed into the function only if User requires calculations on Dew Point
+ * and absolute Humidity. This can be accessed using the dot operator. (e.g ->
+ * rememberRelativeHumiditySensorState().absoluteHumidity)
+ * @param onError Callback invoked on every error state.
+ */
+@Composable
+fun rememberRelativeHumiditySensorState(
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ actualTemp: Float = 0f,
+ onError: (throwable: Throwable) -> Unit = {}
+): RelativeHumiditySensorState {
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.RelativeHumidity,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
+ val relativeHumiditySensorState = remember { mutableStateOf(RelativeHumiditySensorState()) }
+
+ LaunchedEffect(
+ key1 = sensorState,
+ block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ relativeHumiditySensorState.value = RelativeHumiditySensorState(
+ relativeHumidity = sensorStateValues[0],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ actualTemp = actualTemp,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
}
- )
+ }
+ )
- return relativeHumiditySensorState.value
- }
+ return relativeHumiditySensorState.value
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/RotationVectorSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/RotationVectorSensorState.kt
index 30723fe..8bc8bda 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/RotationVectorSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/RotationVectorSensorState.kt
@@ -18,80 +18,100 @@ import androidx.compose.runtime.remember
*/
@Immutable
class RotationVectorSensorState internal constructor(
- val vectorX: Float = 0f,
- val vectorY: Float = 0f,
- val vectorZ: Float = 0f,
- val scalar: Float = 0f,
- val estimatedHeadingAccuracy: Float = 0f,
- val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
- override fun toString(): String {
- return "RotationVectorSensorState(vectorX=$vectorX, " +
- "vectorY=$vectorY, vectorZ=$vectorZ, scalar=$scalar, " +
- "estimatedHeadingAccuracy=$estimatedHeadingAccuracy, " +
- "isAvailable=$isAvailable, accuracy=$accuracy)"
- }
+ val vectorX: Float = 0f,
+ val vectorY: Float = 0f,
+ val vectorZ: Float = 0f,
+ val scalar: Float = 0f,
+ val estimatedHeadingAccuracy: Float = 0f,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ other as RotationVectorSensorState
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- other as RotationVectorSensorState
+ if (vectorX != other.vectorX) return false
+ if (vectorY != other.vectorY) return false
+ if (vectorZ != other.vectorZ) return false
+ if (scalar != other.scalar) return false
+ if (estimatedHeadingAccuracy != other.estimatedHeadingAccuracy) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+ return true
+ }
- if (vectorX != other.vectorX) return false
- if (vectorY != other.vectorY) return false
- if (vectorZ != other.vectorZ) return false
- if (scalar != other.scalar) return false
- if (estimatedHeadingAccuracy != other.estimatedHeadingAccuracy) return false
- if (isAvailable != other.isAvailable) return false
- if (accuracy != other.accuracy) return false
- return true
- }
+ override fun hashCode(): Int {
+ var result = vectorX.hashCode()
+ result = 31 * result + vectorY.hashCode()
+ result = 31 * result + vectorZ.hashCode()
+ result = 31 * result + scalar.hashCode()
+ result = 31 * result + estimatedHeadingAccuracy.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
- override fun hashCode(): Int {
- var result = vectorX.hashCode()
- result = 31 * result + vectorY.hashCode()
- result = 31 * result + vectorZ.hashCode()
- result = 31 * result + scalar.hashCode()
- result = 31 * result + estimatedHeadingAccuracy.hashCode()
- result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
- return result
- }
+ override fun toString(): String {
+ return "RotationVectorSensorState(vectorX=$vectorX, " +
+ "vectorY=$vectorY, vectorZ=$vectorZ, scalar=$scalar, " +
+ "estimatedHeadingAccuracy=$estimatedHeadingAccuracy, " +
+ "isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s instance of [RotationVectorSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberRotationVectorSensorState(
- sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
): RotationVectorSensorState {
- val sensorState = rememberSensorState(
- sensorType = SensorType.RotationVector,
- sensorDelay = sensorDelay,
- onError = onError,
- )
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.RotationVector,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
- val rotationVectorSensorState = remember { mutableStateOf(RotationVectorSensorState()) }
+ val rotationVectorSensorState = remember { mutableStateOf(RotationVectorSensorState()) }
- LaunchedEffect(key1 = sensorState, block = {
- val sensorStateValues = sensorState.data
- if (sensorStateValues.isNotEmpty()) {
- rotationVectorSensorState.value = RotationVectorSensorState(
- vectorX = sensorStateValues[0],
- vectorY = sensorStateValues[1],
- vectorZ = sensorStateValues[2],
- scalar = sensorStateValues[3],
- estimatedHeadingAccuracy = sensorStateValues[4],
- isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
- )
- }
- })
+ LaunchedEffect(key1 = sensorState, block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ rotationVectorSensorState.value = RotationVectorSensorState(
+ vectorX = sensorStateValues[0],
+ vectorY = sensorStateValues[1],
+ vectorZ = sensorStateValues[2],
+ scalar = sensorStateValues[3],
+ estimatedHeadingAccuracy = sensorStateValues[4],
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ })
- return rotationVectorSensorState.value
-}
\ No newline at end of file
+ return rotationVectorSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorDelay.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorDelay.kt
index ad21646..ccf2490 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorDelay.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorDelay.kt
@@ -5,6 +5,8 @@ import android.hardware.SensorManager
enum class SensorDelay {
/**
* Get sensor data as fast as possible
+ * - **``** may
+ * be required to be added in AndroidManifest.xml for this option to work
*/
Fastest,
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt
index ee9139e..468c394 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt
@@ -5,6 +5,8 @@ import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
+import android.hardware.TriggerEvent
+import android.hardware.TriggerEventListener
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.Immutable
@@ -21,32 +23,38 @@ import androidx.compose.ui.platform.LocalContext
* positioning, or you want to monitor changes in the ambient environment near a device.
*
* SensorState provides the availability status and the current data about a sensor.
+ * @param data Data from the sensor. Defaults to an empty list.
* @param isAvailable Whether the required sensor is available in the current device. Defaults to
* false.
- * @param data Data from the sensor. Defaults to an empty list.
* @param accuracy Accuracy factor of the current sensor. Defaults to 0.
*/
@Immutable
internal class SensorState(
- val isAvailable: Boolean = false,
val data: List = emptyList(),
+ val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val triggerEvent: () -> Unit,
+ private val isListenerStarted: MutableState
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is SensorState) return false
- if (isAvailable != other.isAvailable) return false
if (data != other.data) return false
+ if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (triggerEvent != other.triggerEvent) return false
+ if (isListenerStarted != other.isListenerStarted) return false
return true
}
override fun hashCode(): Int {
- var result = isAvailable.hashCode()
- result = 31 * result + data.hashCode()
+ var result = data.hashCode()
+ result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + triggerEvent.hashCode()
+ result = 31 * result + isListenerStarted.hashCode()
return result
}
@@ -54,67 +62,117 @@ internal class SensorState(
return "SensorState(isAvailable=$isAvailable, data=${data.joinToString()}, " +
"accuracy=$accuracy)"
}
+
+ /**
+ * Use this function to trigger the event request for sensors that emit One-Shot events (For
+ * example: [SignificantMotionSensorState]).
+ */
+ internal fun requestEventTrigger() = triggerEvent()
+
+ override fun startListening() {
+ isListenerStarted.value = true
+ }
+
+ override fun stopListening() {
+ isListenerStarted.value = false
+ }
}
@Composable
internal fun rememberSensorState(
sensorType: SensorType,
- sensorDelay: SensorDelay,
+ sensorDelay: SensorDelay? = null,
+ autoStart: Boolean = true,
onError: (throwable: Throwable) -> Unit,
+ onMotionEvent: (Long) -> Unit = {}
): SensorState {
+ val isListenerStarted = remember { mutableStateOf(autoStart) }
val isSensorAvailable = sensorType.rememberIsSensorAvailable()
val sensorData: MutableState> = remember { mutableStateOf(emptyList()) }
val sensorAccuracy: MutableState = remember { mutableStateOf(0) }
+ val sensorManager =
+ LocalContext.current.getSystemService(Context.SENSOR_SERVICE) as SensorManager
val sensorState = remember {
derivedStateOf {
SensorState(
isAvailable = isSensorAvailable,
data = sensorData.value,
accuracy = sensorAccuracy.value,
+ triggerEvent = {
+ runCatching {
+ val sensor =
+ sensorManager.getDefaultSensor(sensorType.toAndroidSensorType())
+ ?: throw SensorNotFoundException(sensorName = sensorType.name)
+
+ if (sensorType == SensorType.SignificantMotion) {
+ val triggerEventListener = object : TriggerEventListener() {
+ override fun onTrigger(event: TriggerEvent?) {
+ onMotionEvent(event?.timestamp ?: 0L)
+ }
+ }
+ sensorManager.requestTriggerSensor(triggerEventListener, sensor)
+ }
+ }.onFailure(onError)
+ },
+ isListenerStarted = isListenerStarted
)
}
}
runCatching {
- val sensorManager =
- LocalContext.current.getSystemService(Context.SENSOR_SERVICE) as SensorManager
-
val sensor = sensorManager.getDefaultSensor(sensorType.toAndroidSensorType())
?: throw SensorNotFoundException(sensorName = sensorType.name)
DisposableEffect(
key1 = sensor,
key2 = sensorDelay,
+ key3 = isListenerStarted.value,
effect = {
- val sensorEventListener = object : SensorEventListener {
- override fun onSensorChanged(event: SensorEvent?) {
- event?.let { nnEvent ->
- nnEvent.values?.let { nnValues ->
- sensorData.value = nnValues.toList()
+ var sensorEventListener: SensorEventListener? = null
+ if (isListenerStarted.value) {
+ sensorEventListener = object : SensorEventListener {
+ override fun onSensorChanged(event: SensorEvent?) {
+ event?.let { nnEvent ->
+ nnEvent.values?.let { nnValues ->
+ sensorData.value = nnValues.toList()
+ }
}
}
- }
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
- sensorAccuracy.value = accuracy
+ override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+ sensorAccuracy.value = accuracy
+ }
}
- }
- sensorManager.registerListener(
- sensorEventListener,
- sensor,
- sensorDelay.toAndroidSensorDelay(),
- )
+ sensorManager.registerListener(
+ sensorEventListener,
+ sensor,
+ (sensorDelay ?: SensorDelay.Normal).toAndroidSensorDelay()
+ )
+ }
onDispose {
sensorManager.unregisterListener(sensorEventListener)
+ isListenerStarted.value = false
}
- },
+ }
)
}.onFailure(onError)
return sensorState.value
}
+interface SensorStateListener {
+ /**
+ * Start listening to this sensor's values
+ */
+ fun startListening()
+
+ /**
+ * Stop listening to this sensor's values
+ */
+ fun stopListening()
+}
+
private class SensorNotFoundException(sensorName: String) :
Exception("The required sensor '$sensorName' was not found on the current device.")
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorType.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorType.kt
index e9b17ce..e66cec6 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorType.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorType.kt
@@ -43,7 +43,6 @@ sealed class SensorType(val name: String) {
object GyroscopeLimitedAxesUncalibrated : SensorType(name = "GyroscopeLimitedAxesUncalibrated")
object Heading : SensorType(name = "Heading")
- object All : SensorType(name = "All")
fun toAndroidSensorType(): Int {
return when (this) {
@@ -73,25 +72,30 @@ sealed class SensorType(val name: String) {
is LowLatencyOffBodyDetect -> checkApi(Build.VERSION_CODES.O) {
Sensor.TYPE_LOW_LATENCY_OFFBODY_DETECT
}
+
is AccelerometerUncalibrated -> checkApi(Build.VERSION_CODES.O) {
Sensor.TYPE_ACCELEROMETER_UNCALIBRATED
}
+
is HingeAngle -> checkApi(Build.VERSION_CODES.R) { Sensor.TYPE_HINGE_ANGLE }
is HeadTracker -> checkApi(Build.VERSION_CODES.TIRAMISU) { Sensor.TYPE_HEAD_TRACKER }
is AccelerometerLimitedAxes -> checkApi(Build.VERSION_CODES.TIRAMISU) {
Sensor.TYPE_ACCELEROMETER_LIMITED_AXES
}
+
is GyroscopeLimitedAxes -> checkApi(Build.VERSION_CODES.TIRAMISU) {
Sensor.TYPE_GYROSCOPE_LIMITED_AXES
}
+
is AccelerometerLimitedAxesUncalibrated -> checkApi(Build.VERSION_CODES.TIRAMISU) {
Sensor.TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED
}
+
is GyroscopeLimitedAxesUncalibrated -> checkApi(Build.VERSION_CODES.TIRAMISU) {
Sensor.TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED
}
+
is Heading -> checkApi(Build.VERSION_CODES.TIRAMISU) { Sensor.TYPE_HEADING }
- is All -> Sensor.TYPE_ALL
}
}
@@ -106,14 +110,18 @@ sealed class SensorType(val name: String) {
}
private inline fun checkApi(expectedApi: Int, block: () -> T): T {
- return if (Build.VERSION.SDK_INT < expectedApi) throw OldApiException(
- currentApi = Build.VERSION.SDK_INT,
- expectedApi = expectedApi
- ) else block()
+ return if (Build.VERSION.SDK_INT < expectedApi) {
+ throw OldApiException(
+ currentApi = Build.VERSION.SDK_INT,
+ expectedApi = expectedApi
+ )
+ } else {
+ block()
+ }
}
private class OldApiException(currentApi: Int, expectedApi: Int) :
Exception(
"The current API ($currentApi) is too low. At least API ($expectedApi) is required to use" +
- "this sensor."
+ "this sensor."
)
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/SignificantMotionSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/SignificantMotionSensorState.kt
new file mode 100644
index 0000000..d521607
--- /dev/null
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/SignificantMotionSensorState.kt
@@ -0,0 +1,130 @@
+package com.mutualmobile.composesensors
+
+import android.hardware.Sensor
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+
+/**
+ * A significant motion ([Sensor.TYPE_SIGNIFICANT_MOTION]) detector triggers when detecting a
+ * significant motion: a motion that might lead to a change in the user location.
+ *
+ * Examples of such significant motions are:
+ * - Walking or biking
+ * - Sitting in a moving car, coach, or train
+ *
+ * Examples of situations that don't trigger significant motion:
+ * - Phone in pocket and person isn't moving
+ * - Phone is on a table and the table shakes a bit due to nearby traffic or washing machine
+ *
+ * For more info, please refer the [Android Documentation Reference](https://source.android.com/docs/core/interaction/sensors/sensor-types#significant_motion)
+ *
+ * @param isListening Whether the app is currently listening to the motion event. Defaults to false.
+ * @param lastEventTimestamp Timestamp (in nanoseconds) of the last motion event. Defaults to 0L.
+ * @param isAvailable Whether the current device has a heart beat sensor. Defaults to false.
+ * @param accuracy Accuracy factor of the heart beat sensor. Defaults to 0.
+ */
+@Immutable
+class SignificantMotionSensorState internal constructor(
+ private val triggerEvent: () -> Unit = {},
+ val isListening: Boolean = false,
+ val lastEventTimestamp: Long = 0L,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: () -> Unit,
+ private val stopListeningEvents: () -> Unit
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is SignificantMotionSensorState) return false
+
+ if (isListening != other.isListening) return false
+ if (lastEventTimestamp != other.lastEventTimestamp) return false
+ if (triggerEvent != other.triggerEvent) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = triggerEvent.hashCode()
+ result = 31 * result + isListening.hashCode()
+ result = 31 * result + lastEventTimestamp.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "SignificantMotionSensorState(triggerEvent=$triggerEvent, " +
+ "isListening=$isListening, lastEventTimestamp=$lastEventTimestamp, " +
+ "isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ /**
+ * Since this sensor's reporting mode is One-Shot, therefore the user has to manually request a
+ * one-time event trigger and subscribe to it. Use this function to trigger such event and use
+ * onMotionEvent to subscribe to the event.
+ */
+ fun requestEventTrigger() = triggerEvent()
+
+ override fun startListening() = startListeningEvents()
+
+ override fun stopListening() = stopListeningEvents()
+}
+
+/**
+ * Creates and [remember]s an instance of [SignificantMotionSensorState].
+ * @param onMotionEvent Callback invoked every time the sensor detects a significant amount of
+ * device motion. Since this sensor's reporting mode is One-Shot, use
+ * [SignificantMotionSensorState.requestEventTrigger] to trigger the event emission and collect
+ * the value (timestamp in nanoseconds - defaults to 0L) of the event in this callback.
+ * @param onError Callback invoked on every error state.
+ */
+@Composable
+fun rememberSignificantMotionSensorState(
+ onMotionEvent: (timestamp: Long) -> Unit,
+ onError: (throwable: Throwable) -> Unit = {}
+): SignificantMotionSensorState {
+ val isListening = remember { mutableStateOf(false) }
+ val lastEventTimestamp = remember { mutableStateOf(0L) }
+
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.SignificantMotion,
+ onError = onError,
+ onMotionEvent = { timestampNs ->
+ onMotionEvent(timestampNs)
+ isListening.value = false
+ lastEventTimestamp.value = timestampNs
+ }
+ )
+
+ val significantMotionSensorState = remember(
+ sensorState,
+ isListening.value,
+ lastEventTimestamp.value
+ ) {
+ mutableStateOf(
+ SignificantMotionSensorState(
+ triggerEvent = {
+ sensorState.requestEventTrigger()
+ isListening.value = true
+ },
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ isListening = isListening.value,
+ lastEventTimestamp = lastEventTimestamp.value,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ )
+ }
+
+ return significantMotionSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/StationaryDetectSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/StationaryDetectSensorState.kt
new file mode 100644
index 0000000..496a003
--- /dev/null
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/StationaryDetectSensorState.kt
@@ -0,0 +1,108 @@
+package com.mutualmobile.composesensors
+
+import android.hardware.Sensor
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Immutable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+
+/**
+ * The Stationary Detect sensor ([Sensor.TYPE_STATIONARY_DETECT]) is used to detect whether the
+ * device is stationary or in motion. This sensor provides a binary output, with a value of either
+ * false or true, indicating the device's motion state.
+ *
+ * For more info, please refer the [Android Documentation Reference](https://developer.android.com/reference/android/hardware/SensorEvent#sensor.type_stationary_detect:)
+ *
+ * @param isDeviceStationary Whether the device is stationary or not. Defaults to false.
+ * @param isAvailable Whether the current device has a heart beat sensor. Defaults to false.
+ * @param accuracy Accuracy factor of the heart beat sensor. Defaults to 0.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+@Immutable
+class StationaryDetectSensorState internal constructor(
+ val isDeviceStationary: Boolean = false,
+ val isAvailable: Boolean = false,
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other !is StationaryDetectSensorState) return false
+
+ if (isDeviceStationary != other.isDeviceStationary) return false
+ if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = isDeviceStationary.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
+ return result
+ }
+
+ override fun toString(): String {
+ return "StationaryDetectSensorState(isDeviceStationary=$isDeviceStationary," +
+ " isAvailable=$isAvailable, accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
+}
+
+/**
+ * Creates and [remember]s an instance of [StationaryDetectSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received.
+ * Defaults to [SensorDelay.Normal].
+ * @param onError Callback invoked on every error state.
+ */
+@RequiresApi(Build.VERSION_CODES.N)
+@Composable
+fun rememberStationaryDetectSensorState(
+ autoStart: Boolean = true,
+ sensorDelay: SensorDelay = SensorDelay.Normal,
+ onError: (throwable: Throwable) -> Unit = {}
+): StationaryDetectSensorState {
+ val sensorState = rememberSensorState(
+ sensorType = SensorType.StationaryDetect,
+ sensorDelay = sensorDelay,
+ autoStart = autoStart,
+ onError = onError
+ )
+ val confidenceSensorState = remember { mutableStateOf(StationaryDetectSensorState()) }
+
+ LaunchedEffect(
+ key1 = sensorState,
+ block = {
+ val sensorStateValues = sensorState.data
+ if (sensorStateValues.isNotEmpty()) {
+ confidenceSensorState.value = StationaryDetectSensorState(
+ isDeviceStationary = sensorStateValues[0] == 1f,
+ isAvailable = sensorState.isAvailable,
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
+ )
+ }
+ }
+ )
+
+ return confidenceSensorState.value
+}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/StepCounterSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/StepCounterSensorState.kt
index bf7abf3..f299c9c 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/StepCounterSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/StepCounterSensorState.kt
@@ -18,7 +18,9 @@ class StepCounterSensorState internal constructor(
val stepCount: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is StepCounterSensorState) return false
@@ -26,6 +28,8 @@ class StepCounterSensorState internal constructor(
if (stepCount != other.stepCount) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -34,29 +38,44 @@ class StepCounterSensorState internal constructor(
var result = stepCount.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "StepCounterSensorState(stepCount=$stepCount isAvailable=$isAvailable, accuracy=$accuracy)"
+ return "StepCounterSensorState(stepCount=$stepCount isAvailable=$isAvailable," +
+ " accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [StepCounterSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberStepCounterSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): StepCounterSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.StepCounter,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val stepCounterSensorState = remember { mutableStateOf(StepCounterSensorState()) }
@@ -69,9 +88,11 @@ fun rememberStepCounterSensorState(
stepCount = sensorStateValues[0],
isAvailable = sensorState.isAvailable,
accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
- },
+ }
)
return stepCounterSensorState.value
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/StepDetectorSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/StepDetectorSensorState.kt
index 085af17..d8b2fa6 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/StepDetectorSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/StepDetectorSensorState.kt
@@ -18,7 +18,9 @@ class StepDetectorSensorState internal constructor(
val stepCount: Float = 0f,
val isAvailable: Boolean = false,
val accuracy: Int = 0,
-) {
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is StepDetectorSensorState) return false
@@ -26,6 +28,8 @@ class StepDetectorSensorState internal constructor(
if (stepCount != other.stepCount) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -34,29 +38,44 @@ class StepDetectorSensorState internal constructor(
var result = stepCount.hashCode()
result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "StepDetectorSensorState(stepCount=$stepCount isAvailable=$isAvailable, accuracy=$accuracy)"
+ return "StepDetectorSensorState(stepCount=$stepCount isAvailable=$isAvailable," +
+ " accuracy=$accuracy)"
+ }
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
}
}
/**
* Creates and [remember]s an instance of [StepDetectorSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberStepDetectorSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
- onError: (throwable: Throwable) -> Unit = {},
+ onError: (throwable: Throwable) -> Unit = {}
): StepDetectorSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.StepDetector,
sensorDelay = sensorDelay,
- onError = onError,
+ autoStart = autoStart,
+ onError = onError
)
val stepDetectorSensorState = remember { mutableStateOf(StepDetectorSensorState()) }
@@ -69,9 +88,11 @@ fun rememberStepDetectorSensorState(
stepCount = sensorStateValues[0],
isAvailable = sensorState.isAvailable,
accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
- },
+ }
)
return stepDetectorSensorState.value
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedAccelerometerSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedAccelerometerSensorState.kt
index a6bb276..8d8dbe7 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedAccelerometerSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedAccelerometerSensorState.kt
@@ -39,8 +39,10 @@ class UncalibratedAccelerometerSensorState internal constructor(
val yBias: Float = 0f,
val zBias: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UncalibratedAccelerometerSensorState) return false
@@ -53,6 +55,8 @@ class UncalibratedAccelerometerSensorState internal constructor(
if (zBias != other.zBias) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -64,8 +68,10 @@ class UncalibratedAccelerometerSensorState internal constructor(
result = 31 * result + xBias.hashCode()
result = 31 * result + yBias.hashCode()
result = 31 * result + zBias.hashCode()
- result = 31 * result + isAvailable.hashCode()
result = 31 * result + accuracy.hashCode()
+ result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -74,24 +80,35 @@ class UncalibratedAccelerometerSensorState internal constructor(
"zForce=$zForce, xBiased=$xBias, yBiased=$yBias, zBiased=$zBias," +
"isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of
* [UncalibratedAccelerometerSensorState].
- *
- * @param sensorDelay The rate at which the raw sensor data should be
- * received. Defaults to [SensorDelay.Normal].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received. Defaults to
+ * [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberUncalibratedAccelerometerSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): UncalibratedAccelerometerSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.AccelerometerUncalibrated,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val uncalibratedAccelerometerSensorState =
@@ -110,7 +127,9 @@ fun rememberUncalibratedAccelerometerSensorState(
yBias = sensorStateValues[4],
zBias = sensorStateValues[5],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedGyroscopeSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedGyroscopeSensorState.kt
index 81f59de..bd9b90d 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedGyroscopeSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedGyroscopeSensorState.kt
@@ -31,8 +31,10 @@ class UncalibratedGyroscopeSensorState internal constructor(
val yBias: Float = 0f,
val zBias: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UncalibratedGyroscopeSensorState) return false
@@ -44,6 +46,9 @@ class UncalibratedGyroscopeSensorState internal constructor(
if (yBias != other.yBias) return false
if (zBias != other.zBias) return false
if (isAvailable != other.isAvailable) return false
+ if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -56,6 +61,9 @@ class UncalibratedGyroscopeSensorState internal constructor(
result = 31 * result + yBias.hashCode()
result = 31 * result + zBias.hashCode()
result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -64,23 +72,34 @@ class UncalibratedGyroscopeSensorState internal constructor(
"zRotation=$zRotation," + "xBias=$xBias," + "yBias=$yBias," + "zBias=$zBias," +
" isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [GyroscopeSensorState].
- *
- * @param sensorDelay The rate at which the raw sensor data should be
- * received. Defaults to [SensorDelay.Normal].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received. Defaults to
+ * [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberUncalibratedGyroscopeSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): UncalibratedGyroscopeSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.GyroscopeUncalibrated,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
val uncalibratedGyroscopeSensorState =
@@ -99,7 +118,9 @@ fun rememberUncalibratedGyroscopeSensorState(
yBias = sensorStateValues[4],
zBias = sensorStateValues[5],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedLimitedAxesAccelerometerSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedLimitedAxesAccelerometerSensorState.kt
index f680709..9921b7c 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedLimitedAxesAccelerometerSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedLimitedAxesAccelerometerSensorState.kt
@@ -33,8 +33,10 @@ class UncalibratedLimitedAxesAccelerometerSensorState internal constructor(
val yAxisSupported: Boolean = false,
val zAxisSupported: Boolean = false,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UncalibratedLimitedAxesAccelerometerSensorState) return false
@@ -50,6 +52,8 @@ class UncalibratedLimitedAxesAccelerometerSensorState internal constructor(
if (zAxisSupported != other.zAxisSupported) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -64,8 +68,10 @@ class UncalibratedLimitedAxesAccelerometerSensorState internal constructor(
result = 31 * result + xAxisSupported.hashCode()
result = 31 * result + yAxisSupported.hashCode()
result = 31 * result + zAxisSupported.hashCode()
+ result = 31 * result + accuracy.hashCode()
result = 31 * result + isAvailable.hashCode()
- result = 31 * result + accuracy
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
@@ -75,25 +81,38 @@ class UncalibratedLimitedAxesAccelerometerSensorState internal constructor(
"xAxisSupported=$xAxisSupported, yAxisSupported=$yAxisSupported, " +
"zAxisSupported=$zAxisSupported, isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [UncalibratedLimitedAxesAccelerometerSensorState].
- * @param sensorDelay The rate at which the raw sensor data should be received.
- * Defaults to [SensorDelay.Normal].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
+ * @param sensorDelay The rate at which the raw sensor data should be received. Defaults to
+ * [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberUncalibratedLimitedAxesAccelerometerSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): UncalibratedLimitedAxesAccelerometerSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.AccelerometerLimitedAxesUncalibrated,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
- val accelerometerSensorState = remember { mutableStateOf(UncalibratedLimitedAxesAccelerometerSensorState()) }
+ val accelerometerSensorState =
+ remember { mutableStateOf(UncalibratedLimitedAxesAccelerometerSensorState()) }
LaunchedEffect(
key1 = sensorState,
@@ -111,7 +130,9 @@ fun rememberUncalibratedLimitedAxesAccelerometerSensorState(
yAxisSupported = sensorStateValues[7] != 0f,
zAxisSupported = sensorStateValues[8] != 0f,
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedMagneticFieldSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedMagneticFieldSensorState.kt
index 81f56c4..f5a065c 100644
--- a/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedMagneticFieldSensorState.kt
+++ b/composesensors/src/main/java/com/mutualmobile/composesensors/UncalibratedMagneticFieldSensorState.kt
@@ -28,8 +28,10 @@ class UncalibratedMagneticFieldSensorState internal constructor(
val yBias: Float = 0f,
val zBias: Float = 0f,
val isAvailable: Boolean = false,
- val accuracy: Int = 0
-) {
+ val accuracy: Int = 0,
+ private val startListeningEvents: (() -> Unit)? = null,
+ private val stopListeningEvents: (() -> Unit)? = null
+) : SensorStateListener {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is UncalibratedMagneticFieldSensorState) return false
@@ -42,6 +44,8 @@ class UncalibratedMagneticFieldSensorState internal constructor(
if (zBias != other.zBias) return false
if (isAvailable != other.isAvailable) return false
if (accuracy != other.accuracy) return false
+ if (startListeningEvents != other.startListeningEvents) return false
+ if (stopListeningEvents != other.stopListeningEvents) return false
return true
}
@@ -53,34 +57,49 @@ class UncalibratedMagneticFieldSensorState internal constructor(
result = 31 * result + xBias.hashCode()
result = 31 * result + yBias.hashCode()
result = 31 * result + zBias.hashCode()
- result = 31 * result + accuracy.hashCode()
result = 31 * result + isAvailable.hashCode()
+ result = 31 * result + accuracy.hashCode()
+ result = 31 * result + startListeningEvents.hashCode()
+ result = 31 * result + stopListeningEvents.hashCode()
return result
}
override fun toString(): String {
- return "UncalibratedMagneticFieldSensorState(xStrength=$xStrength, yStrength=$yStrength, zStrength=$zStrength, " +
- "xBias=$xBias, yBias=$yBias, zBias=$zBias, " +
+ return "UncalibratedMagneticFieldSensorState(xStrength=$xStrength, yStrength=$yStrength," +
+ " zStrength=$zStrength, xBias=$xBias, yBias=$yBias, zBias=$zBias, " +
"isAvailable=$isAvailable, accuracy=$accuracy)"
}
+
+ override fun startListening() {
+ startListeningEvents?.invoke()
+ }
+
+ override fun stopListening() {
+ stopListeningEvents?.invoke()
+ }
}
/**
* Creates and [remember]s an instance of [UncalibratedMagneticFieldSensorState].
+ * @param autoStart Start listening to sensor events as soon as sensor state is initialised.
+ * Defaults to true.
* @param sensorDelay The rate at which the raw sensor data should be received.
* Defaults to [SensorDelay.Normal].
* @param onError Callback invoked on every error state.
*/
@Composable
fun rememberUncalibratedMagneticFieldSensorState(
+ autoStart: Boolean = true,
sensorDelay: SensorDelay = SensorDelay.Normal,
onError: (throwable: Throwable) -> Unit = {}
): UncalibratedMagneticFieldSensorState {
val sensorState = rememberSensorState(
sensorType = SensorType.MagneticFieldUncalibrated,
sensorDelay = sensorDelay,
+ autoStart = autoStart,
onError = onError
)
+
val uncalibratedMagneticFieldSensorState =
remember { mutableStateOf(UncalibratedMagneticFieldSensorState()) }
@@ -97,7 +116,9 @@ fun rememberUncalibratedMagneticFieldSensorState(
yBias = sensorStateValues[4],
zBias = sensorStateValues[5],
isAvailable = sensorState.isAvailable,
- accuracy = sensorState.accuracy
+ accuracy = sensorState.accuracy,
+ startListeningEvents = sensorState::startListening,
+ stopListeningEvents = sensorState::stopListening
)
}
}
diff --git a/sample/src/main/java/com/mutualmobile/sample/ui/screens/sensorlist/SensorsListScreen.kt b/sample/src/main/java/com/mutualmobile/sample/ui/screens/sensorlist/SensorsListScreen.kt
index 765a718..e41690a 100644
--- a/sample/src/main/java/com/mutualmobile/sample/ui/screens/sensorlist/SensorsListScreen.kt
+++ b/sample/src/main/java/com/mutualmobile/sample/ui/screens/sensorlist/SensorsListScreen.kt
@@ -30,27 +30,69 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
+import com.mutualmobile.composesensors.AccelerometerSensorState
+import com.mutualmobile.composesensors.AmbientTemperatureSensorState
+import com.mutualmobile.composesensors.GameRotationVectorSensorState
+import com.mutualmobile.composesensors.GeomagneticRotationVectorSensorState
+import com.mutualmobile.composesensors.GravitySensorState
+import com.mutualmobile.composesensors.GyroscopeSensorState
+import com.mutualmobile.composesensors.HeadTrackerSensorState
+import com.mutualmobile.composesensors.HeadingSensorState
+import com.mutualmobile.composesensors.HeartBeatSensorState
+import com.mutualmobile.composesensors.HeartRateSensorState
+import com.mutualmobile.composesensors.HingeAngleSensorState
+import com.mutualmobile.composesensors.LightSensorState
+import com.mutualmobile.composesensors.LimitedAxesAccelerometerSensorState
+import com.mutualmobile.composesensors.LimitedAxesGyroscopeSensorState
+import com.mutualmobile.composesensors.LinearAccelerationSensorState
+import com.mutualmobile.composesensors.LowLatencyOffBodyDetectSensorState
+import com.mutualmobile.composesensors.MagneticFieldSensorState
+import com.mutualmobile.composesensors.MotionDetectSensorState
+import com.mutualmobile.composesensors.PressureSensorState
+import com.mutualmobile.composesensors.ProximitySensorState
+import com.mutualmobile.composesensors.RotationVectorSensorState
+import com.mutualmobile.composesensors.SignificantMotionSensorState
+import com.mutualmobile.composesensors.StationaryDetectSensorState
+import com.mutualmobile.composesensors.StepCounterSensorState
+import com.mutualmobile.composesensors.StepDetectorSensorState
+import com.mutualmobile.composesensors.UncalibratedAccelerometerSensorState
+import com.mutualmobile.composesensors.UncalibratedGyroscopeSensorState
+import com.mutualmobile.composesensors.UncalibratedLimitedAxesAccelerometerSensorState
+import com.mutualmobile.composesensors.UncalibratedMagneticFieldSensorState
import com.mutualmobile.composesensors.rememberAccelerometerSensorState
import com.mutualmobile.composesensors.rememberAmbientTemperatureSensorState
import com.mutualmobile.composesensors.rememberGameRotationVectorSensorState
+import com.mutualmobile.composesensors.rememberGeomagneticRotationVectorSensorState
import com.mutualmobile.composesensors.rememberGravitySensorState
import com.mutualmobile.composesensors.rememberGyroscopeSensorState
+import com.mutualmobile.composesensors.rememberHeadTrackerSensorState
+import com.mutualmobile.composesensors.rememberHeadingSensorState
+import com.mutualmobile.composesensors.rememberHeartBeatSensorState
import com.mutualmobile.composesensors.rememberHeartRateSensorState
import com.mutualmobile.composesensors.rememberHingeAngleSensorState
import com.mutualmobile.composesensors.rememberLightSensorState
+import com.mutualmobile.composesensors.rememberLimitedAxesAccelerometerSensorState
import com.mutualmobile.composesensors.rememberLimitedAxesGyroscopeSensorState
import com.mutualmobile.composesensors.rememberLinearAccelerationSensorState
import com.mutualmobile.composesensors.rememberLowLatencyOffBodyDetectSensorState
import com.mutualmobile.composesensors.rememberMagneticFieldSensorState
+import com.mutualmobile.composesensors.rememberMotionDetectSensorState
import com.mutualmobile.composesensors.rememberPressureSensorState
import com.mutualmobile.composesensors.rememberProximitySensorState
import com.mutualmobile.composesensors.rememberRotationVectorSensorState
+import com.mutualmobile.composesensors.rememberSignificantMotionSensorState
+import com.mutualmobile.composesensors.rememberStationaryDetectSensorState
+import com.mutualmobile.composesensors.rememberStepCounterSensorState
+import com.mutualmobile.composesensors.rememberStepDetectorSensorState
+import com.mutualmobile.composesensors.rememberUncalibratedAccelerometerSensorState
+import com.mutualmobile.composesensors.rememberUncalibratedGyroscopeSensorState
+import com.mutualmobile.composesensors.rememberUncalibratedLimitedAxesAccelerometerSensorState
import com.mutualmobile.composesensors.rememberUncalibratedMagneticFieldSensorState
import com.mutualmobile.sample.R
import com.mutualmobile.sample.ui.screens.sensorlist.components.CSButton
@@ -59,6 +101,44 @@ import com.mutualmobile.sample.ui.screens.sensorlist.components.SensorItem
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
+val sensorStates: List
+ @SuppressLint("NewApi")
+ @Composable
+ get() = listOf(
+ rememberAccelerometerSensorState(),
+ rememberMagneticFieldSensorState(),
+ rememberGyroscopeSensorState(),
+ rememberLightSensorState(),
+ rememberPressureSensorState(),
+ rememberProximitySensorState(),
+ rememberGravitySensorState(),
+ rememberLinearAccelerationSensorState(),
+ rememberRotationVectorSensorState(),
+ rememberAmbientTemperatureSensorState(),
+ rememberUncalibratedMagneticFieldSensorState(),
+ rememberGameRotationVectorSensorState(),
+ rememberUncalibratedGyroscopeSensorState(),
+ rememberSignificantMotionSensorState(onMotionEvent = {}),
+ rememberStepDetectorSensorState(),
+ rememberStepCounterSensorState(),
+ rememberGeomagneticRotationVectorSensorState(),
+ rememberHeartRateSensorState(),
+ rememberStationaryDetectSensorState(),
+ rememberMotionDetectSensorState(),
+ rememberHeartBeatSensorState(),
+ rememberLowLatencyOffBodyDetectSensorState(),
+ rememberUncalibratedAccelerometerSensorState(),
+ rememberHingeAngleSensorState(),
+ rememberHeadTrackerSensorState(),
+ rememberLimitedAxesAccelerometerSensorState(),
+ rememberLimitedAxesGyroscopeSensorState(),
+ rememberUncalibratedLimitedAxesAccelerometerSensorState(),
+ rememberHeadingSensorState()
+ )
+
+// Please note this sample app will only work on Android 13 and above because we're also consuming
+// sensor data that is only available on Android 13 and above and not making API level checks
+// for every sensor yet.
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter", "NewApi")
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
@@ -66,25 +146,7 @@ fun SensorsListScreen() {
var isTopBarTitleCollapsed by remember { mutableStateOf(false) }
val pagerState = rememberPagerState()
val coroutineScope = rememberCoroutineScope()
-
- // TODO: Keep this updated until a better option is found
- val totalPageCount = rememberSaveable { 16 }
- val accelerometerState = rememberAccelerometerSensorState()
- val magneticFieldState = rememberMagneticFieldSensorState()
- val gyroscopeState = rememberGyroscopeSensorState()
- val lightState = rememberLightSensorState()
- val pressureState = rememberPressureSensorState()
- val proximityState = rememberProximitySensorState()
- val gravityState = rememberGravitySensorState()
- val linearAccelerationState = rememberLinearAccelerationSensorState()
- val rotationVectorState = rememberRotationVectorSensorState()
- val ambientTemperatureState = rememberAmbientTemperatureSensorState()
- val uncalibratedMagneticFieldState = rememberUncalibratedMagneticFieldSensorState()
- val gameRotationVectorState = rememberGameRotationVectorSensorState()
- val heartRateState = rememberHeartRateSensorState()
- val lowLatencyOffBodyDetectState = rememberLowLatencyOffBodyDetectSensorState()
- val hingeAngleState = rememberHingeAngleSensorState()
- val limitedAxesGyroscopeState = rememberLimitedAxesGyroscopeSensorState()
+ val totalSensorsCount = rememberUpdatedState(sensorStates.size)
// Trigger TopBar animation once
LaunchedEffect(Unit) {
@@ -160,17 +222,17 @@ fun SensorsListScreen() {
},
onLongClick = {
coroutineScope.launch {
- pagerState.scrollToPage(totalPageCount)
+ pagerState.scrollToPage(totalSensorsCount.value - 1)
}
},
position = CSButtonPosition.End,
- enabled = pagerState.currentPage != totalPageCount - 1
+ enabled = pagerState.currentPage != totalSensorsCount.value - 1
)
}
}
) {
HorizontalPager(
- pageCount = totalPageCount,
+ pageCount = totalSensorsCount.value,
state = pagerState,
contentPadding = PaddingValues(32.dp),
beyondBoundsPageCount = 1,
@@ -181,145 +243,391 @@ fun SensorsListScreen() {
pagerState.currentOffsetForPage(index)
}
}
- SensorItem(
- name = when (index) {
- 0 -> "Accelerometer"
- 1 -> "Magnetic Field"
- 2 -> "Gyroscope"
- 3 -> "Light"
- 4 -> "Pressure"
- 5 -> "Proximity"
- 6 -> "Gravity"
- 7 -> "Linear Acceleration"
- 8 -> "Rotation Vector"
- 9 -> "Ambient Temperature"
- 10 -> "Magnetic Field (Uncalibrated)"
- 11 -> "Game Rotation Vector"
- 12 -> "Heart Rate"
- 13 -> "Low Latency Off-Body Detection"
- 14 -> "Hinge Angle"
- 15 -> "Gyroscope (Limited Axes)"
- else -> error("Invalid index '$index'")
- },
- scrollProgress = scrollProgress,
- imageRef = when (index) {
- 0 -> R.drawable.accelerometer
- 1 -> R.drawable.magnetic_field
- 2 -> R.drawable.gyroscope
- 3 -> R.drawable.light
- 4 -> R.drawable.pressure
- 5 -> R.drawable.proximity
- 6 -> R.drawable.gravity
- 7 -> R.drawable.linear_acceleration
- 8 -> R.drawable.rotation_vector
- 9 -> R.drawable.ambient_temperature
- 10 -> R.drawable.magnetic_field
- 11 -> R.drawable.game_rotation_vector
- 12 -> R.drawable.heart_rate
- 13 -> R.drawable.low_latency_off_body_detect
- 14 -> R.drawable.hinge_angle
- 15 -> R.drawable.gyroscope
- else -> error("Invalid index '$index'")
- },
- sensorValues = when (index) {
- 0 -> mapOf(
- "Force X" to accelerometerState.xForce,
- "Force Y" to accelerometerState.yForce,
- "Force Z" to accelerometerState.zForce
+ when (val state = sensorStates[index]) {
+ is AccelerometerSensorState -> {
+ SensorItem(
+ name = "Accelerometer",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.accelerometer,
+ sensorValues = mapOf(
+ "Force X" to state.xForce,
+ "Force Y" to state.yForce,
+ "Force Z" to state.zForce
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 1 -> mapOf(
- "Strength X" to magneticFieldState.xStrength,
- "Strength Y" to magneticFieldState.yStrength,
- "Strength Z" to magneticFieldState.zStrength
+ is MagneticFieldSensorState -> {
+ SensorItem(
+ name = "Magnetic Field",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.magnetic_field,
+ sensorValues = mapOf(
+ "Strength X" to state.xStrength,
+ "Strength Y" to state.yStrength,
+ "Strength Z" to state.zStrength
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 2 -> mapOf(
- "Rotation X" to gyroscopeState.xRotation,
- "Rotation Y" to gyroscopeState.yRotation,
- "Rotation Z" to gyroscopeState.zRotation
+ is GyroscopeSensorState -> {
+ SensorItem(
+ name = "Gyroscope",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.gyroscope,
+ sensorValues = mapOf(
+ "Rotation X" to state.xRotation,
+ "Rotation Y" to state.yRotation,
+ "Rotation Z" to state.zRotation
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 3 -> mapOf("Illuminance" to lightState.illuminance)
+ is LightSensorState -> {
+ SensorItem(
+ name = "Light",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.light,
+ sensorValues = mapOf("Illuminance" to state.illuminance),
+ isAvailable = state.isAvailable
+ )
+ }
- 4 -> mapOf("Pressure" to pressureState.pressure)
+ is PressureSensorState -> {
+ SensorItem(
+ name = "Pressure",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.pressure,
+ sensorValues = mapOf("Pressure" to state.pressure),
+ isAvailable = state.isAvailable
+ )
+ }
- 5 -> mapOf("Distance" to proximityState.sensorDistance)
+ is ProximitySensorState -> {
+ SensorItem(
+ name = "Proximity",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.proximity,
+ sensorValues = mapOf("Distance" to state.sensorDistance),
+ isAvailable = state.isAvailable
+ )
+ }
- 6 -> mapOf(
- "Force X" to gravityState.xForce,
- "Force Y" to gravityState.yForce,
- "Force Z" to gravityState.zForce
+ is GravitySensorState -> {
+ SensorItem(
+ name = "Gravity",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.gravity,
+ sensorValues = mapOf(
+ "Force X" to state.xForce,
+ "Force Y" to state.yForce,
+ "Force Z" to state.zForce
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 7 -> mapOf(
- "Force X" to linearAccelerationState.xForce,
- "Force Y" to linearAccelerationState.yForce,
- "Force Z" to linearAccelerationState.zForce
+ is LinearAccelerationSensorState -> {
+ SensorItem(
+ name = "Linear Acceleration",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.linear_acceleration,
+ sensorValues = mapOf(
+ "Force X" to state.xForce,
+ "Force Y" to state.yForce,
+ "Force Z" to state.zForce
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 8 -> mapOf(
- "Vector X" to rotationVectorState.vectorX,
- "Vector Y" to rotationVectorState.vectorY,
- "Vector Z" to rotationVectorState.vectorZ,
- "Scalar" to rotationVectorState.scalar,
- "Heading Accuracy" to rotationVectorState.estimatedHeadingAccuracy
+ is RotationVectorSensorState -> {
+ SensorItem(
+ name = "Rotation Vector",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.rotation_vector,
+ sensorValues = mapOf(
+ "Vector X" to state.vectorX,
+ "Vector Y" to state.vectorY,
+ "Vector Z" to state.vectorZ,
+ "Scalar" to state.scalar,
+ "Heading Accuracy" to state.estimatedHeadingAccuracy
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 9 -> mapOf("Temperature" to ambientTemperatureState.temperature)
+ is AmbientTemperatureSensorState -> {
+ SensorItem(
+ name = "Ambient Temperature",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.ambient_temperature,
+ sensorValues = mapOf("Temperature" to state.temperature),
+ isAvailable = state.isAvailable
+ )
+ }
- 10 -> mapOf(
- "Strength X" to uncalibratedMagneticFieldState.xStrength,
- "Strength Y" to uncalibratedMagneticFieldState.yStrength,
- "Strength Z" to uncalibratedMagneticFieldState.zStrength,
- "Bias X" to uncalibratedMagneticFieldState.xBias,
- "Bias Y" to uncalibratedMagneticFieldState.yBias,
- "Bias Z" to uncalibratedMagneticFieldState.zBias
+ is UncalibratedMagneticFieldSensorState -> {
+ SensorItem(
+ name = "Magnetic Field (Uncalibrated)",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.magnetic_field,
+ sensorValues = mapOf(
+ "Strength X" to state.xStrength,
+ "Strength Y" to state.yStrength,
+ "Strength Z" to state.zStrength,
+ "Bias X" to state.xBias,
+ "Bias Y" to state.yBias,
+ "Bias Z" to state.zBias
+ ),
+ isAvailable = state.isAvailable
)
+ }
- 11 -> mapOf(
- "Vector X" to gameRotationVectorState.vectorX,
- "Vector Y" to gameRotationVectorState.vectorY,
- "Vector Z" to gameRotationVectorState.vectorZ
+ is GameRotationVectorSensorState -> {
+ SensorItem(
+ name = "Game Rotation Vector",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.game_rotation_vector,
+ sensorValues = mapOf(
+ "Vector X" to state.vectorX,
+ "Vector Y" to state.vectorY,
+ "Vector Z" to state.vectorZ
+ ),
+ isAvailable = state.isAvailable
)
+ }
+
+ is UncalibratedGyroscopeSensorState -> {
+ SensorItem(
+ name = "Gyroscope (Uncalibrated)",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.gyroscope,
+ sensorValues = mapOf(
+ "Rotation X" to state.xRotation,
+ "Rotation Y" to state.yRotation,
+ "Rotation Z" to state.zRotation,
+ "Bias X" to state.xBias,
+ "Bias Y" to state.yBias,
+ "Bias Z" to state.zBias
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
- 12 -> mapOf("Heart Rate" to heartRateState.heartRate)
+ is SignificantMotionSensorState -> {
+ // Trigger motion event whenever its card is selected
+ LaunchedEffect(Unit) {
+ state.requestEventTrigger()
+ }
+
+ SensorItem(
+ name = "Significant Motion",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.significant_motion,
+ sensorValues = mapOf(
+ "Listening?" to state.isListening,
+ "Timestamp (ns)" to state.lastEventTimestamp
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
- 13 -> mapOf("Is On Body?" to lowLatencyOffBodyDetectState.isDeviceOnBody)
+ is StepDetectorSensorState -> {
+ SensorItem(
+ name = "Step Detector",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.step_detection,
+ sensorValues = mapOf("Step Count" to state.stepCount),
+ isAvailable = state.isAvailable
+ )
+ }
- 14 -> mapOf("Angle" to hingeAngleState.angle)
+ is StepCounterSensorState -> {
+ SensorItem(
+ name = "Step Counter",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.step_counter,
+ sensorValues = mapOf("Step Count" to state.stepCount),
+ isAvailable = state.isAvailable
+ )
+ }
- 15 -> mapOf(
- "Rotation X" to limitedAxesGyroscopeState.xRotation,
- "Rotation Y" to limitedAxesGyroscopeState.yRotation,
- "Rotation Z" to limitedAxesGyroscopeState.zRotation,
- "X Supported?" to limitedAxesGyroscopeState.xAxisSupported,
- "Y Supported?" to limitedAxesGyroscopeState.yAxisSupported,
- "Z Supported?" to limitedAxesGyroscopeState.zAxisSupported
+ is GeomagneticRotationVectorSensorState -> {
+ SensorItem(
+ name = "Geomagnetic Rotation Vector",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.geomagnetic_rotation_vector,
+ sensorValues = mapOf(
+ "Vector X" to state.vectorX,
+ "Vector Y" to state.vectorY,
+ "Vector Z" to state.vectorZ
+ ),
+ isAvailable = state.isAvailable
)
+ }
- else -> error("Invalid index '$index'")
- },
- isAvailable = when (index) {
- 0 -> accelerometerState.isAvailable
- 1 -> magneticFieldState.isAvailable
- 2 -> gyroscopeState.isAvailable
- 3 -> lightState.isAvailable
- 4 -> pressureState.isAvailable
- 5 -> proximityState.isAvailable
- 6 -> gravityState.isAvailable
- 7 -> linearAccelerationState.isAvailable
- 8 -> rotationVectorState.isAvailable
- 9 -> ambientTemperatureState.isAvailable
- 10 -> uncalibratedMagneticFieldState.isAvailable
- 11 -> gameRotationVectorState.isAvailable
- 12 -> heartRateState.isAvailable
- 13 -> lowLatencyOffBodyDetectState.isAvailable
- 14 -> hingeAngleState.isAvailable
- 15 -> limitedAxesGyroscopeState.isAvailable
- else -> error("Invalid index '$index'")
+ is HeartRateSensorState -> {
+ SensorItem(
+ name = "Heart Rate",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.heart_rate,
+ sensorValues = mapOf("Heart Rate" to state.heartRate),
+ isAvailable = state.isAvailable
+ )
}
- )
+
+ is StationaryDetectSensorState -> {
+ SensorItem(
+ name = "Stationary Detect",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.stationary_detect,
+ sensorValues = mapOf("Is device stationary?" to state.isDeviceStationary),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is MotionDetectSensorState -> {
+ SensorItem(
+ name = "Motion Detect",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.motion_detect,
+ sensorValues = mapOf("Is device moving?" to state.isDeviceInMotion),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is HeartBeatSensorState -> {
+ SensorItem(
+ name = "Heart Beat",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.heart_rate,
+ sensorValues = mapOf("Is confident peak?" to state.isConfidentPeak),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is LowLatencyOffBodyDetectSensorState -> {
+ SensorItem(
+ name = "Low Latency Off-Body Detect",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.low_latency_off_body_detect,
+ sensorValues = mapOf("Is On Body?" to state.isDeviceOnBody),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is UncalibratedAccelerometerSensorState -> {
+ SensorItem(
+ name = "Accelerometer (Uncalibrated)",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.accelerometer,
+ sensorValues = mapOf(
+ "Force X" to state.xForce,
+ "Force Y" to state.yForce,
+ "Force Z" to state.zForce,
+ "Bias X" to state.xBias,
+ "Bias Y" to state.yBias,
+ "Bias Z" to state.zBias
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is HingeAngleSensorState -> {
+ SensorItem(
+ name = "Hinge Angle",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.hinge_angle,
+ sensorValues = mapOf("Angle" to state.angle),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is HeadTrackerSensorState -> {
+ SensorItem(
+ name = "Head Tracker",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.head_tracker,
+ sensorValues = mapOf(
+ "Rotation X" to state.xRotation,
+ "Rotation Y" to state.yRotation,
+ "Rotation Z" to state.zRotation,
+ "Velocity X" to state.xAngularVelocity,
+ "Velocity Y" to state.yAngularVelocity,
+ "Velocity Z" to state.zAngularVelocity
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is LimitedAxesAccelerometerSensorState -> {
+ SensorItem(
+ name = "Accelerometer (Limited Axes)",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.accelerometer,
+ sensorValues = mapOf(
+ "Force X" to state.xForce,
+ "Force Y" to state.yForce,
+ "Force Z" to state.zForce,
+ "X Supported?" to state.xAxisSupported,
+ "Y Supported?" to state.yAxisSupported,
+ "Z Supported?" to state.zAxisSupported
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is LimitedAxesGyroscopeSensorState -> {
+ SensorItem(
+ name = "Gyroscope (Limited Axes)",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.gyroscope,
+ sensorValues = mapOf(
+ "Rotation X" to state.xRotation,
+ "Rotation Y" to state.yRotation,
+ "Rotation Z" to state.zRotation,
+ "X Supported?" to state.xAxisSupported,
+ "Y Supported?" to state.yAxisSupported,
+ "Z Supported?" to state.zAxisSupported
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is UncalibratedLimitedAxesAccelerometerSensorState -> {
+ SensorItem(
+ name = "Accelerometer (Uncalibrated - Limited Axes)",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.accelerometer,
+ sensorValues = mapOf(
+ "Rotation X" to state.xForce,
+ "Rotation Y" to state.yForce,
+ "Rotation Z" to state.zForce,
+ "Bias X" to state.xBias,
+ "Bias Y" to state.yBias,
+ "Bias Z" to state.zBias,
+ "X Supported?" to state.xAxisSupported,
+ "Y Supported?" to state.yAxisSupported,
+ "Z Supported?" to state.zAxisSupported
+ ),
+ isAvailable = state.isAvailable
+ )
+ }
+
+ is HeadingSensorState -> {
+ SensorItem(
+ name = "Heading",
+ scrollProgress = scrollProgress,
+ imageRef = R.drawable.heading,
+ sensorValues = mapOf("Degrees" to state.degrees),
+ isAvailable = state.isAvailable
+ )
+ }
+ }
}
}
}
diff --git a/sample/src/main/res/drawable/ambient_temperature.png b/sample/src/main/res/drawable/ambient_temperature.png
deleted file mode 100644
index 53d0743..0000000
Binary files a/sample/src/main/res/drawable/ambient_temperature.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/ambient_temperature.webp b/sample/src/main/res/drawable/ambient_temperature.webp
new file mode 100644
index 0000000..35ed02f
Binary files /dev/null and b/sample/src/main/res/drawable/ambient_temperature.webp differ
diff --git a/sample/src/main/res/drawable/geomagnetic_rotation_vector.webp b/sample/src/main/res/drawable/geomagnetic_rotation_vector.webp
new file mode 100644
index 0000000..c5c0545
Binary files /dev/null and b/sample/src/main/res/drawable/geomagnetic_rotation_vector.webp differ
diff --git a/sample/src/main/res/drawable/gravity.png b/sample/src/main/res/drawable/gravity.png
deleted file mode 100644
index 5d183ff..0000000
Binary files a/sample/src/main/res/drawable/gravity.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/gravity.webp b/sample/src/main/res/drawable/gravity.webp
new file mode 100644
index 0000000..ad30126
Binary files /dev/null and b/sample/src/main/res/drawable/gravity.webp differ
diff --git a/sample/src/main/res/drawable/gyroscope.png b/sample/src/main/res/drawable/gyroscope.png
deleted file mode 100644
index 5b464ff..0000000
Binary files a/sample/src/main/res/drawable/gyroscope.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/gyroscope.webp b/sample/src/main/res/drawable/gyroscope.webp
new file mode 100644
index 0000000..9071b86
Binary files /dev/null and b/sample/src/main/res/drawable/gyroscope.webp differ
diff --git a/sample/src/main/res/drawable/head_tracker.webp b/sample/src/main/res/drawable/head_tracker.webp
new file mode 100644
index 0000000..a34cc88
Binary files /dev/null and b/sample/src/main/res/drawable/head_tracker.webp differ
diff --git a/sample/src/main/res/drawable/heading.webp b/sample/src/main/res/drawable/heading.webp
new file mode 100644
index 0000000..e9409ca
Binary files /dev/null and b/sample/src/main/res/drawable/heading.webp differ
diff --git a/sample/src/main/res/drawable/heart_rate.png b/sample/src/main/res/drawable/heart_rate.png
deleted file mode 100644
index 0b9cde3..0000000
Binary files a/sample/src/main/res/drawable/heart_rate.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/heart_rate.webp b/sample/src/main/res/drawable/heart_rate.webp
new file mode 100644
index 0000000..4189036
Binary files /dev/null and b/sample/src/main/res/drawable/heart_rate.webp differ
diff --git a/sample/src/main/res/drawable/linear_acceleration.png b/sample/src/main/res/drawable/linear_acceleration.png
deleted file mode 100644
index d5f6f6b..0000000
Binary files a/sample/src/main/res/drawable/linear_acceleration.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/linear_acceleration.webp b/sample/src/main/res/drawable/linear_acceleration.webp
new file mode 100644
index 0000000..90857b1
Binary files /dev/null and b/sample/src/main/res/drawable/linear_acceleration.webp differ
diff --git a/sample/src/main/res/drawable/low_latency_off_body_detect.jpg b/sample/src/main/res/drawable/low_latency_off_body_detect.jpg
deleted file mode 100644
index 0b1cf40..0000000
Binary files a/sample/src/main/res/drawable/low_latency_off_body_detect.jpg and /dev/null differ
diff --git a/sample/src/main/res/drawable/low_latency_off_body_detect.webp b/sample/src/main/res/drawable/low_latency_off_body_detect.webp
new file mode 100644
index 0000000..26466ee
Binary files /dev/null and b/sample/src/main/res/drawable/low_latency_off_body_detect.webp differ
diff --git a/sample/src/main/res/drawable/magnetic_field.png b/sample/src/main/res/drawable/magnetic_field.png
deleted file mode 100644
index ddf6662..0000000
Binary files a/sample/src/main/res/drawable/magnetic_field.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/magnetic_field.webp b/sample/src/main/res/drawable/magnetic_field.webp
new file mode 100644
index 0000000..27b4fc0
Binary files /dev/null and b/sample/src/main/res/drawable/magnetic_field.webp differ
diff --git a/sample/src/main/res/drawable/motion_detect.webp b/sample/src/main/res/drawable/motion_detect.webp
new file mode 100644
index 0000000..450f9e7
Binary files /dev/null and b/sample/src/main/res/drawable/motion_detect.webp differ
diff --git a/sample/src/main/res/drawable/pressure.jpg b/sample/src/main/res/drawable/pressure.jpg
deleted file mode 100644
index f30511f..0000000
Binary files a/sample/src/main/res/drawable/pressure.jpg and /dev/null differ
diff --git a/sample/src/main/res/drawable/pressure.webp b/sample/src/main/res/drawable/pressure.webp
new file mode 100644
index 0000000..07ef003
Binary files /dev/null and b/sample/src/main/res/drawable/pressure.webp differ
diff --git a/sample/src/main/res/drawable/rotation_vector.png b/sample/src/main/res/drawable/rotation_vector.png
deleted file mode 100644
index fb6474b..0000000
Binary files a/sample/src/main/res/drawable/rotation_vector.png and /dev/null differ
diff --git a/sample/src/main/res/drawable/rotation_vector.webp b/sample/src/main/res/drawable/rotation_vector.webp
new file mode 100644
index 0000000..d1b3f8c
Binary files /dev/null and b/sample/src/main/res/drawable/rotation_vector.webp differ
diff --git a/sample/src/main/res/drawable/significant_motion.webp b/sample/src/main/res/drawable/significant_motion.webp
new file mode 100644
index 0000000..cbd702e
Binary files /dev/null and b/sample/src/main/res/drawable/significant_motion.webp differ
diff --git a/sample/src/main/res/drawable/stationary_detect.webp b/sample/src/main/res/drawable/stationary_detect.webp
new file mode 100644
index 0000000..c92f090
Binary files /dev/null and b/sample/src/main/res/drawable/stationary_detect.webp differ
diff --git a/sample/src/main/res/drawable/step_counter.webp b/sample/src/main/res/drawable/step_counter.webp
new file mode 100644
index 0000000..45b5cfb
Binary files /dev/null and b/sample/src/main/res/drawable/step_counter.webp differ
diff --git a/sample/src/main/res/drawable/step_detection.webp b/sample/src/main/res/drawable/step_detection.webp
new file mode 100644
index 0000000..401f436
Binary files /dev/null and b/sample/src/main/res/drawable/step_detection.webp differ