diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 98dffab..627db9b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -5,6 +5,10 @@ plugins { alias(libs.plugins.ksp) } +ksp { + arg("room.schemaLocation", "$projectDir/schemas") +} + android { namespace = "xyz.cottongin.radio247" compileSdk = 35 diff --git a/app/schemas/xyz.cottongin.radio247.data.db.RadioDatabase/1.json b/app/schemas/xyz.cottongin.radio247.data.db.RadioDatabase/1.json new file mode 100644 index 0000000..daa2b4c --- /dev/null +++ b/app/schemas/xyz.cottongin.radio247.data.db.RadioDatabase/1.json @@ -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')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/xyz/cottongin/radio247/RadioApplication.kt b/app/src/main/java/xyz/cottongin/radio247/RadioApplication.kt index 0a5d5d3..52b7867 100644 --- a/app/src/main/java/xyz/cottongin/radio247/RadioApplication.kt +++ b/app/src/main/java/xyz/cottongin/radio247/RadioApplication.kt @@ -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) + } +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/db/ConnectionSpanDao.kt b/app/src/main/java/xyz/cottongin/radio247/data/db/ConnectionSpanDao.kt new file mode 100644 index 0000000..6093261 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/db/ConnectionSpanDao.kt @@ -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> + + @Query("SELECT * FROM connection_spans WHERE endedAt IS NULL LIMIT 1") + suspend fun getActiveSpan(): ConnectionSpan? +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/db/ListeningSessionDao.kt b/app/src/main/java/xyz/cottongin/radio247/data/db/ListeningSessionDao.kt new file mode 100644 index 0000000..6bf14e1 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/db/ListeningSessionDao.kt @@ -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> +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/db/MetadataSnapshotDao.kt b/app/src/main/java/xyz/cottongin/radio247/data/db/MetadataSnapshotDao.kt new file mode 100644 index 0000000..4c91dc6 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/db/MetadataSnapshotDao.kt @@ -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> + + @Query("SELECT * FROM metadata_snapshots ORDER BY timestamp DESC LIMIT :limit") + fun getRecent(limit: Int = 100): Flow> + + @Query("SELECT * FROM metadata_snapshots WHERE artist LIKE '%' || :query || '%' OR title LIKE '%' || :query || '%' ORDER BY timestamp DESC") + fun search(query: String): Flow> +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/db/PlaylistDao.kt b/app/src/main/java/xyz/cottongin/radio247/data/db/PlaylistDao.kt new file mode 100644 index 0000000..7a79bb2 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/db/PlaylistDao.kt @@ -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> + + @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) +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/db/RadioDatabase.kt b/app/src/main/java/xyz/cottongin/radio247/data/db/RadioDatabase.kt new file mode 100644 index 0000000..af74edd --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/db/RadioDatabase.kt @@ -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 +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/db/StationDao.kt b/app/src/main/java/xyz/cottongin/radio247/data/db/StationDao.kt new file mode 100644 index 0000000..08f0b86 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/db/StationDao.kt @@ -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> + + @Query("SELECT * FROM stations WHERE playlistId = :playlistId ORDER BY starred DESC, sortOrder ASC") + fun getStationsByPlaylist(playlistId: Long): Flow> + + @Query("SELECT * FROM stations WHERE playlistId IS NULL ORDER BY starred DESC, sortOrder ASC") + fun getUnsortedStations(): Flow> + + @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) +} diff --git a/app/src/main/java/xyz/cottongin/radio247/data/model/ConnectionSpan.kt b/app/src/main/java/xyz/cottongin/radio247/data/model/ConnectionSpan.kt new file mode 100644 index 0000000..d42d67c --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/model/ConnectionSpan.kt @@ -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 +) diff --git a/app/src/main/java/xyz/cottongin/radio247/data/model/ListeningSession.kt b/app/src/main/java/xyz/cottongin/radio247/data/model/ListeningSession.kt new file mode 100644 index 0000000..3260eb9 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/model/ListeningSession.kt @@ -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 +) diff --git a/app/src/main/java/xyz/cottongin/radio247/data/model/MetadataSnapshot.kt b/app/src/main/java/xyz/cottongin/radio247/data/model/MetadataSnapshot.kt new file mode 100644 index 0000000..f1d055a --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/model/MetadataSnapshot.kt @@ -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 +) diff --git a/app/src/main/java/xyz/cottongin/radio247/data/model/Playlist.kt b/app/src/main/java/xyz/cottongin/radio247/data/model/Playlist.kt new file mode 100644 index 0000000..05221dd --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/model/Playlist.kt @@ -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 +) diff --git a/app/src/main/java/xyz/cottongin/radio247/data/model/Station.kt b/app/src/main/java/xyz/cottongin/radio247/data/model/Station.kt new file mode 100644 index 0000000..8782991 --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/model/Station.kt @@ -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 +) diff --git a/app/src/main/java/xyz/cottongin/radio247/data/prefs/RadioPreferences.kt b/app/src/main/java/xyz/cottongin/radio247/data/prefs/RadioPreferences.kt new file mode 100644 index 0000000..65f56aa --- /dev/null +++ b/app/src/main/java/xyz/cottongin/radio247/data/prefs/RadioPreferences.kt @@ -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 = dataStore.data.map { it[STAY_CONNECTED] ?: false } + val bufferMs: Flow = dataStore.data.map { it[BUFFER_MS] ?: 0 } + val lastStationId: Flow = 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") + } +}