feat: collect gyro and gps
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
package at.lockstep.player.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -22,9 +26,11 @@ import androidx.compose.material3.Slider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@@ -34,8 +40,13 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import at.lockstep.player.LockstepViewModel
|
||||
import at.lockstep.player.playback.PlaybackService
|
||||
import at.lockstep.player.R
|
||||
import at.lockstep.player.util.RunDataCollector
|
||||
import at.lockstep.player.util.RunDataStorage
|
||||
|
||||
data class NowPlayingUiState(
|
||||
val title: String,
|
||||
@@ -153,10 +164,38 @@ fun NowPlayingScreen(
|
||||
fun NowPlayingRoute(
|
||||
playlistId: String,
|
||||
playback: PlaybackService?,
|
||||
viewModel: LockstepViewModel,
|
||||
onBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val collectRunData by viewModel.collectRunData.collectAsStateWithLifecycle()
|
||||
val collector = remember { RunDataCollector(context) }
|
||||
val runSessionFolder = remember { RunDataStorage.newRunSessionFolderName() }
|
||||
|
||||
val locationPermissionLauncher =
|
||||
rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.RequestPermission(),
|
||||
onResult = { granted ->
|
||||
if (collectRunData) {
|
||||
collector.start(enableLocation = granted)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
fun startRunDataCollection() {
|
||||
val hasLocation =
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
if (!hasLocation) {
|
||||
locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
}
|
||||
collector.start(enableLocation = hasLocation)
|
||||
}
|
||||
var playlistDisplayName by remember { mutableStateOf("playlist") }
|
||||
var currentTrackId by remember { mutableStateOf<String?>(null) }
|
||||
var currentQueueIndex by remember { mutableIntStateOf(0) }
|
||||
|
||||
var ui by remember {
|
||||
mutableStateOf(
|
||||
NowPlayingUiState(
|
||||
@@ -170,9 +209,15 @@ fun NowPlayingRoute(
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(playlistId) {
|
||||
playlistDisplayName = viewModel.getPlaylistDisplayName(playlistId)
|
||||
}
|
||||
|
||||
LaunchedEffect(playback) {
|
||||
val service = playback ?: return@LaunchedEffect
|
||||
service.uiState.collect { p ->
|
||||
currentTrackId = p.currentTrackId
|
||||
currentQueueIndex = p.currentQueueIndex
|
||||
ui =
|
||||
NowPlayingUiState(
|
||||
title = p.title,
|
||||
@@ -185,6 +230,58 @@ fun NowPlayingRoute(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(collectRunData, playback) {
|
||||
if (!collectRunData) {
|
||||
collector.setAcceptSamples(false)
|
||||
return@LaunchedEffect
|
||||
}
|
||||
val service = playback ?: return@LaunchedEffect
|
||||
var lastTrackId: String? = null
|
||||
service.uiState.collect { state ->
|
||||
collector.setAcceptSamples(state.isPlaying)
|
||||
val trackId = state.currentTrackId
|
||||
if (trackId != null && trackId != lastTrackId) {
|
||||
collector.markSongStart()
|
||||
lastTrackId = trackId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(collectRunData, playback, playlistId, playlistDisplayName) {
|
||||
if (!collectRunData) return@LaunchedEffect
|
||||
val service = playback ?: return@LaunchedEffect
|
||||
service.trackBoundaryEvents.collect { event ->
|
||||
val snapshot = collector.snapshotAndClear()
|
||||
viewModel.persistRunData(playlistId, playlistDisplayName, runSessionFolder, event, snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(collectRunData) {
|
||||
if (collectRunData) {
|
||||
startRunDataCollection()
|
||||
} else {
|
||||
collector.stop()
|
||||
collector.snapshotAndClear()
|
||||
}
|
||||
onDispose {
|
||||
if (collectRunData) {
|
||||
val snapshot = collector.snapshotAndClear()
|
||||
val trackId = currentTrackId
|
||||
if (!snapshot.isEmpty() && trackId != null) {
|
||||
viewModel.persistRunDataForCurrentTrack(
|
||||
playlistId = playlistId,
|
||||
playlistDisplayName = playlistDisplayName,
|
||||
runSessionFolder = runSessionFolder,
|
||||
trackId = trackId,
|
||||
queueIndex = currentQueueIndex,
|
||||
snapshot = snapshot,
|
||||
)
|
||||
}
|
||||
}
|
||||
collector.release()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
topBar = {
|
||||
|
||||
Reference in New Issue
Block a user