feat: add Room entities, DAOs, database, and DataStore preferences
Made-with: Cursor
This commit is contained in:
@@ -5,6 +5,10 @@ plugins {
|
||||
alias(libs.plugins.ksp)
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "xyz.cottongin.radio247"
|
||||
compileSdk = 35
|
||||
|
||||
321
app/schemas/xyz.cottongin.radio247.data.db.RadioDatabase/1.json
Normal file
321
app/schemas/xyz.cottongin.radio247.data.db.RadioDatabase/1.json
Normal file
@@ -0,0 +1,321 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "2959b46abce28a2c49ca387873ac9c0d",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "stations",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `playlistId` INTEGER, `sortOrder` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `defaultArtworkUrl` TEXT, FOREIGN KEY(`playlistId`) REFERENCES `playlists`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "url",
|
||||
"columnName": "url",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "playlistId",
|
||||
"columnName": "playlistId",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sortOrder",
|
||||
"columnName": "sortOrder",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "defaultArtworkUrl",
|
||||
"columnName": "defaultArtworkUrl",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_stations_playlistId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"playlistId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_stations_playlistId` ON `${TABLE_NAME}` (`playlistId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "playlists",
|
||||
"onDelete": "SET NULL",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"playlistId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "playlists",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `sortOrder` INTEGER NOT NULL, `starred` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "name",
|
||||
"columnName": "name",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sortOrder",
|
||||
"columnName": "sortOrder",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "metadata_snapshots",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stationId` INTEGER NOT NULL, `title` TEXT, `artist` TEXT, `artworkUrl` TEXT, `timestamp` INTEGER NOT NULL, FOREIGN KEY(`stationId`) REFERENCES `stations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "stationId",
|
||||
"columnName": "stationId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "artist",
|
||||
"columnName": "artist",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "artworkUrl",
|
||||
"columnName": "artworkUrl",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "timestamp",
|
||||
"columnName": "timestamp",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_metadata_snapshots_stationId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"stationId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_metadata_snapshots_stationId` ON `${TABLE_NAME}` (`stationId`)"
|
||||
},
|
||||
{
|
||||
"name": "index_metadata_snapshots_timestamp",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"timestamp"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_metadata_snapshots_timestamp` ON `${TABLE_NAME}` (`timestamp`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "stations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"stationId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "listening_sessions",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `stationId` INTEGER NOT NULL, `startedAt` INTEGER NOT NULL, `endedAt` INTEGER, FOREIGN KEY(`stationId`) REFERENCES `stations`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "stationId",
|
||||
"columnName": "stationId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "startedAt",
|
||||
"columnName": "startedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "endedAt",
|
||||
"columnName": "endedAt",
|
||||
"affinity": "INTEGER"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_listening_sessions_stationId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"stationId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_listening_sessions_stationId` ON `${TABLE_NAME}` (`stationId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "stations",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"stationId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "connection_spans",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sessionId` INTEGER NOT NULL, `startedAt` INTEGER NOT NULL, `endedAt` INTEGER, FOREIGN KEY(`sessionId`) REFERENCES `listening_sessions`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sessionId",
|
||||
"columnName": "sessionId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "startedAt",
|
||||
"columnName": "startedAt",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "endedAt",
|
||||
"columnName": "endedAt",
|
||||
"affinity": "INTEGER"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_connection_spans_sessionId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"sessionId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_connection_spans_sessionId` ON `${TABLE_NAME}` (`sessionId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "listening_sessions",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"sessionId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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, '2959b46abce28a2c49ca387873ac9c0d')"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,17 @@
|
||||
package xyz.cottongin.radio247
|
||||
|
||||
import android.app.Application
|
||||
import androidx.room.Room
|
||||
import xyz.cottongin.radio247.data.db.RadioDatabase
|
||||
import xyz.cottongin.radio247.data.prefs.RadioPreferences
|
||||
|
||||
class RadioApplication : Application()
|
||||
class RadioApplication : Application() {
|
||||
val database: RadioDatabase by lazy {
|
||||
Room.databaseBuilder(this, RadioDatabase::class.java, "radio_database")
|
||||
.build()
|
||||
}
|
||||
|
||||
val preferences: RadioPreferences by lazy {
|
||||
RadioPreferences(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package xyz.cottongin.radio247.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import xyz.cottongin.radio247.data.model.ConnectionSpan
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ConnectionSpanDao {
|
||||
@Insert
|
||||
suspend fun insert(span: ConnectionSpan): Long
|
||||
|
||||
@Query("UPDATE connection_spans SET endedAt = :endedAt WHERE id = :id")
|
||||
suspend fun updateEndedAt(id: Long, endedAt: Long)
|
||||
|
||||
@Query("SELECT * FROM connection_spans WHERE sessionId = :sessionId ORDER BY startedAt DESC")
|
||||
fun getBySession(sessionId: Long): Flow<List<ConnectionSpan>>
|
||||
|
||||
@Query("SELECT * FROM connection_spans WHERE endedAt IS NULL LIMIT 1")
|
||||
suspend fun getActiveSpan(): ConnectionSpan?
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package xyz.cottongin.radio247.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import xyz.cottongin.radio247.data.model.ListeningSession
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ListeningSessionDao {
|
||||
@Insert
|
||||
suspend fun insert(session: ListeningSession): Long
|
||||
|
||||
@Query("UPDATE listening_sessions SET endedAt = :endedAt WHERE id = :id")
|
||||
suspend fun updateEndedAt(id: Long, endedAt: Long)
|
||||
|
||||
@Query("SELECT * FROM listening_sessions WHERE endedAt IS NULL LIMIT 1")
|
||||
suspend fun getActiveSession(): ListeningSession?
|
||||
|
||||
@Query("SELECT * FROM listening_sessions ORDER BY startedAt DESC LIMIT :limit")
|
||||
fun getRecentSessions(limit: Int = 50): Flow<List<ListeningSession>>
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package xyz.cottongin.radio247.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import xyz.cottongin.radio247.data.model.MetadataSnapshot
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface MetadataSnapshotDao {
|
||||
@Insert
|
||||
suspend fun insert(snapshot: MetadataSnapshot): Long
|
||||
|
||||
@Query("SELECT * FROM metadata_snapshots WHERE stationId = :stationId ORDER BY timestamp DESC LIMIT :limit")
|
||||
fun getByStation(stationId: Long, limit: Int = 50): Flow<List<MetadataSnapshot>>
|
||||
|
||||
@Query("SELECT * FROM metadata_snapshots ORDER BY timestamp DESC LIMIT :limit")
|
||||
fun getRecent(limit: Int = 100): Flow<List<MetadataSnapshot>>
|
||||
|
||||
@Query("SELECT * FROM metadata_snapshots WHERE artist LIKE '%' || :query || '%' OR title LIKE '%' || :query || '%' ORDER BY timestamp DESC")
|
||||
fun search(query: String): Flow<List<MetadataSnapshot>>
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package xyz.cottongin.radio247.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import xyz.cottongin.radio247.data.model.Playlist
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface PlaylistDao {
|
||||
@Query("SELECT * FROM playlists ORDER BY starred DESC, sortOrder ASC")
|
||||
fun getAllPlaylists(): Flow<List<Playlist>>
|
||||
|
||||
@Query("SELECT * FROM playlists WHERE id = :id")
|
||||
suspend fun getPlaylistById(id: Long): Playlist?
|
||||
|
||||
@Insert
|
||||
suspend fun insert(playlist: Playlist): Long
|
||||
|
||||
@Update
|
||||
suspend fun update(playlist: Playlist)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(playlist: Playlist)
|
||||
|
||||
@Query("UPDATE playlists SET sortOrder = :sortOrder WHERE id = :id")
|
||||
suspend fun updateSortOrder(id: Long, sortOrder: Int)
|
||||
|
||||
@Query("UPDATE playlists SET starred = :starred WHERE id = :id")
|
||||
suspend fun toggleStarred(id: Long, starred: Boolean)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package xyz.cottongin.radio247.data.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import xyz.cottongin.radio247.data.model.ConnectionSpan
|
||||
import xyz.cottongin.radio247.data.model.ListeningSession
|
||||
import xyz.cottongin.radio247.data.model.MetadataSnapshot
|
||||
import xyz.cottongin.radio247.data.model.Playlist
|
||||
import xyz.cottongin.radio247.data.model.Station
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
Station::class,
|
||||
Playlist::class,
|
||||
MetadataSnapshot::class,
|
||||
ListeningSession::class,
|
||||
ConnectionSpan::class
|
||||
],
|
||||
version = 1,
|
||||
exportSchema = true
|
||||
)
|
||||
abstract class RadioDatabase : RoomDatabase() {
|
||||
abstract fun stationDao(): StationDao
|
||||
abstract fun playlistDao(): PlaylistDao
|
||||
abstract fun metadataSnapshotDao(): MetadataSnapshotDao
|
||||
abstract fun listeningSessionDao(): ListeningSessionDao
|
||||
abstract fun connectionSpanDao(): ConnectionSpanDao
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package xyz.cottongin.radio247.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import xyz.cottongin.radio247.data.model.Station
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface StationDao {
|
||||
@Query("SELECT * FROM stations ORDER BY starred DESC, sortOrder ASC")
|
||||
fun getAllStations(): Flow<List<Station>>
|
||||
|
||||
@Query("SELECT * FROM stations WHERE playlistId = :playlistId ORDER BY starred DESC, sortOrder ASC")
|
||||
fun getStationsByPlaylist(playlistId: Long): Flow<List<Station>>
|
||||
|
||||
@Query("SELECT * FROM stations WHERE playlistId IS NULL ORDER BY starred DESC, sortOrder ASC")
|
||||
fun getUnsortedStations(): Flow<List<Station>>
|
||||
|
||||
@Query("SELECT * FROM stations WHERE id = :id")
|
||||
suspend fun getStationById(id: Long): Station?
|
||||
|
||||
@Insert
|
||||
suspend fun insert(station: Station): Long
|
||||
|
||||
@Update
|
||||
suspend fun update(station: Station)
|
||||
|
||||
@Delete
|
||||
suspend fun delete(station: Station)
|
||||
|
||||
@Query("UPDATE stations SET sortOrder = :sortOrder WHERE id = :id")
|
||||
suspend fun updateSortOrder(id: Long, sortOrder: Int)
|
||||
|
||||
@Query("UPDATE stations SET starred = :starred WHERE id = :id")
|
||||
suspend fun toggleStarred(id: Long, starred: Boolean)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package xyz.cottongin.radio247.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "connection_spans",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = ListeningSession::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["sessionId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)],
|
||||
indices = [Index("sessionId")]
|
||||
)
|
||||
data class ConnectionSpan(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
val sessionId: Long,
|
||||
val startedAt: Long,
|
||||
val endedAt: Long? = null
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
package xyz.cottongin.radio247.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "listening_sessions",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = Station::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["stationId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)],
|
||||
indices = [Index("stationId")]
|
||||
)
|
||||
data class ListeningSession(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
val stationId: Long,
|
||||
val startedAt: Long,
|
||||
val endedAt: Long? = null
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package xyz.cottongin.radio247.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "metadata_snapshots",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = Station::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["stationId"],
|
||||
onDelete = ForeignKey.CASCADE
|
||||
)],
|
||||
indices = [Index("stationId"), Index("timestamp")]
|
||||
)
|
||||
data class MetadataSnapshot(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
val stationId: Long,
|
||||
val title: String? = null,
|
||||
val artist: String? = null,
|
||||
val artworkUrl: String? = null,
|
||||
val timestamp: Long
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package xyz.cottongin.radio247.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "playlists")
|
||||
data class Playlist(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
val name: String,
|
||||
val sortOrder: Int = 0,
|
||||
val starred: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
package xyz.cottongin.radio247.data.model
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "stations",
|
||||
foreignKeys = [ForeignKey(
|
||||
entity = Playlist::class,
|
||||
parentColumns = ["id"],
|
||||
childColumns = ["playlistId"],
|
||||
onDelete = ForeignKey.SET_NULL
|
||||
)],
|
||||
indices = [Index("playlistId")]
|
||||
)
|
||||
data class Station(
|
||||
@PrimaryKey(autoGenerate = true) val id: Long = 0,
|
||||
val name: String,
|
||||
val url: String,
|
||||
val playlistId: Long? = null,
|
||||
val sortOrder: Int = 0,
|
||||
val starred: Boolean = false,
|
||||
val defaultArtworkUrl: String? = null
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
package xyz.cottongin.radio247.data.prefs
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.core.longPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class RadioPreferences(private val context: Context) {
|
||||
private val dataStore = context.dataStore
|
||||
|
||||
val stayConnected: Flow<Boolean> = dataStore.data.map { it[STAY_CONNECTED] ?: false }
|
||||
val bufferMs: Flow<Int> = dataStore.data.map { it[BUFFER_MS] ?: 0 }
|
||||
val lastStationId: Flow<Long?> = dataStore.data.map { it[LAST_STATION_ID] }
|
||||
|
||||
suspend fun setStayConnected(value: Boolean) {
|
||||
dataStore.edit { it[STAY_CONNECTED] = value }
|
||||
}
|
||||
|
||||
suspend fun setBufferMs(value: Int) {
|
||||
dataStore.edit { it[BUFFER_MS] = value }
|
||||
}
|
||||
|
||||
suspend fun setLastStationId(value: Long) {
|
||||
dataStore.edit { it[LAST_STATION_ID] = value }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val Context.dataStore by preferencesDataStore(name = "radio_prefs")
|
||||
private val STAY_CONNECTED = booleanPreferencesKey("stay_connected")
|
||||
private val BUFFER_MS = intPreferencesKey("buffer_ms")
|
||||
private val LAST_STATION_ID = longPreferencesKey("last_station_id")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user