mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Load preset input from JSON file in EffectActivity
The JSON file contains a list of playlists, each with a name and a list of media items. The EffectActivity loads the JSON file and creates a list of PlaylistHolder objects, which contain the playlist name and the list of media items. PiperOrigin-RevId: 703069411
This commit is contained in:
parent
f1a0e4b0b7
commit
b6724e2115
27
demos/effect/src/main/assets/media.playlist.json
Normal file
27
demos/effect/src/main/assets/media.playlist.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Cats -> Dogs",
|
||||||
|
"playlist": [
|
||||||
|
{
|
||||||
|
"uri": "https://html5demos.com/assets/dizzy.mp4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Android Block -> Dogs -> BigBuckBunny",
|
||||||
|
"playlist": [
|
||||||
|
{
|
||||||
|
"uri": "https://storage.googleapis.com/exoplayer-test-media-0/android-block-1080-hevc.mp4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uri": "https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
@ -49,6 +49,7 @@ import androidx.compose.ui.res.dimensionResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.MediaItem
|
import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.common.util.Util.SDK_INT
|
import androidx.media3.common.util.Util.SDK_INT
|
||||||
@ -60,11 +61,16 @@ class EffectActivity : ComponentActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent { EffectDemo() }
|
val playlistHolderList = mutableStateOf<List<PlaylistHolder>>(emptyList())
|
||||||
|
lifecycleScope.launch {
|
||||||
|
playlistHolderList.value =
|
||||||
|
loadPlaylistsFromJson(JSON_FILENAME, this@EffectActivity, "EffectActivity")
|
||||||
|
}
|
||||||
|
setContent { EffectDemo(playlistHolderList.value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EffectDemo() {
|
private fun EffectDemo(playlistHolderList: List<PlaylistHolder>) {
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@ -100,7 +106,7 @@ class EffectActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InputChooser(onException: (String) -> Unit, onNewUri: (Uri) -> Unit) {
|
private fun InputChooser(onException: (String) -> Unit, onNewUri: (Uri) -> Unit) {
|
||||||
var showLocalFilePicker by remember { mutableStateOf(false) }
|
var showLocalFilePicker by remember { mutableStateOf(false) }
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.small_padding)),
|
modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.small_padding)),
|
||||||
@ -128,7 +134,7 @@ class EffectActivity : ComponentActivity() {
|
|||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun LocalFilePicker(onException: (String) -> Unit, onFileSelected: (Uri) -> Unit) {
|
private fun LocalFilePicker(onException: (String) -> Unit, onFileSelected: (Uri) -> Unit) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val localFilePickerLauncher =
|
val localFilePickerLauncher =
|
||||||
rememberLauncherForActivityResult(
|
rememberLauncherForActivityResult(
|
||||||
@ -166,7 +172,7 @@ class EffectActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerScreen(exoPlayer: ExoPlayer) {
|
private fun PlayerScreen(exoPlayer: ExoPlayer) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { PlayerView(context).apply { player = exoPlayer } },
|
factory = { PlayerView(context).apply { player = exoPlayer } },
|
||||||
@ -177,9 +183,13 @@ class EffectActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Effects(onException: (String) -> Unit) {
|
private fun Effects(onException: (String) -> Unit) {
|
||||||
Button(onClick = { onException("Button is not yet implemented.") }) {
|
Button(onClick = { onException("Button is not yet implemented.") }) {
|
||||||
Text(text = stringResource(id = R.string.apply_effects))
|
Text(text = stringResource(id = R.string.apply_effects))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val JSON_FILENAME = "media.playlist.json"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.demo.effect
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.JsonReader
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
internal suspend fun loadPlaylistsFromJson(
|
||||||
|
jsonFilename: String,
|
||||||
|
context: Context,
|
||||||
|
tag: String,
|
||||||
|
): List<PlaylistHolder> =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
context.assets.open(jsonFilename).use { inputStream ->
|
||||||
|
val reader = JsonReader(InputStreamReader(inputStream, StandardCharsets.UTF_8))
|
||||||
|
val playlistHolders = buildList {
|
||||||
|
reader.beginArray()
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
readPlaylist(reader)?.let { add(it) }
|
||||||
|
}
|
||||||
|
reader.endArray()
|
||||||
|
}
|
||||||
|
playlistHolders
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(tag, context.getString(R.string.playlist_loading_error, jsonFilename, e))
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readPlaylist(reader: JsonReader): PlaylistHolder? {
|
||||||
|
val playlistHolder = PlaylistHolder("", emptyList())
|
||||||
|
reader.beginObject()
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
val name = reader.nextName()
|
||||||
|
if (name.equals("name")) {
|
||||||
|
playlistHolder.title = reader.nextString()
|
||||||
|
} else if (name.equals("playlist")) {
|
||||||
|
playlistHolder.mediaItems = buildList {
|
||||||
|
reader.beginArray()
|
||||||
|
while (reader.hasNext()) {
|
||||||
|
reader.beginObject()
|
||||||
|
reader.nextName()
|
||||||
|
add(MediaItem.fromUri(Uri.parse(reader.nextString())))
|
||||||
|
reader.endObject()
|
||||||
|
}
|
||||||
|
reader.endArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.endObject()
|
||||||
|
// Only return the playlistHolder object if it has media items
|
||||||
|
return if (playlistHolder.mediaItems.isNotEmpty()) playlistHolder else null
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class PlaylistHolder(var title: String, var mediaItems: List<MediaItem>)
|
@ -19,4 +19,5 @@
|
|||||||
<string name="choose_local_file">Choose local file</string>
|
<string name="choose_local_file">Choose local file</string>
|
||||||
<string name="apply_effects">Apply effects</string>
|
<string name="apply_effects">Apply effects</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
|
<string name="playlist_loading_error">Error loading playlist from %1$s: %2$s</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user