From 4095e345993b5b38c10be67fab295bcd67f3ae41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= Date: Wed, 16 Oct 2024 14:53:19 +0200 Subject: [PATCH] Add server picker in "Examples" (#752) --- .../srgssr/pillarbox/demo/MainNavigation.kt | 30 ++- .../demo/ui/examples/InsertContentView.kt | 245 ++++++++++++++++-- 2 files changed, 245 insertions(+), 30 deletions(-) diff --git a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt index 756f3ff22..d61814211 100644 --- a/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt +++ b/pillarbox-demo/src/main/java/ch/srgssr/pillarbox/demo/MainNavigation.kt @@ -209,16 +209,16 @@ private fun ListsMenu( ) { val context = LocalContext.current val currentServerUrl = currentServer.toString() - val servers = remember { getServers(context) } + val servers = remember { getServers(context).groupBy { it.serverName }.values } - servers.forEachIndexed { index, (server, environmentConfig) -> + servers.forEachIndexed { index, environmentConfig -> environmentConfig.forEach { config -> val isSelected = currentServerUrl == config.host.toString() && currentForceSAM == config.forceSAM && currentLocation == config.location DropdownMenuItem( - text = { Text(text = "$server - ${config.name}") }, + text = { Text(text = config.displayName) }, onClick = { onServerSelected(config.host, config.forceSAM, config.location) isMenuVisible = false @@ -236,7 +236,7 @@ private fun ListsMenu( ) } - if (index < servers.lastIndex) { + if (index < servers.size - 1) { HorizontalDivider( modifier = Modifier.padding(vertical = MaterialTheme.paddings.small), color = MaterialTheme.colorScheme.outline, @@ -246,41 +246,47 @@ private fun ListsMenu( } } -private fun getServers(context: Context): List>> { - val ilServers = listOf(null, "CH", "WW").map { location -> +internal fun getServers(context: Context): List { + val ilServers = listOf(null, "CH", "WW").flatMap { location -> val name = location?.let { "IL ($location)" } ?: "IL" - name to listOf( + listOf( EnvironmentConfig( name = context.getString(R.string.production), + serverName = name, host = IlHost.PROD, location = location, ), EnvironmentConfig( name = context.getString(R.string.stage), + serverName = name, host = IlHost.STAGE, location = location, ), EnvironmentConfig( name = context.getString(R.string.test), + serverName = name, host = IlHost.TEST, location = location, ), ) } - val samServer = "SAM" to listOf( + val samServer = listOf( EnvironmentConfig( name = context.getString(R.string.production), + serverName = "SAM", host = IlHost.PROD, forceSAM = true, ), EnvironmentConfig( name = context.getString(R.string.stage), + serverName = "SAM", host = IlHost.STAGE, forceSAM = true, ), EnvironmentConfig( name = context.getString(R.string.test), + serverName = "SAM", host = IlHost.TEST, forceSAM = true, ) @@ -289,12 +295,16 @@ private fun getServers(context: Context): List DemoItem.URN( + isValidUrn -> DemoItem.URN( title = uri, urn = uri, + host = checkNotNull(environmentConfig).host, + forceSAM = environmentConfig.forceSAM, + forceLocation = environmentConfig.location, ) else -> error("Invalid URI: $uri") @@ -74,10 +105,25 @@ fun InsertContentView( modifier: Modifier = Modifier, onPlayClick: (DemoItem) -> Unit ) { - var insertContentData by remember { + val (insertContentData, setInsertContentData) = remember { mutableStateOf(InsertContentData()) } + InsertContentViewInternal( + insertContentData = insertContentData, + modifier = modifier, + setInsertContentData = setInsertContentData, + onPlayClick = onPlayClick, + ) +} + +@Composable +private fun InsertContentViewInternal( + insertContentData: InsertContentData, + modifier: Modifier = Modifier, + setInsertContentData: (InsertContentData) -> Unit, + onPlayClick: (DemoItem) -> Unit, +) { Column( modifier = modifier.padding(bottom = MaterialTheme.paddings.small), verticalArrangement = Arrangement.spacedBy(MaterialTheme.paddings.small), @@ -89,8 +135,8 @@ fun InsertContentView( modifier = Modifier .fillMaxWidth() .padding(horizontal = MaterialTheme.paddings.mini), - onValueChange = { insertContentData = insertContentData.copy(uri = it) }, - onClearClick = { insertContentData = InsertContentData() } + onValueChange = { setInsertContentData(insertContentData.copy(uri = it)) }, + onClearClick = { setInsertContentData(InsertContentData()) }, ) AnimatedVisibility(visible = insertContentData.isValidUrl) { @@ -100,12 +146,33 @@ fun InsertContentView( modifier = Modifier .fillMaxWidth() .padding(horizontal = MaterialTheme.paddings.mini), - onValueChange = { insertContentData = insertContentData.copy(licenseUrl = it) }, - onClearClick = { insertContentData = insertContentData.copy(licenseUrl = "") } + onValueChange = { setInsertContentData(insertContentData.copy(licenseUrl = it)) }, + onClearClick = { setInsertContentData(insertContentData.copy(licenseUrl = "")) }, ) } - AnimatedVisibility(visible = insertContentData.uri.isNotBlank()) { + AnimatedVisibility(visible = insertContentData.isValidUrn) { + val context = LocalContext.current + val servers = remember { getServers(context) } + + LaunchedEffect(servers) { + setInsertContentData(insertContentData.copy(environmentConfig = servers[0])) + } + + if (insertContentData.environmentConfig != null) { + InsertContentDropDown( + value = insertContentData.environmentConfig, + label = stringResource(R.string.server), + entries = servers, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = MaterialTheme.paddings.mini), + onEntrySelected = { setInsertContentData(insertContentData.copy(environmentConfig = it)) }, + ) + } + } + + AnimatedVisibility(visible = insertContentData.hasValidInput) { Button(onClick = { onPlayClick(insertContentData.toDemoItem()) }) { Text(text = stringResource(R.string.play)) } @@ -146,23 +213,161 @@ private fun InsertContentTextField( } @Composable -@Preview(showBackground = true) -private fun InsertContentViewPreview() { - PillarboxTheme { - InsertContentView { +private fun InsertContentDropDown( + value: EnvironmentConfig, + label: String, + entries: List, + modifier: Modifier = Modifier, + onEntrySelected: (entry: EnvironmentConfig) -> Unit, +) { + var showDropdownMenu by remember { mutableStateOf(false) } + + Box(modifier = modifier) { + val interactionSource = remember { MutableInteractionSource() } + val isPressed by interactionSource.collectIsPressedAsState() + + LaunchedEffect(isPressed) { + if (isPressed) { + showDropdownMenu = true + } + } + + OutlinedTextField( + value = value.displayName, + onValueChange = {}, + modifier = Modifier.fillMaxWidth(), + readOnly = true, + label = { Text(text = label) }, + trailingIcon = { + val iconRotation by animateFloatAsState( + targetValue = if (showDropdownMenu) -180f else 0f, + label = "icon_rotation_animation", + ) + + Icon( + imageVector = Icons.Default.ExpandMore, + contentDescription = null, + modifier = Modifier.rotate(iconRotation), + ) + }, + interactionSource = interactionSource, + ) + + DropdownMenu( + expanded = showDropdownMenu, + onDismissRequest = { showDropdownMenu = false }, + offset = DpOffset(x = 0.dp, y = MaterialTheme.paddings.small), + ) { + entries.forEach { entry -> + DropdownMenuItem( + text = { Text(text = entry.displayName) }, + onClick = { + onEntrySelected(entry) + showDropdownMenu = false + }, + ) + } } } } @Composable -@Preview(showBackground = true) -private fun InsertContentTextFieldPreview() { +@Preview(group = "InsertContentView", showBackground = true) +private fun InsertContentViewEmptyPreview() { + val (insertContentData, setInsertContentData) = remember { + mutableStateOf(InsertContentData()) + } + + PillarboxTheme { + InsertContentViewInternal( + insertContentData = insertContentData, + setInsertContentData = setInsertContentData, + onPlayClick = {}, + ) + } +} + +@Composable +@Preview(group = "InsertContentView", showBackground = true) +private fun InsertContentViewUrlPreview() { + val (insertContentData, setInsertContentData) = remember { + mutableStateOf(InsertContentData(uri = "https://www.example.com/", licenseUrl = "https://www.license.com/")) + } + + PillarboxTheme { + InsertContentViewInternal( + insertContentData = insertContentData, + setInsertContentData = setInsertContentData, + onPlayClick = {}, + ) + } +} + +@Composable +@Preview(group = "InsertContentView", showBackground = true) +private fun InsertContentViewUrnPreview() { + val (insertContentData, setInsertContentData) = remember { + mutableStateOf(InsertContentData(uri = "urn:rts:video:1234567")) + } + + PillarboxTheme { + InsertContentViewInternal( + insertContentData = insertContentData, + setInsertContentData = setInsertContentData, + onPlayClick = {}, + ) + } +} + +@Composable +@Preview(group = "InsertContentTextField", showBackground = true) +private fun InsertContentTextFieldEmptyPreview() { + val (value, setValue) = remember { + mutableStateOf("") + } + PillarboxTheme { InsertContentTextField( - value = "Value", - label = "Label", - onValueChange = {}, - onClearClick = {} + value = value, + label = stringResource(R.string.enter_url_or_urn), + onValueChange = setValue, + onClearClick = { setValue("") }, + ) + } +} + +@Composable +@Preview(group = "InsertContentTextField", showBackground = true) +private fun InsertContentTextFieldValuePreview() { + val (value, setValue) = remember { + mutableStateOf("Value") + } + + PillarboxTheme { + InsertContentTextField( + value = value, + label = stringResource(R.string.enter_url_or_urn), + onValueChange = setValue, + onClearClick = { setValue("") }, + ) + } +} + +@Composable +@Preview(showBackground = true) +private fun InsertContentDropDownPreview() { + val context = LocalContext.current + val servers = getServers(context) + val (server, setServer) = remember { + mutableStateOf(servers[0]) + } + + PillarboxTheme { + InsertContentDropDown( + value = server, + label = stringResource(R.string.enter_url_or_urn), + entries = servers, + onEntrySelected = setServer, ) } }