mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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:
parent
7b9cfd1964
commit
f6116a121a
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user