From dc21f3add2c9ac8916f5d59ec1c2b84266d37bdf Mon Sep 17 00:00:00 2001 From: shahddaghash Date: Tue, 10 Dec 2024 02:48:03 -0800 Subject: [PATCH] Add preset input picker functionality Added functionality to `Choose preset input` button. The picker allows the user to select a preset input from a list of loaded preset playlists. The selected preset input is then used to populate the ExoPlayer with the corresponding media items. PiperOrigin-RevId: 704626144 --- .../media3/demo/effect/EffectActivity.kt | 111 ++++++++++++++---- demos/effect/src/main/res/values/strings.xml | 3 + 2 files changed, 92 insertions(+), 22 deletions(-) diff --git a/demos/effect/src/main/java/androidx/media3/demo/effect/EffectActivity.kt b/demos/effect/src/main/java/androidx/media3/demo/effect/EffectActivity.kt index bd50296a59..0bbb822b76 100644 --- a/demos/effect/src/main/java/androidx/media3/demo/effect/EffectActivity.kt +++ b/demos/effect/src/main/java/androidx/media3/demo/effect/EffectActivity.kt @@ -30,7 +30,10 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectable +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -88,10 +91,13 @@ class EffectActivity : ComponentActivity() { horizontalAlignment = Alignment.CenterHorizontally, ) { InputChooser( - onException = { coroutineScope.launch { snackbarHostState.showSnackbar(it) } } - ) { uri -> + playlistHolderList, + onException = { message -> + coroutineScope.launch { snackbarHostState.showSnackbar(message) } + }, + ) { mediaItems -> exoPlayer.apply { - setMediaItem(MediaItem.fromUri(uri)) + setMediaItems(mediaItems) prepare() } } @@ -106,44 +112,105 @@ class EffectActivity : ComponentActivity() { } @Composable - private fun InputChooser(onException: (String) -> Unit, onNewUri: (Uri) -> Unit) { - var showLocalFilePicker by remember { mutableStateOf(false) } + private fun InputChooser( + playlistHolderList: List, + onException: (String) -> Unit, + onNewMediaItems: (List) -> Unit, + ) { + var showPresetInputChooser by remember { mutableStateOf(false) } + var showLocalFileChooser by remember { mutableStateOf(false) } Row( modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.small_padding)), horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.small_padding)), ) { - Button(onClick = { onException("Button is not yet implemented.") }) { + Button(onClick = { showPresetInputChooser = true }) { Text(text = stringResource(id = R.string.choose_preset_input)) } - Button(onClick = { showLocalFilePicker = true }) { + Button(onClick = { showLocalFileChooser = true }) { Text(text = stringResource(id = R.string.choose_local_file)) } } - if (showLocalFilePicker) { - LocalFilePicker( - onException = { - onException(it) - showLocalFilePicker = false + if (showPresetInputChooser) { + if (playlistHolderList.isNotEmpty()) { + PresetInputChooser( + playlistHolderList, + onDismissRequest = { showPresetInputChooser = false }, + ) { mediaItems -> + onNewMediaItems(mediaItems) + showPresetInputChooser = false } - ) { uri -> - onNewUri(uri) - showLocalFilePicker = false + } else { + onException(stringResource(id = R.string.no_loaded_playlists_error)) + showPresetInputChooser = false + } + } + if (showLocalFileChooser) { + LocalFileChooser( + onException = { message -> + onException(message) + showLocalFileChooser = false + } + ) { mediaItems -> + onNewMediaItems(mediaItems) + showLocalFileChooser = false } } } + @Composable + private fun PresetInputChooser( + playlistHolderList: List, + onDismissRequest: () -> Unit, + onInputSelected: (List) -> Unit, + ) { + var selectedOption by remember { mutableStateOf(playlistHolderList.first()) } + + AlertDialog( + onDismissRequest = onDismissRequest, + title = { Text(stringResource(id = R.string.choose_preset_input)) }, + confirmButton = { + Button(onClick = { onInputSelected(selectedOption.mediaItems) }) { + Text(text = stringResource(id = R.string.ok)) + } + }, + text = { + Column { + playlistHolderList.forEach { playlistHolder -> + Row( + Modifier.fillMaxWidth() + .selectable( + (playlistHolder == selectedOption), + onClick = { selectedOption = playlistHolder }, + ), + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton( + selected = (playlistHolder == selectedOption), + onClick = { selectedOption = playlistHolder }, + ) + Text(playlistHolder.title) + } + } + } + }, + ) + } + @OptIn(UnstableApi::class) @Composable - private fun LocalFilePicker(onException: (String) -> Unit, onFileSelected: (Uri) -> Unit) { + private fun LocalFileChooser( + onException: (String) -> Unit, + onFileSelected: (List) -> Unit, + ) { val context = LocalContext.current - val localFilePickerLauncher = + val localFileChooserLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenDocument(), onResult = { uri: Uri? -> if (uri != null) { - onFileSelected(uri) + onFileSelected(listOf(MediaItem.fromUri(uri))) } else { - onException("File couldn't be opened. Please try again.") + onException(getString(R.string.can_not_open_file_error)) } }, ) @@ -152,9 +219,9 @@ class EffectActivity : ComponentActivity() { contract = ActivityResultContracts.RequestPermission(), onResult = { isGranted: Boolean -> if (isGranted) { - localFilePickerLauncher.launch(arrayOf("video/*")) + localFileChooserLauncher.launch(arrayOf("video/*")) } else { - onException("Permission was not granted.") + onException(getString(R.string.permission_not_granted_error)) } }, ) @@ -164,7 +231,7 @@ class EffectActivity : ComponentActivity() { else Manifest.permission.READ_EXTERNAL_STORAGE val permissionCheck = ContextCompat.checkSelfPermission(context, permission) if (permissionCheck == android.content.pm.PackageManager.PERMISSION_GRANTED) { - localFilePickerLauncher.launch(arrayOf("video/*")) + localFileChooserLauncher.launch(arrayOf("video/*")) } else { permissionLauncher.launch(permission) } diff --git a/demos/effect/src/main/res/values/strings.xml b/demos/effect/src/main/res/values/strings.xml index 8a48c99821..75d6cfd38c 100644 --- a/demos/effect/src/main/res/values/strings.xml +++ b/demos/effect/src/main/res/values/strings.xml @@ -20,4 +20,7 @@ Apply effects OK Error loading playlist from %1$s: %2$s + There are no loaded preset inputs. + "File couldn't be opened. Please try again." + "Permission was not granted."