feat: sync Playlists, wip: pair songs

This commit is contained in:
2026-05-14 02:43:49 +02:00
parent 26115f773f
commit e2ab026e84
36 changed files with 2324 additions and 34 deletions

View File

@@ -0,0 +1,128 @@
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
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,
) {
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)
navController.navigate(Routes.nowPlaying(playlist.id))
}
},
onOpenSettings = {
navController.navigate(Routes.Settings)
},
)
}
composable(Routes.Settings) {
SettingsScreen(
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,
onBack = { navController.popBackStack() },
)
}
}
}
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)
},
)
}