diff --git a/app/schemas/it.vfsfitvnm.vimusic.DatabaseInitializer/4.json b/app/schemas/it.vfsfitvnm.vimusic.DatabaseInitializer/4.json new file mode 100644 index 0000000..d422cb0 --- /dev/null +++ b/app/schemas/it.vfsfitvnm.vimusic.DatabaseInitializer/4.json @@ -0,0 +1,310 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "d5720e465abdf99b583c183298f18340", + "entities": [ + { + "tableName": "Song", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `albumInfoId` INTEGER, `durationText` TEXT NOT NULL, `thumbnailUrl` TEXT, `lyrics` TEXT, `likedAt` INTEGER, `totalPlayTimeMs` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "albumInfoId", + "columnName": "albumInfoId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "durationText", + "columnName": "durationText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lyrics", + "columnName": "lyrics", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "likedAt", + "columnName": "likedAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "totalPlayTimeMs", + "columnName": "totalPlayTimeMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SongInPlaylist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`songId` TEXT NOT NULL, `playlistId` INTEGER NOT NULL, `position` INTEGER NOT NULL, PRIMARY KEY(`songId`, `playlistId`), FOREIGN KEY(`songId`) REFERENCES `Song`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`playlistId`) REFERENCES `Playlist`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "songId", + "columnName": "songId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "playlistId", + "columnName": "playlistId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "songId", + "playlistId" + ] + }, + "indices": [ + { + "name": "index_SongInPlaylist_songId", + "unique": false, + "columnNames": [ + "songId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SongInPlaylist_songId` ON `${TABLE_NAME}` (`songId`)" + }, + { + "name": "index_SongInPlaylist_playlistId", + "unique": false, + "columnNames": [ + "playlistId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SongInPlaylist_playlistId` ON `${TABLE_NAME}` (`playlistId`)" + } + ], + "foreignKeys": [ + { + "table": "Song", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "songId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Playlist", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "playlistId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "Playlist", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `browseId` TEXT, `text` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "browseId", + "columnName": "browseId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "SongWithAuthors", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`songId` TEXT NOT NULL, `authorInfoId` INTEGER NOT NULL, PRIMARY KEY(`songId`, `authorInfoId`), FOREIGN KEY(`songId`) REFERENCES `Song`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`authorInfoId`) REFERENCES `Info`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "songId", + "columnName": "songId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "authorInfoId", + "columnName": "authorInfoId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "songId", + "authorInfoId" + ] + }, + "indices": [ + { + "name": "index_SongWithAuthors_authorInfoId", + "unique": false, + "columnNames": [ + "authorInfoId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SongWithAuthors_authorInfoId` ON `${TABLE_NAME}` (`authorInfoId`)" + } + ], + "foreignKeys": [ + { + "table": "Song", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "songId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "Info", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "authorInfoId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "SearchQuery", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `query` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "query", + "columnName": "query", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SearchQuery_query", + "unique": true, + "columnNames": [ + "query" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SearchQuery_query` ON `${TABLE_NAME}` (`query`)" + } + ], + "foreignKeys": [] + } + ], + "views": [ + { + "viewName": "SortedSongInPlaylist", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS SELECT * FROM SongInPlaylist ORDER BY position" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd5720e465abdf99b583c183298f18340')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/Database.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/Database.kt index 9e0c445..4faf91c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/Database.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/Database.kt @@ -2,9 +2,8 @@ package it.vfsfitvnm.vimusic import android.content.Context import android.database.Cursor -import android.os.Parcel -import androidx.media3.common.MediaItem import androidx.room.* +import androidx.room.migration.AutoMigrationSpec import it.vfsfitvnm.vimusic.models.* import kotlinx.coroutines.flow.Flow @@ -84,15 +83,6 @@ interface Database { @Insert(onConflict = OnConflictStrategy.ABORT) fun insert(song: Song): Long - @Insert(onConflict = OnConflictStrategy.ABORT) - fun insertQueuedMediaItems(queuedMediaItems: List) - - @Query("SELECT * FROM QueuedMediaItem") - fun queuedMediaItems(): List - - @Query("DELETE FROM QueuedMediaItem") - fun clearQueuedMediaItems() - @Update fun update(song: Song) @@ -130,19 +120,18 @@ interface Database { Info::class, SongWithAuthors::class, SearchQuery::class, - QueuedMediaItem::class ], views = [ SortedSongInPlaylist::class ], - version = 3, + version = 4, exportSchema = true, autoMigrations = [ AutoMigration(from = 1, to = 2), AutoMigration(from = 2, to = 3), - ] + AutoMigration(from = 3, to = 4, spec = DatabaseInitializer.From3To4Migration::class), + ], ) -@TypeConverters(Converters::class) abstract class DatabaseInitializer protected constructor() : RoomDatabase() { abstract val database: Database @@ -158,37 +147,9 @@ abstract class DatabaseInitializer protected constructor() : RoomDatabase() { } } } -} -@TypeConverters -object Converters { - // TODO: temporary - @TypeConverter - fun mediaItemFromByteArray(value: ByteArray?): MediaItem? { - return value?.let { byteArray -> - val parcel = Parcel.obtain() - parcel.unmarshall(byteArray, 0, byteArray.size) - parcel.setDataPosition(0) - - val pb = parcel.readBundle(MediaItem::class.java.classLoader) - parcel.recycle() - pb?.let { - MediaItem.CREATOR.fromBundle(pb) - } - } - } - - // TODO: temporary - @TypeConverter - fun mediaItemToByteArray(mediaItem: MediaItem?): ByteArray? { - return mediaItem?.toBundle()?.let { persistableBundle -> - val parcel = Parcel.obtain() - parcel.writeBundle(persistableBundle) - parcel.marshall().also { - parcel.recycle() - } - } - } + @DeleteTable.Entries(DeleteTable(tableName = "QueuedMediaItem")) + class From3To4Migration : AutoMigrationSpec } val Database.internal: RoomDatabase diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/models/QueuedMediaItem.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/models/QueuedMediaItem.kt deleted file mode 100644 index bfb95df..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/models/QueuedMediaItem.kt +++ /dev/null @@ -1,13 +0,0 @@ -package it.vfsfitvnm.vimusic.models - -import androidx.media3.common.MediaItem -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity -class QueuedMediaItem( - @PrimaryKey(autoGenerate = true) val id: Long = 0, - @ColumnInfo(typeAffinity = ColumnInfo.BLOB) val mediaItem: MediaItem, - var position: Long? -) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt index 7aab940..cc9d86a 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/services/PlayerService.kt @@ -41,7 +41,6 @@ import com.google.common.util.concurrent.Futures import com.google.common.util.concurrent.ListenableFuture import it.vfsfitvnm.vimusic.* import it.vfsfitvnm.vimusic.R -import it.vfsfitvnm.vimusic.models.QueuedMediaItem import it.vfsfitvnm.vimusic.utils.* import it.vfsfitvnm.youtubemusic.Outcome import kotlinx.coroutines.* @@ -123,55 +122,9 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller, .build() player.addListener(this) - - if (preferences.persistentQueue) { - coroutineScope.launch(Dispatchers.IO) { - val queuedMediaItems = Database.queuedMediaItems() - Database.clearQueuedMediaItems() - - if (queuedMediaItems.isEmpty()) return@launch - - val index = queuedMediaItems.indexOfFirst { it.position != null }.coerceAtLeast(0) - - withContext(Dispatchers.Main) { - player.setMediaItems( - queuedMediaItems - .map(QueuedMediaItem::mediaItem) - .map { mediaItem -> - mediaItem.buildUpon() - .setUri(mediaItem.mediaId) - .setCustomCacheKey(mediaItem.mediaId) - .build() - }, - true - ) - player.seekTo(index, queuedMediaItems[index].position ?: 0) - player.playWhenReady = false - player.prepare() - } - } - } } override fun onDestroy() { - if (preferences.persistentQueue) { - val mediaItems = player.currentTimeline.mediaItems - val mediaItemIndex = player.currentMediaItemIndex - val mediaItemPosition = player.currentPosition - - Database.internal.queryExecutor.execute { - Database.clearQueuedMediaItems() - Database.insertQueuedMediaItems( - mediaItems.mapIndexed { index, mediaItem -> - QueuedMediaItem( - mediaItem = mediaItem, - position = if (index == mediaItemIndex) mediaItemPosition else null - ) - } - ) - } - } - player.release() mediaSession.release() cache.release() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationSettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationSettingsScreen.kt index de8ef26..0bb2029 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationSettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/NotificationSettingsScreen.kt @@ -78,8 +78,8 @@ fun NotificationSettingsScreen() { } SwitchSettingEntry( - title = "[SOON] Show Like button", - text = "Mark a song as favorite", + title = "Show Like button", + text = "Coming soon!", isChecked = preferences.displayLikeButtonInNotification, onCheckedChange = { preferences.displayLikeButtonInNotification = it