media3 playback related code, not working though
This commit is contained in:
parent
2e4bca966a
commit
b614106dd9
|
@ -23,6 +23,15 @@
|
|||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.PlaybackService"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaSessionService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -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<MediaBrowser>
|
||||
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<MediaItem> = rootFuture.get()!!
|
||||
val root: MediaItem = result.value!!
|
||||
// pushPathStack(root)
|
||||
},
|
||||
MoreExecutors.directExecutor()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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}")
|
||||
},
|
||||
|
|
|
@ -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<Folder> = listOf(),
|
||||
songs: List<Song> = listOf(),
|
||||
onFolderItemClicked: (Folder) -> Unit = {},
|
||||
onSongItemClicked: (Song, List<Song>) -> 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<Song>) -> 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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user