diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 134fd83..57f2fd5 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -67,6 +67,8 @@ dependencies {
implementation(libs.androidx.media3.session)
implementation(libs.androidx.room.runtime)
implementation(libs.com.google.devtools.ksp.gradle.plugin)
+ implementation(libs.kotlinx.coroutines.android)
+ implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
diff --git a/app/src/androidTest/java/usr/empty/player/ExampleInstrumentedTest.kt b/app/src/androidTest/java/usr/empty/player/ExampleInstrumentedTest.kt
deleted file mode 100644
index ad64afd..0000000
--- a/app/src/androidTest/java/usr/empty/player/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package usr.empty.player
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("usr.empty.player", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0cf4f82..eed58ac 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,6 +16,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
+ android:name=".EmptyApplication"
android:theme="@style/Theme.Player">
-
diff --git a/app/src/main/java/usr/empty/player/EmptyApplication.kt b/app/src/main/java/usr/empty/player/EmptyApplication.kt
new file mode 100644
index 0000000..1a7e4b2
--- /dev/null
+++ b/app/src/main/java/usr/empty/player/EmptyApplication.kt
@@ -0,0 +1,53 @@
+package usr.empty.player
+
+import android.app.Application
+import androidx.room.Room
+import usr.empty.player.database.AppDatabase
+import usr.empty.player.items.AlbumDescriptor
+import usr.empty.player.items.ArtistDescriptor
+import usr.empty.player.items.NotaDescriptor
+
+
+class EmptyApplication : Application() {
+ companion object {
+ private var _appInstance: EmptyApplication? = null
+
+ val appInstance: EmptyApplication
+ get() = _appInstance!!
+ }
+
+ lateinit var database: AppDatabase
+
+ val allTracks = ArrayList()
+
+ val allAlbums = ArrayList()
+
+ val allArtists = HashMap()
+
+ override fun onCreate() {
+ super.onCreate()
+ database = Room.databaseBuilder(
+ applicationContext, AppDatabase::class.java, "local"
+ ).allowMainThreadQueries().build()
+ database.artistDao().getAllArtists().forEach { allArtists[it.name ?: "unknown"] = ArtistDescriptor.fromArtist(it) }
+ database.albumDao().getAllAlbums().forEach { album ->
+ AlbumDescriptor.fromAlbum(album, database).let {
+ allAlbums.add(it)
+ allArtists[it.artistName]?.albumList?.add(it)
+ }
+ }
+ database.trackDao().getAll().forEach { track ->
+ NotaDescriptor.fromTrack(track, database).let {
+ allTracks.add(it)
+ allArtists[it.artist]?.trackList?.add(it)
+ }
+ }
+ _appInstance = this
+ }
+
+ fun addNewTrack(nota: NotaDescriptor) {
+ database.addNewTrack(nota)
+ allArtists[nota.artist]?.trackList?.add(nota)
+ allTracks.add(nota)
+ }
+}
\ 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 a43e8c3..198558a 100644
--- a/app/src/main/java/usr/empty/player/MainActivity.kt
+++ b/app/src/main/java/usr/empty/player/MainActivity.kt
@@ -12,36 +12,52 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.toMutableStateList
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.RectangleShape
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 androidx.room.Room
+import kotlinx.coroutines.delay
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
-import usr.empty.player.database.AppDatabase
-import usr.empty.player.database.Track
+import usr.empty.player.items.NotaDescriptor
import usr.empty.player.ui.theme.PlayerTheme
+import kotlin.math.max
+import kotlin.math.min
inline fun nullifyException(block: () -> T) = try {
@@ -55,12 +71,6 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- // Log.d("meow", "${filesDir.toPath().resolve("all_tracks.data")}")
- val db = Room.databaseBuilder(
- applicationContext, AppDatabase::class.java, "local"
- ).allowMainThreadQueries().build()
- Log.d("meow", "${db.trackDao().getAll()}")
-
if (!Environment.isExternalStorageManager()) {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
val uri = Uri.fromParts("package", packageName, null)
@@ -93,7 +103,12 @@ fun uriToPath(uri: Uri): String {
@Composable
fun MainLayout(modifier: Modifier = Modifier) {
val context = LocalContext.current
- val notas = remember { mutableStateListOf() }
+ val queueId = remember { mutableIntStateOf(-1) }
+ val notas = remember { EmptyApplication.appInstance.allTracks.toMutableStateList() }
+ val isPlaying =
+ remember { mutableStateOf(if (PlayerService.isServiceRunning) PlayerService.serviceInstance.player.isPlaying else false) }
+ val currentNota =
+ remember { mutableStateOf(if (PlayerService.isServiceRunning) PlayerService.serviceInstance.currentTrack?.descriptor else null) }
val pickAudioLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
@@ -101,26 +116,21 @@ fun MainLayout(modifier: Modifier = Modifier) {
audioUri?.run {
uriToPath(this).let {
MediaMetadataRetriever().apply {
- setDataSource(it) // notas.add(NotaDescriptor(name = nullifyException {
- // extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
- // } ?: it.split('/').last().split('.').first(), artist = nullifyException {
- // extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
- // } ?: "Unknown Artist", sourceType = NotaDescriptor.Source.LOCAL, source = it))
- val db = Room.databaseBuilder(
- context.applicationContext, AppDatabase::class.java, "local"
- ).allowMainThreadQueries().build()
- db.trackDao().insertTrack(Track(title = nullifyException {
+ setDataSource(it)
+ val nd = NotaDescriptor(name = nullifyException {
extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
- } ?: it.split('/').last().split('.').first(),
- artistId = 0,
- albumId = null,
- sourceType = NotaDescriptor.Source.LOCAL,
- source = it))
+ } ?: it.split('/').last().split('.').first(), artist = nullifyException {
+ extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
+ } ?: "Unknown Artist", sourceType = NotaDescriptor.Source.LOCAL, source = it)
+ notas.add(nd)
+ queueId.intValue--
+ EmptyApplication.appInstance.addNewTrack(nd)
}
}
}
}
+
Column(modifier.fillMaxSize()) {
Row(
horizontalArrangement = Arrangement.Absolute.SpaceEvenly,
@@ -133,7 +143,6 @@ fun MainLayout(modifier: Modifier = Modifier) {
}) {
Text("add track")
}
-// VerticalDivider()
Button(shape = RectangleShape, colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer, contentColor = MaterialTheme.colorScheme.secondary
), modifier = Modifier, onClick = {
@@ -155,19 +164,32 @@ fun MainLayout(modifier: Modifier = Modifier) {
Text("| play >")
}
}
- NotaList(notas)
+ NotaList(notas, modifier = Modifier.weight(1f), queueId = queueId.intValue, playState = isPlaying, currentNota)
+ if (isPlaying.value) Player(
+ modifier = Modifier
+ .padding(bottom = 20.dp)
+ .border(1.dp, color = MaterialTheme.colorScheme.primary, shape = RoundedCornerShape(25))
+ )
}
}
@UnstableApi
@Composable
-fun NotaList(notas: List, modifier: Modifier = Modifier, queueId: Int = notas.hashCode()) {
+fun NotaList(
+ notas: List,
+ modifier: Modifier = Modifier,
+ queueId: Int = notas.hashCode(),
+ playState: MutableState,
+ currentNota: MutableState
+) {
val context = LocalContext.current
LazyColumn(
- modifier = modifier,
+ modifier = modifier
) {
itemsIndexed(notas) { index, nota ->
- Column(modifier = Modifier
+ val localModifier =
+ if (currentNota.value == nota) Modifier.background(MaterialTheme.colorScheme.secondaryContainer) else Modifier
+ Column(modifier = localModifier
.fillParentMaxWidth()
.clickable {
Log.d("meow", "queueId: $queueId")
@@ -177,6 +199,7 @@ fun NotaList(notas: List, modifier: Modifier = Modifier, queueId
putExtra("queueId", queueId)
putExtra("queue", Json.encodeToString(notas))
putExtra("start", index)
+ playState.value = true
})
}
.padding(4.dp)
@@ -192,14 +215,86 @@ fun NotaList(notas: List, modifier: Modifier = Modifier, queueId
}
}
+fun minMax(vMin: Float, x: Float, vMax: Float) = max(min(vMax, x), vMin)
+
@UnstableApi
-@Preview
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun Preview() {
- NotaList(
- listOf(
- NotaDescriptor(name = "hello", artist = "there", sourceType = NotaDescriptor.Source.LOCAL, source = ""),
- NotaDescriptor(name = "preview", artist = "me", sourceType = NotaDescriptor.Source.LOCAL, source = ""),
- )
- )
+fun Player(modifier: Modifier = Modifier) {
+ val sliderPosition = remember { mutableFloatStateOf(0.0f) }
+ val sliderMovingAllow = remember { mutableStateOf(true) }
+ val service = PlayerService.serviceInstance
+ LaunchedEffect(Unit) {
+ while (true) {
+ if (service.player.isPlaying and sliderMovingAllow.value) {
+ sliderPosition.floatValue = minMax(0.0f, service.player.currentPosition.toFloat() / service.player.duration.toFloat(), 1.0f)
+ delay(service.player.duration / 2000)
+ Log.d("meow", "${service.player.duration / 2000}")
+ } else delay(10)
+ }
+ }
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(1.dp)
+ .then(modifier)
+ .padding(horizontal = 16.dp, vertical = 8.dp)
+ ) { // Image(
+ // imageVector = Icons.Filled.PlayArrow,
+ // contentDescription = "play",
+ // colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary),
+ // modifier = Modifier
+ // .size(50.dp)
+ // .align(Alignment.CenterVertically)
+ // )
+ Column(modifier = Modifier.align(Alignment.CenterVertically)) {
+ val nota = service.currentTrack?.descriptor ?: NotaDescriptor(
+ "unknown track", "unknown artist", NotaDescriptor.Source.LOCAL, ""
+ )
+ Text(nota.name, style = MaterialTheme.typography.titleLarge)
+ Text(nota.artist, style = MaterialTheme.typography.titleSmall)
+ Slider(value = sliderPosition.floatValue, thumb = {
+ Box(
+ Modifier
+ .clip(RoundedCornerShape(2.dp))
+ .width(3.dp)
+ .height(18.dp)
+ .background(MaterialTheme.colorScheme.tertiary)
+ )
+ }, track = {
+ Row(Modifier.clip(RoundedCornerShape(1.dp))) {
+ if (sliderPosition.floatValue != 0.0f) Box(
+ Modifier
+ .weight(sliderPosition.floatValue)
+ .height(2.dp)
+ .background(
+ Brush.linearGradient(
+ listOf(
+ MaterialTheme.colorScheme.secondary, MaterialTheme.colorScheme.tertiary
+ )
+ )
+ )
+ )
+ if (sliderPosition.floatValue != 1.0f) Box(
+ Modifier
+ .weight(1 - sliderPosition.floatValue)
+ .height(2.dp)
+ .background(
+ Brush.linearGradient(
+ listOf(
+ MaterialTheme.colorScheme.tertiaryContainer, MaterialTheme.colorScheme.secondaryContainer
+ )
+ )
+ )
+ )
+ }
+ }, onValueChange = {
+ sliderPosition.floatValue = it
+ sliderMovingAllow.value = false
+ }, onValueChangeFinished = {
+ service.player.seekTo((service.player.duration.toFloat() * sliderPosition.floatValue).toLong())
+ sliderMovingAllow.value = true
+ })
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/usr/empty/player/Nota.kt b/app/src/main/java/usr/empty/player/Nota.kt
index bc6e84e..0c8e1ba 100644
--- a/app/src/main/java/usr/empty/player/Nota.kt
+++ b/app/src/main/java/usr/empty/player/Nota.kt
@@ -2,6 +2,7 @@ package usr.empty.player
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
+import usr.empty.player.items.NotaDescriptor
class Nota(val descriptor: NotaDescriptor) {
diff --git a/app/src/main/java/usr/empty/player/NotaDescriptor.kt b/app/src/main/java/usr/empty/player/NotaDescriptor.kt
deleted file mode 100644
index 7f1ad11..0000000
--- a/app/src/main/java/usr/empty/player/NotaDescriptor.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package usr.empty.player
-
-import kotlinx.serialization.Serializable
-
-
-@Serializable
-data class NotaDescriptor(
- val name: String,
- val artist: String,
- val sourceType: Source,
- val source: String,
-) {
- enum class Source {
- LOCAL
- }
-}
\ 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 0acddd4..8d2790e 100644
--- a/app/src/main/java/usr/empty/player/PlayerService.kt
+++ b/app/src/main/java/usr/empty/player/PlayerService.kt
@@ -18,21 +18,44 @@ 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.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
+import usr.empty.player.items.NotaDescriptor
@UnstableApi
class PlayerService : MediaSessionService() {
+ companion object {
+ private var _serviceInstance: PlayerService? = null
+
+ val serviceInstance: PlayerService
+ get() = _serviceInstance!!
+
+ var isServiceRunning = false
+ }
+
private lateinit var mediaSession: MediaSession
lateinit var player: ExoPlayer
private lateinit var playerNotificationManager: PlayerNotificationManager
private val notificationId = 1004
private var virtualQueue = NotaQueue()
- private var queueId = -1
+ val currentTrack
+ get() = virtualQueue.current
+ private var queueId = 0
+ var control = Channel()
+ var data = Channel>()
+ @OptIn(ExperimentalCoroutinesApi::class)
@SuppressLint("ForegroundServiceType")
override fun onCreate() {
super.onCreate()
+ _serviceInstance = this
+ isServiceRunning = true
player = ExoPlayer.Builder(this).build()
player.addListener(object : Player.Listener {
override fun onIsPlayingChanged(isPlaying: Boolean) {
@@ -49,13 +72,13 @@ class PlayerService : MediaSessionService() {
override fun createCurrentContentIntent(player: Player) = null
- override fun getCurrentContentText(player: Player) = "context?"
+ override fun getCurrentContentText(player: Player) = "content?"
override fun getCurrentLargeIcon(player: Player, callback: PlayerNotificationManager.BitmapCallback) = null
})
.build()
.apply {
- setPriority(NotificationCompat.PRIORITY_DEFAULT)
+ setPriority(NotificationCompat.PRIORITY_MAX)
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
setPlayer(player)
setMediaSessionToken(mediaSession.platformToken)
@@ -66,6 +89,16 @@ class PlayerService : MediaSessionService() {
val nBuilder = NotificationCompat.Builder(this, "emptyynes").setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession))
startForeground(notificationId, nBuilder.build(), FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK)
+
+ CoroutineScope(SupervisorJob()).launch { // data.
+ while (true) {
+ while (control.isEmpty) {
+ data.send(player.isPlaying to (player.currentPosition.toFloat() / player.duration.toFloat()))
+ delay(if (player.isPlaying) player.duration / 2000 else 33)
+ }
+ if (!control.receive()) break
+ }
+ }
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
diff --git a/app/src/main/java/usr/empty/player/database/Album.kt b/app/src/main/java/usr/empty/player/database/Album.kt
new file mode 100644
index 0000000..51c97dd
--- /dev/null
+++ b/app/src/main/java/usr/empty/player/database/Album.kt
@@ -0,0 +1,30 @@
+package usr.empty.player.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Dao
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy.Companion.IGNORE
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import java.util.UUID
+
+@Entity
+data class Album(
+ @ColumnInfo val name: String, @ColumnInfo val artistId: UUID
+) {
+ @PrimaryKey
+ var uuid: UUID = UUID.nameUUIDFromBytes("$name$artistId".toByteArray())
+}
+
+@Dao
+interface AlbumDao {
+ @Insert(onConflict = IGNORE)
+ fun insertAlbum(album: Album)
+
+ @Query("SELECT * FROM album")
+ fun getAllAlbums(): List
+
+ @Query("SELECT * FROM album WHERE :uuid = uuid")
+ fun getAlbumByUUID(uuid: UUID): Album?
+}
\ No newline at end of file
diff --git a/app/src/main/java/usr/empty/player/database/AppDatabase.kt b/app/src/main/java/usr/empty/player/database/AppDatabase.kt
index 7abf5c6..cf97a82 100644
--- a/app/src/main/java/usr/empty/player/database/AppDatabase.kt
+++ b/app/src/main/java/usr/empty/player/database/AppDatabase.kt
@@ -2,10 +2,20 @@ package usr.empty.player.database
import androidx.room.Database
import androidx.room.RoomDatabase
+import usr.empty.player.items.NotaDescriptor
@Database(
- entities = [Track::class], version = 1
+ entities = [Track::class, Artist::class, Album::class], version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun trackDao(): TrackDao
+ abstract fun artistDao(): ArtistDao
+ abstract fun albumDao(): AlbumDao
+
+ fun addNewTrack(nota: NotaDescriptor) {
+ Artist(Artist.ArtistType.UNKNOWN, nota.artist).let { artist ->
+ artistDao().insertArtist(artist)
+ trackDao().insertTrack(Track(nota.name, artist.uuid, null, nota.sourceType, nota.source))
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/usr/empty/player/database/Artist.kt b/app/src/main/java/usr/empty/player/database/Artist.kt
new file mode 100644
index 0000000..dc531cd
--- /dev/null
+++ b/app/src/main/java/usr/empty/player/database/Artist.kt
@@ -0,0 +1,35 @@
+package usr.empty.player.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Dao
+import androidx.room.Entity
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy.Companion.IGNORE
+import androidx.room.PrimaryKey
+import androidx.room.Query
+import java.util.UUID
+
+
+@Entity
+data class Artist(
+ @ColumnInfo val type: ArtistType, @ColumnInfo val name: String?
+) {
+ enum class ArtistType {
+ UNKNOWN, SINGLE, DUET, FEAT, BAND, CIRCLE
+ }
+
+ @PrimaryKey
+ var uuid: UUID = if (name == null) UUID.randomUUID() else UUID.nameUUIDFromBytes(name.toByteArray())
+}
+
+@Dao
+interface ArtistDao {
+ @Insert(onConflict = IGNORE)
+ fun insertArtist(artist: Artist)
+
+ @Query("SELECT * FROM artist")
+ fun getAllArtists(): List
+
+ @Query("SELECT * FROM artist WHERE :uuid = uuid")
+ fun getArtistByUUID(uuid: UUID): Artist?
+}
\ No newline at end of file
diff --git a/app/src/main/java/usr/empty/player/database/Track.kt b/app/src/main/java/usr/empty/player/database/Track.kt
index 1d064be..e6d1e90 100644
--- a/app/src/main/java/usr/empty/player/database/Track.kt
+++ b/app/src/main/java/usr/empty/player/database/Track.kt
@@ -4,38 +4,39 @@ import androidx.room.ColumnInfo
import androidx.room.Dao
import androidx.room.Entity
import androidx.room.Insert
+import androidx.room.OnConflictStrategy.Companion.REPLACE
import androidx.room.PrimaryKey
import androidx.room.Query
-import usr.empty.player.NotaDescriptor
+import usr.empty.player.items.NotaDescriptor
import java.util.UUID
// a.k.a. Nota
@Entity
data class Track(
@ColumnInfo val title: String,
- @ColumnInfo val artistId: Int,
- @ColumnInfo val albumId: Int?,
+ @ColumnInfo val artistId: UUID,
+ @ColumnInfo val albumId: UUID?,
@ColumnInfo val sourceType: NotaDescriptor.Source,
@ColumnInfo val source: String,
) {
- @PrimaryKey(autoGenerate = true)
- var uid: Int = 0
-
- @ColumnInfo
+ @PrimaryKey
var uuid: UUID = UUID.nameUUIDFromBytes("${title}${artistId}".toByteArray())
}
@Dao
interface TrackDao {
- @Insert
+ @Insert(onConflict = REPLACE)
fun insertTrack(track: Track)
@Query("SELECT * FROM track")
fun getAll(): List