Twelve: database: Add Subsonic providers table
Change-Id: I8451e276b5f7ae8335026d6f3e201369e5c52223
diff --git a/app/schemas/org.lineageos.twelve.database.TwelveDatabase/3.json b/app/schemas/org.lineageos.twelve.database.TwelveDatabase/3.json
new file mode 100644
index 0000000..097715f
--- /dev/null
+++ b/app/schemas/org.lineageos.twelve.database.TwelveDatabase/3.json
@@ -0,0 +1,307 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 3,
+ "identityHash": "f770cb80ab0712cb4fc77db84d40e9a5",
+ "entities": [
+ {
+ "tableName": "Playlist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `last_modified` INTEGER NOT NULL, `track_count` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastModified",
+ "columnName": "last_modified",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "trackCount",
+ "columnName": "track_count",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "playlist_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "Item",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`item_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `audio_uri` TEXT NOT NULL, `count` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "item_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "audioUri",
+ "columnName": "audio_uri",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "count",
+ "columnName": "count",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "item_id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_Item_audio_uri",
+ "unique": true,
+ "columnNames": [
+ "audio_uri"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_Item_audio_uri` ON `${TABLE_NAME}` (`audio_uri`)"
+ }
+ ],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "PlaylistItemCrossRef",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_id` INTEGER NOT NULL, `item_id` INTEGER NOT NULL, `last_modified` INTEGER NOT NULL, PRIMARY KEY(`playlist_id`, `item_id`), FOREIGN KEY(`playlist_id`) REFERENCES `Playlist`(`playlist_id`) ON UPDATE CASCADE ON DELETE CASCADE , FOREIGN KEY(`item_id`) REFERENCES `Item`(`item_id`) ON UPDATE CASCADE ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "playlistId",
+ "columnName": "playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "itemId",
+ "columnName": "item_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastModified",
+ "columnName": "last_modified",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "playlist_id",
+ "item_id"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_PlaylistItemCrossRef_item_id",
+ "unique": false,
+ "columnNames": [
+ "item_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_PlaylistItemCrossRef_item_id` ON `${TABLE_NAME}` (`item_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "Playlist",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "playlist_id"
+ ],
+ "referencedColumns": [
+ "playlist_id"
+ ]
+ },
+ {
+ "table": "Item",
+ "onDelete": "CASCADE",
+ "onUpdate": "CASCADE",
+ "columns": [
+ "item_id"
+ ],
+ "referencedColumns": [
+ "item_id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "ResumptionItem",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`playlist_index` INTEGER NOT NULL, `resumption_playlist_id` INTEGER NOT NULL, `media_id` TEXT NOT NULL, PRIMARY KEY(`playlist_index`), FOREIGN KEY(`resumption_playlist_id`) REFERENCES `ResumptionPlaylist`(`resumption_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
+ "fields": [
+ {
+ "fieldPath": "playlistIndex",
+ "columnName": "playlist_index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "resumptionPlaylistId",
+ "columnName": "resumption_playlist_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "mediaId",
+ "columnName": "media_id",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "playlist_index"
+ ]
+ },
+ "indices": [
+ {
+ "name": "index_ResumptionItem_playlist_index",
+ "unique": true,
+ "columnNames": [
+ "playlist_index"
+ ],
+ "orders": [],
+ "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ResumptionItem_playlist_index` ON `${TABLE_NAME}` (`playlist_index`)"
+ },
+ {
+ "name": "index_ResumptionItem_resumption_playlist_id",
+ "unique": false,
+ "columnNames": [
+ "resumption_playlist_id"
+ ],
+ "orders": [],
+ "createSql": "CREATE INDEX IF NOT EXISTS `index_ResumptionItem_resumption_playlist_id` ON `${TABLE_NAME}` (`resumption_playlist_id`)"
+ }
+ ],
+ "foreignKeys": [
+ {
+ "table": "ResumptionPlaylist",
+ "onDelete": "CASCADE",
+ "onUpdate": "NO ACTION",
+ "columns": [
+ "resumption_playlist_id"
+ ],
+ "referencedColumns": [
+ "resumption_id"
+ ]
+ }
+ ]
+ },
+ {
+ "tableName": "ResumptionPlaylist",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`resumption_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `start_index` INTEGER NOT NULL, `start_position_ms` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "resumption_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startIndex",
+ "columnName": "start_index",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "startPositionMs",
+ "columnName": "start_position_ms",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "resumption_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "SubsonicProvider",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`subsonic_provider_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, `use_legacy_authentication` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "subsonic_provider_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "url",
+ "columnName": "url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "useLegacyAuthentication",
+ "columnName": "use_legacy_authentication",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "subsonic_provider_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "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, 'f770cb80ab0712cb4fc77db84d40e9a5')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt b/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt
index e125ed5..f7a5202 100644
--- a/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt
+++ b/app/src/main/java/org/lineageos/twelve/database/TwelveDatabase.kt
@@ -17,11 +17,13 @@
import org.lineageos.twelve.database.dao.PlaylistItemCrossRefDao
import org.lineageos.twelve.database.dao.PlaylistWithItemsDao
import org.lineageos.twelve.database.dao.ResumptionPlaylistDao
+import org.lineageos.twelve.database.dao.SubsonicProviderDao
import org.lineageos.twelve.database.entities.Item
import org.lineageos.twelve.database.entities.Playlist
import org.lineageos.twelve.database.entities.PlaylistItemCrossRef
import org.lineageos.twelve.database.entities.ResumptionItem
import org.lineageos.twelve.database.entities.ResumptionPlaylist
+import org.lineageos.twelve.database.entities.SubsonicProvider
@Database(
entities = [
@@ -33,10 +35,14 @@
/* Resumption */
ResumptionItem::class,
ResumptionPlaylist::class,
+
+ /* Providers */
+ SubsonicProvider::class,
],
- version = 2,
+ version = 3,
autoMigrations = [
AutoMigration(from = 1, to = 2),
+ AutoMigration(from = 2, to = 3),
],
)
@TypeConverters(UriConverter::class)
@@ -46,6 +52,7 @@
abstract fun getPlaylistItemCrossRefDao(): PlaylistItemCrossRefDao
abstract fun getPlaylistWithItemsDao(): PlaylistWithItemsDao
abstract fun getResumptionPlaylistDao(): ResumptionPlaylistDao
+ abstract fun getSubsonicProviderDao(): SubsonicProviderDao
companion object {
@Volatile
diff --git a/app/src/main/java/org/lineageos/twelve/database/dao/SubsonicProviderDao.kt b/app/src/main/java/org/lineageos/twelve/database/dao/SubsonicProviderDao.kt
new file mode 100644
index 0000000..273f56e
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/database/dao/SubsonicProviderDao.kt
@@ -0,0 +1,72 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.database.dao
+
+import androidx.room.Dao
+import androidx.room.Query
+import kotlinx.coroutines.flow.Flow
+import org.lineageos.twelve.database.entities.SubsonicProvider
+
+@Dao
+interface SubsonicProviderDao {
+ /**
+ * Add a new subsonic provider to the database.
+ */
+ @Query(
+ """
+ INSERT INTO SubsonicProvider (name, url, username, password, use_legacy_authentication)
+ VALUES (:name, :url, :username, :password, :useLegacyAuthentication)
+ """
+ )
+ suspend fun create(
+ name: String,
+ url: String,
+ username: String,
+ password: String,
+ useLegacyAuthentication: Boolean,
+ ): Long
+
+ /**
+ * Update a subsonic provider.
+ */
+ @Query(
+ """
+ UPDATE SubsonicProvider
+ SET name = :name,
+ url = :url,
+ username = :username,
+ password = :password,
+ use_legacy_authentication = :useLegacyAuthentication
+ WHERE subsonic_provider_id = :subsonicProviderId
+ """
+ )
+ suspend fun update(
+ subsonicProviderId: Long,
+ name: String,
+ url: String,
+ username: String,
+ password: String,
+ useLegacyAuthentication: Boolean,
+ )
+
+ /**
+ * Delete a subsonic provider from the database.
+ */
+ @Query("DELETE FROM SubsonicProvider WHERE subsonic_provider_id = :subsonicProviderId")
+ suspend fun delete(subsonicProviderId: Long)
+
+ /**
+ * Fetch all subsonic providers from the database.
+ */
+ @Query("SELECT * FROM SubsonicProvider")
+ fun getAll(): Flow<List<SubsonicProvider>>
+
+ /**
+ * Fetch a subsonic provider by its ID from the database.
+ */
+ @Query("SELECT * FROM SubsonicProvider WHERE subsonic_provider_id = :subsonicProviderId")
+ fun getById(subsonicProviderId: Long): Flow<SubsonicProvider?>
+}
diff --git a/app/src/main/java/org/lineageos/twelve/database/entities/SubsonicProvider.kt b/app/src/main/java/org/lineageos/twelve/database/entities/SubsonicProvider.kt
new file mode 100644
index 0000000..c6ae45d
--- /dev/null
+++ b/app/src/main/java/org/lineageos/twelve/database/entities/SubsonicProvider.kt
@@ -0,0 +1,23 @@
+/*
+ * SPDX-FileCopyrightText: 2024 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.lineageos.twelve.database.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * Subsonic provider entity.
+ */
+@Entity
+data class SubsonicProvider(
+ @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "subsonic_provider_id") val id: Long,
+ @ColumnInfo(name = "name") val name: String,
+ @ColumnInfo(name = "url") val url: String,
+ @ColumnInfo(name = "username") val username: String,
+ @ColumnInfo(name = "password") val password: String,
+ @ColumnInfo(name = "use_legacy_authentication") val useLegacyAuthentication: Boolean,
+)