From b614106dd9ff8d1012e07d6f31afaf1004ac1dd3 Mon Sep 17 00:00:00 2001 From: Gary Wang Date: Tue, 23 Nov 2021 00:08:30 +0800 Subject: [PATCH] media3 playback related code, not working though --- app/src/main/AndroidManifest.xml | 9 ++ .../java/net/blumia/pcmdroid/MainActivity.kt | 56 +++++++++- .../pcmdroid/service/PlaybackService.kt | 61 +++++++++++ .../java/net/blumia/pcmdroid/ui/NavGraph.kt | 13 +++ .../pcmdroid/ui/screen/main/MainPlayer.kt | 103 +++++++++++------- 5 files changed, 200 insertions(+), 42 deletions(-) create mode 100644 app/src/main/java/net/blumia/pcmdroid/service/PlaybackService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fc6adb3..5914718 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -23,6 +23,15 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/net/blumia/pcmdroid/MainActivity.kt b/app/src/main/java/net/blumia/pcmdroid/MainActivity.kt index c56cec8..2d913cd 100644 --- a/app/src/main/java/net/blumia/pcmdroid/MainActivity.kt +++ b/app/src/main/java/net/blumia/pcmdroid/MainActivity.kt @@ -1,5 +1,6 @@ package net.blumia.pcmdroid +import android.content.ComponentName import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -9,12 +10,25 @@ import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview +import androidx.media3.common.MediaItem +import androidx.media3.common.util.UnstableApi +import androidx.media3.session.LibraryResult +import androidx.media3.session.MediaBrowser +import androidx.media3.session.SessionToken +import com.google.common.util.concurrent.ListenableFuture +import com.google.common.util.concurrent.MoreExecutors +import net.blumia.pcmdroid.service.PlaybackService import net.blumia.pcmdroid.ui.NavGraph import net.blumia.pcmdroid.ui.screen.main.MainPlayer import net.blumia.pcmdroid.ui.theme.PrivateCloudMusicTheme +import java.lang.Exception class MainActivity : ComponentActivity() { + private lateinit var browserFuture: ListenableFuture + private val browser: MediaBrowser? + get() = if (browserFuture.isDone) browserFuture.get() else null + private val model: MainViewModel by viewModels { MainViewModelFactory((application as MainApplication).repository) } @@ -25,9 +39,49 @@ class MainActivity : ComponentActivity() { PrivateCloudMusicTheme { // A surface container using the 'background' color from the theme Surface(color = MaterialTheme.colors.background) { - NavGraph(model) + NavGraph(model, browser) } } } } + + override fun onStart() { + super.onStart() + initializeBrowser() + } + + override fun onStop() { + releaseBrowser() + super.onStop() + } + + @androidx.annotation.OptIn(UnstableApi::class) + private fun initializeBrowser() { + browserFuture = + MediaBrowser.Builder( + this, + SessionToken(this, ComponentName(this, PlaybackService::class.java)) + ) + .buildAsync() + browserFuture.addListener({ pushRoot() }, MoreExecutors.directExecutor()) + } + + private fun releaseBrowser() { + MediaBrowser.releaseFuture(browserFuture) + } + + private fun pushRoot() { + // browser can be initialized many times + // only push root at the first initialization + val browser = this.browser ?: return + val rootFuture = browser.getLibraryRoot(/* params= */ null) + rootFuture.addListener( + { + val result: LibraryResult = rootFuture.get()!! + val root: MediaItem = result.value!! +// pushPathStack(root) + }, + MoreExecutors.directExecutor() + ) + } } \ No newline at end of file diff --git a/app/src/main/java/net/blumia/pcmdroid/service/PlaybackService.kt b/app/src/main/java/net/blumia/pcmdroid/service/PlaybackService.kt new file mode 100644 index 0000000..6face65 --- /dev/null +++ b/app/src/main/java/net/blumia/pcmdroid/service/PlaybackService.kt @@ -0,0 +1,61 @@ +package net.blumia.pcmdroid.service + +import android.app.PendingIntent.FLAG_IMMUTABLE +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.content.Intent +import android.os.Build +import androidx.core.app.TaskStackBuilder +import androidx.media3.common.AudioAttributes +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.session.MediaLibraryService +import androidx.media3.session.MediaSession +import net.blumia.pcmdroid.MainActivity + +class PlaybackService : MediaLibraryService() { + + private lateinit var player: ExoPlayer + private lateinit var mediaLibrarySession: MediaLibrarySession + private val librarySessionCallback = CustomMediaLibrarySessionCallback() + + private inner class CustomMediaLibrarySessionCallback + : MediaLibrarySession.MediaLibrarySessionCallback + { + } + + override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaLibrarySession { + return mediaLibrarySession + } + + override fun onCreate() { + super.onCreate() + initializeSessionAndPlayer() + } + + override fun onDestroy() { + player.release() + mediaLibrarySession.release() + super.onDestroy() + } + + private fun initializeSessionAndPlayer() { + player = + ExoPlayer.Builder(this) + .setAudioAttributes(AudioAttributes.DEFAULT, true) + .build() + + val mainActivityIntent = Intent(this, MainActivity::class.java) + val pendingIntent = + TaskStackBuilder.create(this).run { + addNextIntent(mainActivityIntent) + + val immutableFlag = if (Build.VERSION.SDK_INT >= 23) FLAG_IMMUTABLE else 0 + getPendingIntent(0, immutableFlag or FLAG_UPDATE_CURRENT) + } + + mediaLibrarySession = + MediaLibrarySession.Builder(this, player, librarySessionCallback) +// .setMediaItemFiller(CustomMediaItemFiller()) + .setSessionActivity(pendingIntent!!) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/net/blumia/pcmdroid/ui/NavGraph.kt b/app/src/main/java/net/blumia/pcmdroid/ui/NavGraph.kt index de69556..a4034d8 100644 --- a/app/src/main/java/net/blumia/pcmdroid/ui/NavGraph.kt +++ b/app/src/main/java/net/blumia/pcmdroid/ui/NavGraph.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.lifecycle.MutableLiveData +import androidx.media3.common.MediaItem +import androidx.media3.session.MediaBrowser import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -30,6 +32,7 @@ object MainDestinations { @Composable fun NavGraph( viewModel: MainViewModel, + browser: MediaBrowser?, navController: NavHostController = rememberNavController(), startDestination: String = MainDestinations.MAIN_ROUTE, ) { @@ -62,6 +65,16 @@ fun NavGraph( onFolderItemClicked = { folder -> viewModel.fetchFolder(folder) }, + onSongItemClicked = { song, songs -> + Log.d("vvv", song.url + browser.toString()) + val playlist = browser ?: return@MainPlayer + + val item = MediaItem.Builder().setUri(song.url).build() + + playlist.setMediaItem(item) + playlist.prepare() + playlist.play() + }, onEditServerActionTriggered = { server -> navController.navigate( "${MainDestinations.EDIT_SERVER_ROUTE}?id=${server.serverId}") }, diff --git a/app/src/main/java/net/blumia/pcmdroid/ui/screen/main/MainPlayer.kt b/app/src/main/java/net/blumia/pcmdroid/ui/screen/main/MainPlayer.kt index 764eaa9..94e7cff 100644 --- a/app/src/main/java/net/blumia/pcmdroid/ui/screen/main/MainPlayer.kt +++ b/app/src/main/java/net/blumia/pcmdroid/ui/screen/main/MainPlayer.kt @@ -29,6 +29,62 @@ import net.blumia.pcmdroid.model.Folder import net.blumia.pcmdroid.model.Server import net.blumia.pcmdroid.model.Song +@Preview(showBackground = true) +@OptIn(ExperimentalMaterialApi::class) +@Composable +fun FileList( + modifier: Modifier = Modifier, + currentFolder: Folder? = null, + folders: List = listOf(), + songs: List = listOf(), + onFolderItemClicked: (Folder) -> Unit = {}, + onSongItemClicked: (Song, List) -> Unit = { _, _ -> }, +) { + Column( + modifier = modifier + ) { + + Breadcrumb( + folder = currentFolder, + onFolderClicked = onFolderItemClicked, + ) + + folders.forEach { folder -> + ListItem( + modifier = Modifier.clickable { + onFolderItemClicked(folder) + }, + icon = { + Icon( + imageVector = Icons.Filled.FolderOpen, + contentDescription = null, + ) + }, + text = { + Text(folder.displayName()) + } + ) + } + + songs.forEach { song -> + ListItem( + modifier = Modifier.clickable { + onSongItemClicked(song, songs) + }, + icon = { + Icon( + imageVector = Icons.Filled.MusicNote, + contentDescription = null + ) + }, + text = { + Text(song.displayName()) + } + ) + } + } +} + @Preview(showBackground = true) @OptIn(ExperimentalMaterialApi::class) @Composable @@ -82,6 +138,7 @@ fun MainPlayer( addServerActionTriggered: () -> Unit = {}, onServerItemIconClicked: (Server) -> Unit = {}, onFolderItemClicked: (Folder) -> Unit = {}, + onSongItemClicked: (Song, List) -> Unit = { _, _ -> }, onEditServerActionTriggered: (Server) -> Unit = {}, onDeleteServerActionTriggered: (Server) -> Unit = {}, settingsActionTriggered: () -> Unit = {}, @@ -160,49 +217,13 @@ fun MainPlayer( Column( modifier = Modifier.fillMaxSize() ) { - Column( + FileList( modifier = Modifier .weight(1f, fill = true) - .verticalScroll(rememberScrollState()) - ) { - - Breadcrumb( - folder = currentFolder, - onFolderClicked = onFolderItemClicked, - ) - - folders.forEach { folder -> - ListItem( - modifier = Modifier.clickable { - onFolderItemClicked(folder) - }, - icon = { - Icon( - imageVector = Icons.Filled.FolderOpen, - contentDescription = null, - ) - }, - text = { - Text(folder.displayName()) - } - ) - } - - songs.forEach { song -> - ListItem( - modifier = Modifier.clickable { }, - icon = { - Icon( - imageVector = Icons.Filled.MusicNote, - contentDescription = null - ) - }, - text = { - Text(song.displayName()) - } - ) - } - } + .verticalScroll(rememberScrollState()), + currentFolder, folders, songs, + onFolderItemClicked, onSongItemClicked + ) NowPlaying() } }