Add functionality for Choose local file button

Added the functionality for `Choose local file` button to be able to go and select a local file from the device. The file was then displayed by using ExoPlayer inside PlayerView.

PiperOrigin-RevId: 693756565
This commit is contained in:
shahddaghash 2024-11-06 09:32:35 -08:00 committed by Copybara-Service
parent 7b9cfd1964
commit f6116a121a
3 changed files with 90 additions and 17 deletions

View File

@ -74,6 +74,7 @@ dependencies {
implementation 'androidx.compose.material3:material3'
implementation 'com.google.android.material:material:' + androidxMaterialVersion
implementation project(modulePrefix + 'lib-exoplayer')
implementation project(modulePrefix + 'lib-ui')
// For detecting and debugging leaks only. LeakCanary is not needed for demo app to work.

View File

@ -19,6 +19,8 @@
<uses-sdk/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
<application
android:allowBackup="false"

View File

@ -15,9 +15,14 @@
*/
package androidx.media3.demo.effect
import android.Manifest
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.OptIn
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -31,14 +36,23 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.media3.common.MediaItem
import androidx.media3.common.util.UnstableApi
import androidx.media3.common.util.Util.SDK_INT
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import kotlinx.coroutines.launch
@ -53,6 +67,10 @@ class EffectActivity : ComponentActivity() {
fun EffectDemo() {
val snackbarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val exoPlayer by remember {
mutableStateOf(ExoPlayer.Builder(context).build().apply { playWhenReady = true })
}
Scaffold(
modifier = Modifier.fillMaxSize(),
@ -64,18 +82,17 @@ class EffectActivity : ComponentActivity() {
horizontalAlignment = Alignment.CenterHorizontally,
) {
InputChooser(
onButtonClick = {
coroutineScope.launch {
snackbarHostState.showSnackbar(message = "Button is not yet implemented.")
}
onException = { coroutineScope.launch { snackbarHostState.showSnackbar(it) } }
) { uri ->
exoPlayer.apply {
setMediaItem(MediaItem.fromUri(uri))
prepare()
}
)
PlayerScreen()
}
PlayerScreen(exoPlayer)
Effects(
onButtonClick = {
coroutineScope.launch {
snackbarHostState.showSnackbar(message = "Button is not yet implemented.")
}
onException = { message ->
coroutineScope.launch { snackbarHostState.showSnackbar(message) }
}
)
}
@ -83,25 +100,76 @@ class EffectActivity : ComponentActivity() {
}
@Composable
fun InputChooser(onButtonClick: () -> Unit) {
fun InputChooser(onException: (String) -> Unit, onNewUri: (Uri) -> Unit) {
var showLocalFilePicker 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 = onButtonClick) {
Button(onClick = { onException("Button is not yet implemented.") }) {
Text(text = stringResource(id = R.string.choose_preset_input))
}
Button(onClick = onButtonClick) {
Button(onClick = { showLocalFilePicker = true }) {
Text(text = stringResource(id = R.string.choose_local_file))
}
}
if (showLocalFilePicker) {
LocalFilePicker(
onException = {
onException(it)
showLocalFilePicker = false
}
) { uri ->
onNewUri(uri)
showLocalFilePicker = false
}
}
}
@OptIn(UnstableApi::class)
@Composable
fun LocalFilePicker(onException: (String) -> Unit, onFileSelected: (Uri) -> Unit) {
val context = LocalContext.current
val localFilePickerLauncher =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.OpenDocument(),
onResult = { uri: Uri? ->
if (uri != null) {
onFileSelected(uri)
} else {
onException("File couldn't be opened. Please try again.")
}
},
)
val permissionLauncher =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isGranted: Boolean ->
if (isGranted) {
localFilePickerLauncher.launch(arrayOf("video/*"))
} else {
onException("Permission was not granted.")
}
},
)
LaunchedEffect(Unit) {
val permission =
if (SDK_INT >= 33) Manifest.permission.READ_MEDIA_VIDEO
else Manifest.permission.READ_EXTERNAL_STORAGE
val permissionCheck = ContextCompat.checkSelfPermission(context, permission)
if (permissionCheck == android.content.pm.PackageManager.PERMISSION_GRANTED) {
localFilePickerLauncher.launch(arrayOf("video/*"))
} else {
permissionLauncher.launch(permission)
}
}
}
@Composable
fun PlayerScreen() {
fun PlayerScreen(exoPlayer: ExoPlayer) {
val context = LocalContext.current
AndroidView(
factory = { PlayerView(context).apply {} },
factory = { PlayerView(context).apply { player = exoPlayer } },
modifier =
Modifier.height(dimensionResource(id = R.dimen.android_view_height))
.padding(all = dimensionResource(id = R.dimen.small_padding)),
@ -109,7 +177,9 @@ class EffectActivity : ComponentActivity() {
}
@Composable
fun Effects(onButtonClick: () -> Unit) {
Button(onClick = onButtonClick) { Text(text = stringResource(id = R.string.apply_effects)) }
fun Effects(onException: (String) -> Unit) {
Button(onClick = { onException("Button is not yet implemented.") }) {
Text(text = stringResource(id = R.string.apply_effects))
}
}
}