From 1956170e33e7bfa20ddbedab52ba1838245a3652 Mon Sep 17 00:00:00 2001 From: Isaac Iwasaki Date: Fri, 4 Oct 2024 05:36:58 +0700 Subject: [PATCH] tracks virtual queue implemented --- .idea/misc.xml | 1 - app/src/main/AndroidManifest.xml | 16 ++- .../java/usr/empty/player/MainActivity.kt | 24 +++-- .../main/java/usr/empty/player/NotaQueue.kt | 9 ++ .../java/usr/empty/player/PlayerService.kt | 97 +++++++++++++++++-- .../java/usr/empty/player/ui/theme/Color.kt | 11 --- 6 files changed, 128 insertions(+), 30 deletions(-) create mode 100644 app/src/main/java/usr/empty/player/NotaQueue.kt delete mode 100644 app/src/main/java/usr/empty/player/ui/theme/Color.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 0ad17cb..8978d23 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d4e9dbe..0cf4f82 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,13 @@ - - + + + + + - + \ No newline at end of file diff --git a/app/src/main/java/usr/empty/player/MainActivity.kt b/app/src/main/java/usr/empty/player/MainActivity.kt index e884543..1234f11 100644 --- a/app/src/main/java/usr/empty/player/MainActivity.kt +++ b/app/src/main/java/usr/empty/player/MainActivity.kt @@ -6,6 +6,7 @@ import android.net.Uri import android.os.Bundle import android.os.Environment import android.provider.Settings +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent @@ -19,7 +20,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.MaterialTheme @@ -34,6 +35,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.media3.common.util.UnstableApi import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import usr.empty.player.ui.theme.PlayerTheme @@ -45,6 +47,7 @@ inline fun nullifyException(block: () -> T) = try { null } +@UnstableApi class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -56,7 +59,10 @@ class MainActivity : ComponentActivity() { startActivity(intent) } - startService(Intent(this, PlayerService::class.java)) + startForegroundService(Intent(this, PlayerService::class.java).apply { + putExtra("check", "empty") + putExtra("type", "start") + }) enableEdgeToEdge() setContent { @@ -74,6 +80,7 @@ fun uriToPath(uri: Uri): String { return "/storage/" + result.replace("primary:", "/storage/emulated/0/") } +@UnstableApi @Composable fun MainLayout(modifier: Modifier = Modifier) { val context = LocalContext.current @@ -134,20 +141,24 @@ fun MainLayout(modifier: Modifier = Modifier) { } } +@UnstableApi @Composable -fun NotaList(notas: List, modifier: Modifier = Modifier) { +fun NotaList(notas: List, modifier: Modifier = Modifier, queueId: Int = notas.hashCode()) { val context = LocalContext.current LazyColumn( modifier = modifier, ) { - items(notas) { nota -> + itemsIndexed(notas) { index, nota -> Column(modifier = Modifier .fillParentMaxWidth() .clickable { + Log.d("meow", "queueId: $queueId") context.startService(Intent(context, PlayerService::class.java).apply { putExtra("check", "empty") - putExtra("type", "nota") - putExtra("nota", Json.encodeToString(nota)) + putExtra("type", "queue") + putExtra("queueId", queueId) + putExtra("queue", Json.encodeToString(notas)) + putExtra("start", index) }) } .padding(4.dp) @@ -163,6 +174,7 @@ fun NotaList(notas: List, modifier: Modifier = Modifier) { } } +@UnstableApi @Preview @Composable fun Preview() { diff --git a/app/src/main/java/usr/empty/player/NotaQueue.kt b/app/src/main/java/usr/empty/player/NotaQueue.kt new file mode 100644 index 0000000..e004e02 --- /dev/null +++ b/app/src/main/java/usr/empty/player/NotaQueue.kt @@ -0,0 +1,9 @@ +package usr.empty.player + +import java.util.LinkedList + +class NotaQueue : LinkedList() { + var currentId = 0 + val current + get() = if (currentId < size) this[currentId] else null +} \ No newline at end of file diff --git a/app/src/main/java/usr/empty/player/PlayerService.kt b/app/src/main/java/usr/empty/player/PlayerService.kt index a71b6fd..0acddd4 100644 --- a/app/src/main/java/usr/empty/player/PlayerService.kt +++ b/app/src/main/java/usr/empty/player/PlayerService.kt @@ -1,28 +1,71 @@ package usr.empty.player +import android.annotation.SuppressLint +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context import android.content.Intent +import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK import android.util.Log +import androidx.core.app.NotificationCompat import androidx.media3.common.MediaItem +import androidx.media3.common.Player +import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.session.MediaSession import androidx.media3.session.MediaSessionService +import androidx.media3.session.MediaStyleNotificationHelper +import androidx.media3.ui.PlayerNotificationManager import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import kotlinx.serialization.json.Json -import java.util.concurrent.ConcurrentLinkedDeque +@UnstableApi class PlayerService : MediaSessionService() { private lateinit var mediaSession: MediaSession lateinit var player: ExoPlayer - private var virtualQueue = ConcurrentLinkedDeque() + private lateinit var playerNotificationManager: PlayerNotificationManager + private val notificationId = 1004 + private var virtualQueue = NotaQueue() + private var queueId = -1 + @SuppressLint("ForegroundServiceType") override fun onCreate() { super.onCreate() - with(ExoPlayer.Builder(this).build()) { - player = this - mediaSession = MediaSession.Builder(this@PlayerService, this).setCallback(MediaSessionCallback()).build() - } + player = ExoPlayer.Builder(this).build() + player.addListener(object : Player.Listener { + override fun onIsPlayingChanged(isPlaying: Boolean) { + if (!isPlaying) { + if (player.playbackState == Player.STATE_ENDED) next() + } + super.onIsPlayingChanged(isPlaying) + } + }) + mediaSession = MediaSession.Builder(this, player).setCallback(MediaSessionCallback()).build() + playerNotificationManager = PlayerNotificationManager.Builder(this, notificationId, "emptyynes") + .setMediaDescriptionAdapter(object : PlayerNotificationManager.MediaDescriptionAdapter { + override fun getCurrentContentTitle(player: Player) = "title?" + + override fun createCurrentContentIntent(player: Player) = null + + override fun getCurrentContentText(player: Player) = "context?" + + override fun getCurrentLargeIcon(player: Player, callback: PlayerNotificationManager.BitmapCallback) = null + }) + .build() + .apply { + setPriority(NotificationCompat.PRIORITY_DEFAULT) + setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + setPlayer(player) + setMediaSessionToken(mediaSession.platformToken) + } + + val notificationManager: NotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(NotificationChannel("emptyynes", "Channel", NotificationManager.IMPORTANCE_LOW)) + + val nBuilder = NotificationCompat.Builder(this, "emptyynes").setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession)) + startForeground(notificationId, nBuilder.build(), FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -31,24 +74,62 @@ class PlayerService : MediaSessionService() { if (getStringExtra("check") != "empty") return@run handleCommand(getStringExtra("type")!!, this) } - return super.onStartCommand(intent, flags, startId) + super.onStartCommand(intent, flags, startId) + return START_STICKY } private fun handleCommand(type: String, intent: Intent) { when (type) { + "start" -> { + + } + + "queue" -> { + Log.d("meow", "queue!") + intent.getIntExtra("queueId", -1).let { newQueueId -> + if (newQueueId == queueId) return@let + virtualQueue.clear() + Log.d("meow", "queue reset!") + virtualQueue.addAll(Json.decodeFromString>(intent.getStringExtra("queue")!!).map { Nota(it) }) + queueId = newQueueId + } + virtualQueue.currentId = intent.getIntExtra("start", -1) + Log.d("meow", "currentId: ${virtualQueue.currentId}") + virtualQueue.current?.let { + it.prepare(player) + player.prepare() + player.play() + } + } + + "next" -> { + + } + "nota" -> { + virtualQueue.clear() virtualQueue.add(Nota(Json.decodeFromString(intent.getStringExtra("nota")!!))) -// virtualQueue.first.prepare(player) virtualQueue.last.prepare(player) player.prepare() player.play() Log.d("nya", virtualQueue.last.mediaSource.toString()) } + "pause" -> player.pause() "play" -> player.play() } } + fun next() { + virtualQueue.currentId++ + virtualQueue.current?.let { + it.prepare(player) + player.prepare() + player.play() + } + Log.d("meow", "next!") + } + override fun onDestroy() { player.release() mediaSession.release() diff --git a/app/src/main/java/usr/empty/player/ui/theme/Color.kt b/app/src/main/java/usr/empty/player/ui/theme/Color.kt deleted file mode 100644 index 617afae..0000000 --- a/app/src/main/java/usr/empty/player/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package usr.empty.player.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file