From 6cb2eeda57a766956897a0a39d755295b76c8997 Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Sat, 25 Mar 2023 20:27:23 +0530 Subject: [PATCH 1/5] Introduce onError lambda, replace SensorState interface with SensorState @Immutable class that holds all values of a sensor, rename all sensor state classes to include the word `Sensor` --- .../AccelerometerSensorState.kt | 89 +++++++++++++ .../composesensors/AccelerometerState.kt | 75 ----------- .../composesensors/GyroscopeSensorState.kt | 87 +++++++++++++ .../composesensors/GyroscopeState.kt | 76 ----------- .../composesensors/LightSensorState.kt | 78 +++++++++++ .../mutualmobile/composesensors/LightState.kt | 58 --------- .../MagneticFieldSensorState.kt | 88 +++++++++++++ .../composesensors/MagneticFieldState.kt | 75 ----------- .../composesensors/PressureSensorState.kt | 77 +++++++++++ .../composesensors/PressureState.kt | 63 --------- .../composesensors/SensorState.kt | 122 +++++++++++++----- .../mutualmobile/composesensors/SensorType.kt | 7 +- .../com/mutualmobile/sample/MainActivity.kt | 7 +- 13 files changed, 515 insertions(+), 387 deletions(-) create mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt delete mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerState.kt create mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt delete mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeState.kt create mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt delete mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/LightState.kt create mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt delete mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldState.kt create mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt delete mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/PressureState.kt diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt new file mode 100644 index 0000000..42c8758 --- /dev/null +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerSensorState.kt @@ -0,0 +1,89 @@ +package com.mutualmobile.composesensors + +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 + +/** + * An acceleration sensor determines the acceleration that is applied to a device by measuring the + * forces that are applied to the sensor itself. + * @param xForce Acceleration force along the x axis (including gravity) in m/s^2. Defaults to 0f. + * @param yForce Acceleration force along the y axis (including gravity) in m/s^2. Defaults to 0f. + * @param zForce Acceleration force along the z axis (including gravity) in m/s^2. Defaults to 0f. + * @param isAvailable Whether the current device has an accelerometer sensor. Defaults to false. + * @param accuracy Accuracy factor of the accelerometer sensor. Defaults to 0. + */ +@Immutable +class AccelerometerSensorState internal constructor( + val xForce: Float = 0f, + val yForce: Float = 0f, + val zForce: Float = 0f, + val isAvailable: Boolean = false, + val accuracy: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is AccelerometerSensorState) return false + + if (xForce != other.xForce) return false + if (yForce != other.yForce) return false + if (zForce != other.zForce) return false + if (isAvailable != other.isAvailable) return false + if (accuracy != other.accuracy) return false + + return true + } + + override fun hashCode(): Int { + var result = xForce.hashCode() + result = 31 * result + yForce.hashCode() + result = 31 * result + zForce.hashCode() + result = 31 * result + isAvailable.hashCode() + result = 31 * result + accuracy.hashCode() + return result + } + + override fun toString(): String { + return "AccelerometerSensorState(xForce=$xForce, yForce=$yForce, zForce=$zForce, " + + "isAvailable=$isAvailable, accuracy=$accuracy)" + } +} + +/** + * Creates and [remember]s an instance of [AccelerometerSensorState]. + * @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( + sensorDelay: SensorDelay = SensorDelay.Normal, + onError: (throwable: Throwable) -> Unit = {}, +): AccelerometerSensorState { + val sensorState = rememberSensorState( + sensorType = SensorType.Accelerometer, + sensorDelay = sensorDelay, + onError = onError, + ) + val accelerometerSensorState = remember { mutableStateOf(AccelerometerSensorState()) } + + LaunchedEffect( + key1 = sensorState, + block = { + val sensorStateValues = sensorState.data + if (sensorStateValues.isNotEmpty()) { + accelerometerSensorState.value = AccelerometerSensorState( + xForce = sensorStateValues[0], + yForce = sensorStateValues[1], + zForce = sensorStateValues[2], + isAvailable = sensorState.isAvailable, + accuracy = sensorState.accuracy + ) + } + } + ) + + return accelerometerSensorState.value +} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerState.kt deleted file mode 100644 index c53e41c..0000000 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/AccelerometerState.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.mutualmobile.composesensors - -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 - -/** - * An acceleration sensor determines the acceleration that is applied to a device by measuring the - * forces that are applied to the sensor itself. - * @param xForce Acceleration force along the x axis (including gravity) in m/s^2. - * @param yForce Acceleration force along the y axis (including gravity) in m/s^2. - * @param zForce Acceleration force along the z axis (including gravity) in m/s^2. - */ -@Immutable -class AccelerometerState internal constructor( - val xForce: Float = 0f, - val yForce: Float = 0f, - val zForce: Float = 0f, -) : SensorState { - override val isAvailable: Boolean - @Composable - get() = SensorType.Accelerometer.isSensorAvailable() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is AccelerometerState) return false - - if (xForce != other.xForce) return false - if (yForce != other.yForce) return false - if (zForce != other.zForce) return false - - return true - } - - override fun hashCode(): Int { - var result = xForce.hashCode() - result = 31 * result + yForce.hashCode() - result = 31 * result + zForce.hashCode() - return result - } - - override fun toString(): String { - return "AccelerometerState(xForce=$xForce, yForce=$yForce, zForce=$zForce)" - } -} - -/** - * Creates and [remember]s an instance of [AccelerometerState]. - */ -@Composable -fun rememberAccelerometerState(sensorDelay: SensorDelay = SensorDelay.Normal): AccelerometerState { - val sensorState = rememberSensorState( - sensorType = SensorType.Accelerometer, - sensorDelay = sensorDelay - ) - val accelerometerState = remember { mutableStateOf(AccelerometerState()) } - - LaunchedEffect( - key1 = sensorState.value, - block = { - val sensorStateValues = sensorState.value - if (sensorStateValues.isNotEmpty()) { - accelerometerState.value = AccelerometerState( - xForce = sensorStateValues[0], - yForce = sensorStateValues[1], - zForce = sensorStateValues[2] - ) - } - } - ) - - return accelerometerState.value -} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt new file mode 100644 index 0000000..d6d268e --- /dev/null +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeSensorState.kt @@ -0,0 +1,87 @@ +package com.mutualmobile.composesensors + +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 + +/** + * Measures a device's rate of rotation in rad/s around each of the three physical axes + * (x, y, and z). + * @param xRotation Rate of rotation around the x axis. Defaults to 0f. + * @param yRotation Rate of rotation around the y axis. Defaults to 0f. + * @param zRotation Rate of rotation around the z axis. 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. + */ +@Immutable +class GyroscopeSensorState internal constructor( + val xRotation: Float = 0f, + val yRotation: Float = 0f, + val zRotation: Float = 0f, + val isAvailable: Boolean = false, + val accuracy: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is GyroscopeSensorState) return false + + if (xRotation != other.xRotation) return false + if (yRotation != other.yRotation) return false + if (zRotation != other.zRotation) return false + if (isAvailable != other.isAvailable) return false + + return true + } + + override fun hashCode(): Int { + var result = xRotation.hashCode() + result = 31 * result + yRotation.hashCode() + result = 31 * result + zRotation.hashCode() + result = 31 * result + isAvailable.hashCode() + return result + } + + override fun toString(): String { + return "GyroscopeSensorState(xRotation=$xRotation, yRotation=$yRotation, " + + "zRotation=$zRotation, isAvailable=$isAvailable, accuracy=$accuracy)" + } +} + +/** + * 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 onError Callback invoked on every error state. + */ +@Composable +fun rememberGyroscopeSensorState( + sensorDelay: SensorDelay = SensorDelay.Normal, + onError: (throwable: Throwable) -> Unit = {}, +): GyroscopeSensorState { + val sensorState = rememberSensorState( + sensorType = SensorType.Gyroscope, + sensorDelay = sensorDelay, + onError = onError, + ) + val gyroscopeSensorState = remember { mutableStateOf(GyroscopeSensorState()) } + + LaunchedEffect( + key1 = sensorState, + block = { + val sensorStateValues = sensorState.data + if (sensorStateValues.isNotEmpty()) { + gyroscopeSensorState.value = GyroscopeSensorState( + xRotation = sensorStateValues[0], + yRotation = sensorStateValues[1], + zRotation = sensorStateValues[2], + isAvailable = sensorState.isAvailable, + accuracy = sensorState.accuracy + ) + } + } + ) + + return gyroscopeSensorState.value +} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeState.kt deleted file mode 100644 index 5fb4ac6..0000000 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/GyroscopeState.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.mutualmobile.composesensors - -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 - -/** - * Measures a device's rate of rotation in rad/s around each of the three physical axes - * (x, y, and z). - * @param xRotation Rate of rotation around the x axis. - * @param yRotation Rate of rotation around the y axis. - * @param zRotation Rate of rotation around the z axis. - */ -@Immutable -class GyroscopeState internal constructor( - val xRotation: Float = 0f, - val yRotation: Float = 0f, - val zRotation: Float = 0f, -) : SensorState { - override val isAvailable: Boolean - @Composable - get() = SensorType.Gyroscope.isSensorAvailable() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is GyroscopeState) return false - - if (xRotation != other.xRotation) return false - if (yRotation != other.yRotation) return false - if (zRotation != other.zRotation) return false - - return true - } - - override fun hashCode(): Int { - var result = xRotation.hashCode() - result = 31 * result + yRotation.hashCode() - result = 31 * result + zRotation.hashCode() - return result - } - - override fun toString(): String { - return "GyroscopeState(xRotation=$xRotation, yRotation=$yRotation, " + - "zRotation=$zRotation)" - } -} - -/** - * Creates and [remember]s an instance of [GyroscopeState]. - */ -@Composable -fun rememberGyroscopeState(sensorDelay: SensorDelay = SensorDelay.Normal): GyroscopeState { - val sensorState = rememberSensorState( - sensorType = SensorType.Gyroscope, - sensorDelay = sensorDelay - ) - val gyroscopeState = remember { mutableStateOf(GyroscopeState()) } - - LaunchedEffect( - key1 = sensorState.value, - block = { - val sensorStateValues = sensorState.value - if (sensorStateValues.isNotEmpty()) { - gyroscopeState.value = GyroscopeState( - xRotation = sensorStateValues[0], - yRotation = sensorStateValues[1], - zRotation = sensorStateValues[2] - ) - } - } - ) - - return gyroscopeState.value -} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt new file mode 100644 index 0000000..04343da --- /dev/null +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/LightSensorState.kt @@ -0,0 +1,78 @@ +package com.mutualmobile.composesensors + +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 + +/** + * Measures the ambient light level (illumination) in lx. + * @param illuminance Level of brightness of the light (in lx). + * @param isAvailable Whether the current device has a light sensor. Defaults to false. + * @param accuracy Accuracy factor of the light sensor. Defaults to 0. + */ +@Immutable +class LightSensorState internal constructor( + val illuminance: Float = 0f, + val isAvailable: Boolean = false, + val accuracy: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is LightSensorState) return false + + if (illuminance != other.illuminance) return false + if (isAvailable != other.isAvailable) return false + if (accuracy != other.accuracy) return false + + return true + } + + override fun hashCode(): Int { + var result = illuminance.hashCode() + result = 31 * result + isAvailable.hashCode() + result = 31 * result + accuracy.hashCode() + return result + } + + override fun toString(): String { + return "LightSensorState(illuminance=$illuminance, isAvailable=$isAvailable, " + + "accuracy=$accuracy)" + } +} + +/** + * Creates and [remember]s an instance of [LightSensorState]. + * @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( + sensorDelay: SensorDelay = SensorDelay.Normal, + onError: (throwable: Throwable) -> Unit = {}, +): LightSensorState { + val sensorState = rememberSensorState( + sensorType = SensorType.Light, + sensorDelay = sensorDelay, + onError = onError, + ) + val lightSensorState = remember { mutableStateOf(LightSensorState()) } + + LaunchedEffect( + key1 = sensorState, + block = { + val sensorStateValues = sensorState.data + if (sensorStateValues.isNotEmpty()) { + lightSensorState.value = LightSensorState( + illuminance = sensorStateValues[0], + isAvailable = sensorState.isAvailable, + accuracy = sensorState.accuracy + ) + } + } + ) + + return lightSensorState.value +} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/LightState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/LightState.kt deleted file mode 100644 index 6fbd90a..0000000 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/LightState.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.mutualmobile.composesensors - -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 - -/** - * Measures the ambient light level (illumination) in lx. - * @param illuminance Level of brightness of the light. - */ -@Immutable -class LightState internal constructor( - val illuminance: Float = 0f, -) : SensorState { - override val isAvailable: Boolean - @Composable - get() = SensorType.Light.isSensorAvailable() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is LightState) return false - - if (illuminance != other.illuminance) return false - - return true - } - - override fun hashCode(): Int { - return illuminance.hashCode() - } - - override fun toString(): String { - return "LightState(illuminance=$illuminance)" - } -} - -/** - * Creates and [remember]s an instance of [LightState]. - */ -@Composable -fun rememberLightState(sensorDelay: SensorDelay = SensorDelay.Normal): LightState { - val sensorState = rememberSensorState(sensorType = SensorType.Light, sensorDelay = sensorDelay) - val lightState = remember { mutableStateOf(LightState()) } - - LaunchedEffect( - key1 = sensorState.value, - block = { - val sensorStateValues = sensorState.value - if (sensorStateValues.isNotEmpty()) { - lightState.value = LightState(illuminance = sensorStateValues[0]) - } - } - ) - - return lightState.value -} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt new file mode 100644 index 0000000..722e1e0 --- /dev/null +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldSensorState.kt @@ -0,0 +1,88 @@ +package com.mutualmobile.composesensors + +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 + +/** + * Measures the ambient geomagnetic field for all three physical axes (x, y, z) in μT. + * @param xStrength Geomagnetic field strength along the x axis in μT. Defaults to 0f. + * @param yStrength Geomagnetic field strength along the y axis in μT. Defaults to 0f. + * @param zStrength Geomagnetic field strength along the z axis in μT. Defaults to 0f. + * @param isAvailable Whether the current device has a magnetic field sensor. Defaults to false. + * @param accuracy Accuracy factor of the magnetic field sensor. Defaults to 0. + */ +@Immutable +class MagneticFieldSensorState internal constructor( + val xStrength: Float = 0f, + val yStrength: Float = 0f, + val zStrength: Float = 0f, + val isAvailable: Boolean = false, + val accuracy: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is MagneticFieldSensorState) return false + + if (xStrength != other.xStrength) return false + if (yStrength != other.yStrength) return false + if (zStrength != other.zStrength) return false + if (isAvailable != other.isAvailable) return false + if (accuracy != other.accuracy) return false + + return true + } + + override fun hashCode(): Int { + var result = xStrength.hashCode() + result = 31 * result + yStrength.hashCode() + result = 31 * result + zStrength.hashCode() + result = 31 * result + isAvailable.hashCode() + result = 31 * result + accuracy.hashCode() + return result + } + + override fun toString(): String { + return "MagneticFieldSensorState(xStrength=$xStrength, yStrength=$yStrength, " + + "zStrength=$zStrength, isAvailable=$isAvailable, accuracy=$accuracy)" + } +} + +/** + * Creates and [remember]s an instance of [MagneticFieldSensorState]. + * @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( + sensorDelay: SensorDelay = SensorDelay.Normal, + onError: (throwable: Throwable) -> Unit = {}, +): MagneticFieldSensorState { + val sensorState = rememberSensorState( + sensorType = SensorType.MagneticField, + sensorDelay = sensorDelay, + onError = onError, + ) + val magneticFieldSensorState = remember { mutableStateOf(MagneticFieldSensorState()) } + + LaunchedEffect( + key1 = sensorState, + block = { + val sensorStateValues = sensorState.data + if (sensorStateValues.isNotEmpty()) { + magneticFieldSensorState.value = MagneticFieldSensorState( + xStrength = sensorStateValues[0], + yStrength = sensorStateValues[1], + zStrength = sensorStateValues[2], + isAvailable = sensorState.isAvailable, + accuracy = sensorState.accuracy + ) + } + } + ) + + return magneticFieldSensorState.value +} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldState.kt deleted file mode 100644 index 12f6f8f..0000000 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/MagneticFieldState.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.mutualmobile.composesensors - -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 - -/** - * Measures the ambient geomagnetic field for all three physical axes (x, y, z) in μT. - * @param xStrength Geomagnetic field strength along the x axis. - * @param yStrength Geomagnetic field strength along the y axis. - * @param zStrength Geomagnetic field strength along the z axis. - */ -@Immutable -class MagneticFieldState internal constructor( - val xStrength: Float = 0f, - val yStrength: Float = 0f, - val zStrength: Float = 0f, -) : SensorState { - override val isAvailable: Boolean - @Composable - get() = SensorType.MagneticField.isSensorAvailable() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is MagneticFieldState) return false - - if (xStrength != other.xStrength) return false - if (yStrength != other.yStrength) return false - if (zStrength != other.zStrength) return false - - return true - } - - override fun hashCode(): Int { - var result = xStrength.hashCode() - result = 31 * result + yStrength.hashCode() - result = 31 * result + zStrength.hashCode() - return result - } - - override fun toString(): String { - return "MagneticFieldState(xStrength=$xStrength, yStrength=$yStrength, " + - "zStrength=$zStrength)" - } -} - -/** - * Creates and [remember]s an instance of [MagneticFieldState]. - */ -@Composable -fun rememberMagneticFieldState(sensorDelay: SensorDelay = SensorDelay.Normal): MagneticFieldState { - val sensorState = rememberSensorState( - sensorType = SensorType.MagneticField, - sensorDelay = sensorDelay - ) - val magneticFieldState = remember { mutableStateOf(MagneticFieldState()) } - - LaunchedEffect( - key1 = sensorState.value, - block = { - val sensorStateValues = sensorState.value - if (sensorStateValues.isNotEmpty()) { - magneticFieldState.value = MagneticFieldState( - xStrength = sensorStateValues[0], - yStrength = sensorStateValues[1], - zStrength = sensorStateValues[2] - ) - } - } - ) - - return magneticFieldState.value -} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt new file mode 100644 index 0000000..978d5fc --- /dev/null +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/PressureSensorState.kt @@ -0,0 +1,77 @@ +package com.mutualmobile.composesensors + +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 + +/** + * Measures the ambient air pressure in hPa or mbar. + * @param pressure Ambient air pressure in hPa or mbar. + * @param isAvailable Whether the current device has a light sensor. Defaults to false. + * @param accuracy Accuracy factor of the magnetic field sensor. Defaults to 0. + */ +@Immutable +class PressureSensorState internal constructor( + val pressure: Float = 0f, + val isAvailable: Boolean = false, + val accuracy: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PressureSensorState) return false + + if (pressure != other.pressure) return false + if (isAvailable != other.isAvailable) return false + if (accuracy != other.accuracy) return false + + return true + } + + override fun hashCode(): Int { + var result = pressure.hashCode() + result = 31 * result + isAvailable.hashCode() + result = 31 * result + accuracy.hashCode() + return result + } + + override fun toString(): String { + return "PressureState(pressure=$pressure, isAvailable=$isAvailable, accuracy=$accuracy)" + } +} + +/** + * Creates and [remember]s an instance of [PressureSensorState]. + * @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 rememberPressureState( + sensorDelay: SensorDelay = SensorDelay.Normal, + onError: (throwable: Throwable) -> Unit = {}, +): PressureSensorState { + val sensorState = rememberSensorState( + sensorType = SensorType.Pressure, + sensorDelay = sensorDelay, + onError = onError, + ) + val pressureSensorState = remember { mutableStateOf(PressureSensorState()) } + + LaunchedEffect( + key1 = sensorState, + block = { + val sensorStateValues = sensorState.data + if (sensorStateValues.isNotEmpty()) { + pressureSensorState.value = PressureSensorState( + pressure = sensorStateValues[0], + isAvailable = sensorState.isAvailable, + accuracy = sensorState.accuracy + ) + } + } + ) + + return pressureSensorState.value +} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/PressureState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/PressureState.kt deleted file mode 100644 index afd725d..0000000 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/PressureState.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.mutualmobile.composesensors - -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 - -/** - * Measures the ambient air pressure in hPa or mbar. - * @param pressure Ambient air pressure. - */ -@Immutable -class PressureState internal constructor( - val pressure: Float = 0f, -) : SensorState { - override val isAvailable: Boolean - @Composable - get() = SensorType.Pressure.isSensorAvailable() - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is PressureState) return false - - if (pressure != other.pressure) return false - - return true - } - - override fun hashCode(): Int { - return pressure.hashCode() - } - - override fun toString(): String { - return "PressureState(pressure=$pressure)" - } -} - -/** - * Creates and [remember]s an instance of [PressureState]. - */ -@Composable -fun rememberPressureState(sensorDelay: SensorDelay = SensorDelay.Normal): PressureState { - val sensorState = rememberSensorState( - sensorType = SensorType.Pressure, - sensorDelay = sensorDelay - ) - val pressureState = remember { mutableStateOf(PressureState()) } - - LaunchedEffect( - key1 = sensorState.value, - block = { - val sensorStateValues = sensorState.value - if (sensorStateValues.isNotEmpty()) { - pressureState.value = PressureState( - pressure = sensorStateValues[0] - ) - } - } - ) - - return pressureState.value -} diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt index 1e7ce99..37c320f 100644 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorState.kt @@ -7,63 +7,115 @@ import android.hardware.SensorEventListener import android.hardware.SensorManager import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.Immutable import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -interface SensorState { - /** - * States whether this sensor is available for the current device. - */ - val isAvailable: Boolean @Composable get +/** + * Most Android-powered devices have built-in sensors that measure motion, orientation, and various + * environmental conditions. These sensors are capable of providing raw data with high precision + * and accuracy, and are useful if you want to monitor three-dimensional device movement or + * 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 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 accuracy: Int = 0, +) { + 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 (accuracy != other.accuracy) return false + + return true + } + + override fun hashCode(): Int { + var result = isAvailable.hashCode() + result = 31 * result + data.hashCode() + result = 31 * result + accuracy.hashCode() + return result + } + + override fun toString(): String { + return "SensorState(isAvailable=$isAvailable, data=${data.joinToString()}, " + + "accuracy=$accuracy)" + } } @Composable internal fun rememberSensorState( sensorType: SensorType, - sensorDelay: SensorDelay -): State> { + sensorDelay: SensorDelay, + onError: (throwable: Throwable) -> Unit, +): SensorState { + val isSensorAvailable = sensorType.rememberIsSensorAvailable() val sensorData: MutableState> = remember { mutableStateOf(emptyList()) } + val sensorAccuracy: MutableState = remember { mutableStateOf(0) } + val sensorState = remember { + derivedStateOf { + SensorState( + isAvailable = isSensorAvailable, + data = sensorData.value, + accuracy = sensorAccuracy.value + ) + } + } - val sensorManager = - LocalContext.current.getSystemService(Context.SENSOR_SERVICE) as SensorManager + runCatching { + val sensorManager = + LocalContext.current.getSystemService(Context.SENSOR_SERVICE) as SensorManager - val sensor = sensorManager.getDefaultSensor(sensorType.toAndroidSensorType()) - ?: throw SensorNotFoundException(sensorName = sensorType.name) + val sensor = sensorManager.getDefaultSensor(sensorType.toAndroidSensorType()) + ?: throw SensorNotFoundException(sensorName = sensorType.name) - DisposableEffect( - key1 = sensor, - key2 = sensorDelay, - effect = { + DisposableEffect( + key1 = sensor, + key2 = sensorDelay, + effect = { - val sensorEventListener = object : SensorEventListener { - override fun onSensorChanged(event: SensorEvent?) { - event?.let { nnEvent -> - nnEvent.values?.let { nnValues -> - sensorData.value = nnValues.toList() + val 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) {} - } + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { + sensorAccuracy.value = accuracy + } + } - sensorManager.registerListener( - sensorEventListener, - sensor, - sensorDelay.toAndroidSensorDelay() - ) + sensorManager.registerListener( + sensorEventListener, + sensor, + sensorDelay.toAndroidSensorDelay() + ) - onDispose { - sensorManager.unregisterListener(sensorEventListener) + onDispose { + sensorManager.unregisterListener(sensorEventListener) + } } - } - ) + ) + }.onFailure(onError) - return sensorData + return sensorState.value } private class SensorNotFoundException(sensorName: String) : - Exception("The required sensor '$sensorName' was not found.") + 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 645bade..e9b17ce 100644 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/SensorType.kt +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/SensorType.kt @@ -5,6 +5,7 @@ import android.hardware.Sensor import android.hardware.SensorManager import android.os.Build import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext sealed class SensorType(val name: String) { @@ -95,10 +96,12 @@ sealed class SensorType(val name: String) { } @Composable - internal fun isSensorAvailable(): Boolean { + internal fun rememberIsSensorAvailable(): Boolean { val context = LocalContext.current val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager - return sensorManager.getDefaultSensor(this.toAndroidSensorType()) != null + return remember(context, sensorManager) { + sensorManager.getDefaultSensor(this.toAndroidSensorType()) != null + } } } diff --git a/sample/src/main/java/com/mutualmobile/sample/MainActivity.kt b/sample/src/main/java/com/mutualmobile/sample/MainActivity.kt index a35e63a..1333fde 100644 --- a/sample/src/main/java/com/mutualmobile/sample/MainActivity.kt +++ b/sample/src/main/java/com/mutualmobile/sample/MainActivity.kt @@ -10,7 +10,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import com.mutualmobile.composesensors.rememberAccelerometerState +import com.mutualmobile.composesensors.rememberAccelerometerSensorState import com.mutualmobile.sample.ui.theme.ComposeSensorsTheme class MainActivity : ComponentActivity() { @@ -23,12 +23,13 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - val accelerometerState = rememberAccelerometerState() + val accelerometerState = rememberAccelerometerSensorState() Text( text = "Force X: ${accelerometerState.xForce}" + "\nForce Y: ${accelerometerState.yForce}" + "\nForce Z: ${accelerometerState.zForce}" + - "\nIs Available?: ${accelerometerState.isAvailable}" + "\nIs Available?: ${accelerometerState.isAvailable}," + + "\nAccuracy?: ${accelerometerState.accuracy}" ) } } From 755bb4f2962e99d9b090a836acbc1ae96aa56876 Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Sat, 25 Mar 2023 20:28:55 +0530 Subject: [PATCH 2/5] Update README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 16497cb..4e39205 100644 --- a/README.md +++ b/README.md @@ -52,12 +52,12 @@ dependencyResolutionManagement { The library provides straightforward state methods for multiple sensors like Accelerometer, Gyroscope, etc (more mentioned below). Following is an example on how to get the current values from the `Accelerometer`: ``` -val accelerometerState = rememberAccelerometerState() +val accelerometerState = rememberAccelerometerSensorState() ``` Use it in an example: ``` -val accelerometerState = rememberAccelerometerState() -// Optional: You could also write: rememberAccelerometerState(sensorDelay = SensorDelay.Fastest) for fetching sensor data faster +val accelerometerState = rememberAccelerometerSensorState() +// Optional: You could also write: rememberAccelerometerSensorState(sensorDelay = SensorDelay.Fastest) for fetching sensor data faster Text( text = "Force X: ${accelerometerState.xForce}" + @@ -71,11 +71,11 @@ Text( ComposeSensors plans to support the following Android sensors: Sensor | Status | Composable ------------- | ------------- | ------------- -Accelerometer | ✅ | rememberAccelerometerState() -Magnetic Field | ✅ | rememberMagneticFieldState() -Gyroscope | ✅ | rememberGyroscopeState() -Light | ✅️ | rememberLightState() -Pressure | ✅️ | rememberPressureState() +Accelerometer | ✅ | rememberAccelerometerSensorState() +Magnetic Field | ✅ | rememberMagneticFieldSensorState() +Gyroscope | ✅ | rememberGyroscopeSensorState() +Light | ✅️ | rememberLightSensorState() +Pressure | ✅️ | rememberPressureSensorState() Proximity | ⚠️ | WIP Gravity | ⚠️ | WIP Linear Acceleration | ⚠️ | WIP From 12cba62908b3dc07672967be6bd927e091a1170d Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Sun, 26 Mar 2023 21:14:12 +0530 Subject: [PATCH 3/5] Implement GravitySensorState --- README.md | 2 +- .../composesensors/GravitySensorState.kt | 87 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt diff --git a/README.md b/README.md index 4e39205..d447c05 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ Gyroscope | ✅ | rememberGyroscopeSensorState() Light | ✅️ | rememberLightSensorState() Pressure | ✅️ | rememberPressureSensorState() Proximity | ⚠️ | WIP -Gravity | ⚠️ | WIP +Gravity | ✅️ | rememberGravitySensorState() Linear Acceleration | ⚠️ | WIP Rotation Vector | ⚠️ | WIP Relative Humidity | ⚠️ | WIP diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt new file mode 100644 index 0000000..6c83dea --- /dev/null +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt @@ -0,0 +1,87 @@ +package com.mutualmobile.composesensors + +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 + +/** + * Measures the Gravity of gravity in m/s2 that is applied to a device on all three physical axes + * (x, y, z). + * @param xForce Force of gravity along the x axis. Defaults to 0f. + * @param yForce Force of gravity along the y axis. Defaults to 0f. + * @param zForce Force of gravity along the z axis. Defaults to 0f. + * @param isAvailable Whether the current device has a Gravity sensor. Defaults to false. + * @param accuracy Accuracy factor of the Gravity sensor. Defaults to 0. + */ +@Immutable +class GravitySensorState internal constructor( + val xForce: Float = 0f, + val yForce: Float = 0f, + val zForce: Float = 0f, + val isAvailable: Boolean = false, + val accuracy: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is GravitySensorState) return false + + if (xForce != other.xForce) return false + if (yForce != other.yForce) return false + if (zForce != other.zForce) return false + if (isAvailable != other.isAvailable) return false + + return true + } + + override fun hashCode(): Int { + var result = xForce.hashCode() + result = 31 * result + yForce.hashCode() + result = 31 * result + zForce.hashCode() + result = 31 * result + isAvailable.hashCode() + return result + } + + override fun toString(): String { + return "GravitySensorState(xForce=$xForce, yForce=$yForce, " + + "zForce=$zForce, isAvailable=$isAvailable, accuracy=$accuracy)" + } +} + +/** + * Creates and [remember]s an instance of [GravitySensorState]. + * @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( + sensorDelay: SensorDelay = SensorDelay.Normal, + onError: (throwable: Throwable) -> Unit = {}, +): GravitySensorState { + val sensorState = rememberSensorState( + sensorType = SensorType.Gravity, + sensorDelay = sensorDelay, + onError = onError, + ) + val gravitySensorState = remember { mutableStateOf(GravitySensorState()) } + + LaunchedEffect( + key1 = sensorState, + block = { + val sensorStateValues = sensorState.data + if (sensorStateValues.isNotEmpty()) { + gravitySensorState.value = GravitySensorState( + xForce = sensorStateValues[0], + yForce = sensorStateValues[1], + zForce = sensorStateValues[2], + isAvailable = sensorState.isAvailable, + accuracy = sensorState.accuracy + ) + } + } + ) + + return gravitySensorState.value +} From e316d01bc50dbd22f1dbfb3620200994bdb098f3 Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Sun, 26 Mar 2023 21:16:20 +0530 Subject: [PATCH 4/5] Fix GravitySensorState documentation --- .../java/com/mutualmobile/composesensors/GravitySensorState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt b/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt index 6c83dea..96e2017 100644 --- a/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt +++ b/composesensors/src/main/java/com/mutualmobile/composesensors/GravitySensorState.kt @@ -7,7 +7,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember /** - * Measures the Gravity of gravity in m/s2 that is applied to a device on all three physical axes + * Measures the force of gravity in m/s2 that is applied to a device on all three physical axes * (x, y, z). * @param xForce Force of gravity along the x axis. Defaults to 0f. * @param yForce Force of gravity along the y axis. Defaults to 0f. From 2fe421dfcfcec418b471f0b1a01b77f2879b9ae4 Mon Sep 17 00:00:00 2001 From: Shubham Singh Date: Sun, 26 Mar 2023 21:19:27 +0530 Subject: [PATCH 5/5] Remove the extra `composesensors` term from the dependency group id --- composesensors/gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composesensors/gradle.properties b/composesensors/gradle.properties index 8b32d2c..c42f6ce 100644 --- a/composesensors/gradle.properties +++ b/composesensors/gradle.properties @@ -3,9 +3,9 @@ SONATYPE_HOST=DEFAULT RELEASE_SIGNING_ENABLED=true SONATYPE_AUTOMATIC_RELEASE=true -GROUP=com.mutualmobile.composesensors +GROUP=com.mutualmobile POM_ARTIFACT_ID=composesensors -VERSION_NAME=1.0.0-SNAPSHOT +VERSION_NAME=0.0.1-SNAPSHOT VERSION_CODE=1 POM_NAME=Compose Sensors