2026-05-14 02:43:49 +02:00
|
|
|
package at.lockstep.player.ui
|
|
|
|
|
|
|
|
|
|
import android.content.ComponentName
|
|
|
|
|
import android.content.Context
|
|
|
|
|
import android.content.Intent
|
|
|
|
|
import android.content.ServiceConnection
|
|
|
|
|
import android.os.IBinder
|
|
|
|
|
import androidx.activity.ComponentActivity
|
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
|
import androidx.compose.runtime.DisposableEffect
|
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
|
import androidx.compose.runtime.mutableStateOf
|
|
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
|
import androidx.compose.runtime.setValue
|
2026-05-15 09:03:20 +02:00
|
|
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
2026-05-14 02:43:49 +02:00
|
|
|
import androidx.core.content.ContextCompat
|
|
|
|
|
import androidx.navigation.NavHostController
|
|
|
|
|
import androidx.navigation.NavType
|
|
|
|
|
import androidx.navigation.compose.NavHost
|
|
|
|
|
import androidx.navigation.compose.composable
|
|
|
|
|
import androidx.navigation.navArgument
|
|
|
|
|
import at.lockstep.jukebox.PlaylistSummary
|
|
|
|
|
import at.lockstep.player.LockstepViewModel
|
|
|
|
|
import at.lockstep.player.playback.PlaybackService
|
|
|
|
|
import at.lockstep.player.ui.library.LibraryScreen
|
|
|
|
|
import at.lockstep.player.ui.navigation.Routes
|
|
|
|
|
import at.lockstep.player.ui.pairing.PairingScreen
|
|
|
|
|
import at.lockstep.player.ui.settings.SettingsScreen
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
|
fun LockstepAppNavHost(
|
|
|
|
|
activity: ComponentActivity,
|
|
|
|
|
navController: NavHostController,
|
|
|
|
|
viewModel: LockstepViewModel,
|
|
|
|
|
) {
|
2026-05-15 09:03:20 +02:00
|
|
|
val annotationMode by viewModel.annotationMode.collectAsStateWithLifecycle()
|
2026-05-14 02:43:49 +02:00
|
|
|
var playback by remember { mutableStateOf<PlaybackService?>(null) }
|
|
|
|
|
|
|
|
|
|
DisposableEffect(Unit) {
|
|
|
|
|
val connection =
|
|
|
|
|
object : ServiceConnection {
|
|
|
|
|
override fun onServiceConnected(
|
|
|
|
|
name: ComponentName?,
|
|
|
|
|
binder: IBinder?,
|
|
|
|
|
) {
|
|
|
|
|
playback = (binder as PlaybackService.LocalBinder).getService()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onServiceDisconnected(name: ComponentName?) {
|
|
|
|
|
playback = null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
activity.bindService(
|
|
|
|
|
Intent(activity, PlaybackService::class.java),
|
|
|
|
|
connection,
|
|
|
|
|
Context.BIND_AUTO_CREATE,
|
|
|
|
|
)
|
|
|
|
|
onDispose {
|
|
|
|
|
activity.unbindService(connection)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NavHost(
|
|
|
|
|
navController = navController,
|
|
|
|
|
startDestination = Routes.Library,
|
|
|
|
|
) {
|
|
|
|
|
composable(Routes.Library) {
|
|
|
|
|
LibraryScreen(
|
|
|
|
|
viewModel = viewModel,
|
|
|
|
|
onPlaylistSelected = { playlist: PlaylistSummary, hasPaired ->
|
|
|
|
|
if (!hasPaired) {
|
|
|
|
|
navController.navigate(Routes.pairing(playlist.id))
|
|
|
|
|
} else {
|
|
|
|
|
startPlaylistPlayback(activity, playlist.id)
|
2026-05-15 09:03:20 +02:00
|
|
|
if (annotationMode) {
|
|
|
|
|
navController.navigate(Routes.annotation(playlist.id))
|
|
|
|
|
} else {
|
|
|
|
|
navController.navigate(Routes.nowPlaying(playlist.id))
|
|
|
|
|
}
|
2026-05-14 02:43:49 +02:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
onOpenSettings = {
|
|
|
|
|
navController.navigate(Routes.Settings)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
composable(Routes.Settings) {
|
|
|
|
|
SettingsScreen(
|
2026-05-14 03:03:17 +02:00
|
|
|
viewModel = viewModel,
|
2026-05-14 02:43:49 +02:00
|
|
|
onBack = { navController.popBackStack() },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
composable(
|
|
|
|
|
route = Routes.Pairing,
|
|
|
|
|
arguments =
|
|
|
|
|
listOf(
|
|
|
|
|
navArgument("playlistId") { type = NavType.StringType },
|
|
|
|
|
),
|
|
|
|
|
) { entry ->
|
|
|
|
|
val playlistId = entry.arguments?.getString("playlistId").orEmpty()
|
|
|
|
|
PairingScreen(
|
|
|
|
|
playlistId = playlistId,
|
|
|
|
|
viewModel = viewModel,
|
|
|
|
|
onBack = { navController.popBackStack() },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
composable(
|
|
|
|
|
route = Routes.NowPlaying,
|
|
|
|
|
arguments =
|
|
|
|
|
listOf(
|
|
|
|
|
navArgument("playlistId") { type = NavType.StringType },
|
|
|
|
|
),
|
|
|
|
|
) { entry ->
|
|
|
|
|
val playlistId = entry.arguments?.getString("playlistId").orEmpty()
|
|
|
|
|
NowPlayingRoute(
|
|
|
|
|
playlistId = playlistId,
|
|
|
|
|
playback = playback,
|
2026-05-24 07:17:51 +02:00
|
|
|
viewModel = viewModel,
|
2026-05-31 11:53:20 +02:00
|
|
|
onBack = {
|
|
|
|
|
viewModel.suppressNextLibraryMetadataSync()
|
|
|
|
|
navController.popBackStack()
|
|
|
|
|
},
|
2026-05-14 02:43:49 +02:00
|
|
|
)
|
|
|
|
|
}
|
2026-05-15 09:03:20 +02:00
|
|
|
composable(
|
|
|
|
|
route = Routes.Annotation,
|
|
|
|
|
arguments =
|
|
|
|
|
listOf(
|
|
|
|
|
navArgument("playlistId") { type = NavType.StringType },
|
|
|
|
|
),
|
|
|
|
|
) { entry ->
|
|
|
|
|
val playlistId = entry.arguments?.getString("playlistId").orEmpty()
|
|
|
|
|
AnnotationRoute(
|
|
|
|
|
playlistId = playlistId,
|
|
|
|
|
playback = playback,
|
|
|
|
|
viewModel = viewModel,
|
|
|
|
|
onBack = { navController.popBackStack() },
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-05-14 02:43:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun startPlaylistPlayback(
|
|
|
|
|
context: Context,
|
|
|
|
|
playlistId: String,
|
|
|
|
|
) {
|
|
|
|
|
ContextCompat.startForegroundService(
|
|
|
|
|
context,
|
|
|
|
|
Intent(context, PlaybackService::class.java).apply {
|
|
|
|
|
action = PlaybackService.ACTION_START_PLAYLIST
|
|
|
|
|
putExtra(PlaybackService.EXTRA_PLAYLIST_ID, playlistId)
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
}
|