Files
LightMeter/app/src/main/java/eu/wa5p/lightmeter/MainActivity.kt
T

262 lines
7.2 KiB
Kotlin

package eu.wa5p.lightmeter
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import eu.wa5p.lightmeter.ui.theme.LightMeterTheme
import kotlin.math.roundToInt
const val TAG: String = "lightmeter MainActivity"
class MainActivity : ComponentActivity(), SensorEventListener {
private lateinit var sensorManager: SensorManager
private var illuminanceSensor: Sensor? = null
private var lightmeterState = mutableStateOf(
LightmeterState(
illuminance = 0.0,
aperture = 1.8,
iso = 100,
shutterSpeed = 0.01
)
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
LightMeterTheme {
LightmeterHomeScreen(state = lightmeterState )
}
}
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
illuminanceSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)
}
private fun updateLightmeterState(illuminance: Double) {
val iso = lightmeterState.value.iso
val aperture = lightmeterState.value.aperture
val shutterSpeed = (aperture * aperture) / (illuminance * (iso / 100))
lightmeterState.value = lightmeterState.value.copy(
illuminance = illuminance,
iso = iso,
aperture = aperture,
shutterSpeed = shutterSpeed
)
}
override fun onSensorChanged(event: SensorEvent?) {
event?.values?.let {
for (v in it) {
Log.d(TAG, "sensor changed: $v lux")
updateLightmeterState(v.toDouble())
}
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
Log.d(TAG, "accuracy changed: sensor: ${sensor?.name}. accuracy: $accuracy")
}
override fun onResume() {
super.onResume()
sensorManager.registerListener(this, illuminanceSensor, SensorManager.SENSOR_DELAY_NORMAL)
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
}
override fun onDestroy() {
super.onDestroy()
sensorManager.unregisterListener(this)
}
}
@Composable
fun LightmeterListItem(
label: String,
value: String,
unit: String
) {
Row(modifier = Modifier.padding(bottom = 8.dp)) {
Text(
modifier = Modifier
.weight(0.4f)
.padding(all = 8.dp),
text = label
)
Text(
modifier = Modifier
.weight(0.4f)
.padding(all = 8.dp),
textAlign = TextAlign.Right,
text = value
)
Text(
modifier = Modifier
.weight(0.2f)
.padding(all = 8.dp),
text = unit
)
}
}
@Preview(showBackground = true)
@Composable
fun LightmeterListItemPreview() {
LightMeterTheme {
LightmeterListItem(
label = "Illuminance",
value = "1234",
unit = "lux"
)
}
}
// TODO: Recalculate state on change
@Composable
fun IsoSlider(state: MutableState<LightmeterState>) {
Row(
modifier = Modifier.padding(all = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.weight(0.3f),
text = "ISO"
)
Slider(
modifier = Modifier.weight(0.7f),
value = state.value.iso.toFloat(),
onValueChange = { value ->
state.value = state.value.copy(iso = value.roundToInt())
},
valueRange = 1f..2000f
)
}
}
val previewlightmeterState = mutableStateOf(
LightmeterState(
illuminance = 0.0,
aperture = 1.8,
iso = 100,
shutterSpeed = 0.01
)
)
@Preview(showBackground = true)
@Composable
fun IsoSliderPreview() {
LightMeterTheme {
IsoSlider(
state = remember { previewlightmeterState }
)
}
}
// TODO: Recalculate state on change
@Composable
fun ApertureSlider(state: MutableState<LightmeterState>) {
Row(
modifier = Modifier.padding(all = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.weight(0.3f),
text = "Aperture"
)
Slider(
modifier = Modifier.weight(0.7f),
value = state.value.aperture.toFloat(),
onValueChange = { value ->
state.value = state.value.copy(aperture = value.toDouble())
},
valueRange = 0.85f..22f
)
}
}
@Preview(showBackground = true)
@Composable
fun ApertureSliderPreview() {
LightMeterTheme {
ApertureSlider(state = remember { previewlightmeterState })
}
}
@Composable
fun LightmeterHomeScreen(state: MutableState<LightmeterState>) {
Column(modifier = Modifier
.fillMaxSize()
.padding(top = 64.dp)
.padding(all = 32.dp)
) {
Column(
verticalArrangement = Arrangement.Top
) {
LightmeterListItem(
label = "Illuminance",
value = "%.2f".format(state.value.illuminance),
unit = "lux"
)
LightmeterListItem(
label = "ISO",
value = state.value.iso.toString(),
unit = ""
)
LightmeterListItem(
label = "Aperture",
value = "%.2f".format(state.value.aperture),
unit = "f"
)
LightmeterListItem(
label = "Shutter Speed",
value = "%.2f".format(state.value.shutterSpeed),
unit = "s"
)
}
Column(
modifier = Modifier.padding(top = 64.dp),
verticalArrangement = Arrangement.Bottom
) {
IsoSlider(state = state)
ApertureSlider(state = state)
}
}
}
@Preview(showBackground = true)
@Composable
fun LightmeterHomeScreenPreview() {
LightMeterTheme {
LightmeterHomeScreen(
state = remember { previewlightmeterState }
)
}
}