diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt index 571a4ad7..a82d93d1 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/activity/NavigationActivity.kt @@ -157,9 +157,8 @@ class NavigationActivity : AppCompatActivity() { setMenuForServerCapabilities() } - // Determine first run and migrate server settings to DB as early as possible - var showWelcomeScreen = Util.isFirstRun() - val areServersMigrated: Boolean = serverSettingsModel.migrateFromPreferences() + // Determine if this is a first run + val showWelcomeScreen = Util.isFirstRun() // Migrate Feature storage if needed // TODO: Remove in December 2022 @@ -167,9 +166,6 @@ class NavigationActivity : AppCompatActivity() { Settings.migrateFeatureStorage() } - // If there are any servers in the DB, do not show the welcome screen - showWelcomeScreen = showWelcomeScreen and !areServersMigrated - loadSettings() // This is a first run with only the demo entry inside the database @@ -194,14 +190,14 @@ class NavigationActivity : AppCompatActivity() { recreate() } - serverRepository.liveServerCount().observe( - this, - { count -> - cachedServerCount = count ?: 0 - updateNavigationHeaderForServer() - } - ) - ActiveServerProvider.liveActiveServerId.observe(this, { updateNavigationHeaderForServer() }) + serverRepository.liveServerCount().observe(this) { count -> + cachedServerCount = count ?: 0 + updateNavigationHeaderForServer() + } + + ActiveServerProvider.liveActiveServerId.observe(this) { + updateNavigationHeaderForServer() + } } private fun updateNavigationHeaderForServer() { diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/ServerRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/ServerRowAdapter.kt index 89c7a5ea..8790bf7c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/ServerRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/adapters/ServerRowAdapter.kt @@ -1,4 +1,4 @@ -package org.moire.ultrasonic.fragment +package org.moire.ultrasonic.adapters import android.content.Context import android.graphics.drawable.Drawable diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt index be4af76f..e132c67f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ActiveServerProvider.kt @@ -19,8 +19,6 @@ import timber.log.Timber /** * This class can be used to retrieve the properties of the Active Server * It caches the settings read up from the DB to improve performance. - * - * TODO: There seems to be some confusion whether offline id is 0 or -1. Clean this up (carefully!) */ class ActiveServerProvider( private val repository: ServerSettingDao @@ -35,7 +33,7 @@ class ActiveServerProvider( */ @JvmOverloads fun getActiveServer(serverId: Int = getActiveServerId()): ServerSetting { - if (serverId > 0) { + if (serverId > OFFLINE_DB_ID) { if (cachedServer != null && cachedServer!!.id == serverId) return cachedServer!! // Ideally this is the only call where we block the thread while using the repository @@ -53,22 +51,11 @@ class ActiveServerProvider( return cachedServer!! } - setActiveServerId(0) + // Fallback to Offline + setActiveServerId(OFFLINE_DB_ID) } - return ServerSetting( - id = -1, - index = 0, - name = UApp.applicationContext().getString(R.string.main_offline), - url = "http://localhost", - userName = "", - password = "", - jukeboxByDefault = false, - allowSelfSignedCertificate = false, - ldapSupport = false, - musicFolderId = "", - minimumApiVersion = null - ) + return OFFLINE_DB } /** @@ -77,9 +64,9 @@ class ActiveServerProvider( */ fun setActiveServerByIndex(index: Int) { Timber.d("setActiveServerByIndex $index") - if (index < 1) { + if (index <= OFFLINE_DB_INDEX) { // Offline mode is selected - setActiveServerId(0) + setActiveServerId(OFFLINE_DB_ID) return } @@ -103,22 +90,20 @@ class ActiveServerProvider( Timber.i("Switching to new database, id:$activeServer") cachedServerId = activeServer - return Room.databaseBuilder( - UApp.applicationContext(), - MetaDatabase::class.java, - METADATA_DB + cachedServerId - ) - .fallbackToDestructiveMigrationOnDowngrade() - .build() + return buildDatabase(cachedServerId) } val offlineMetaDatabase: MetaDatabase by lazy { - Room.databaseBuilder( + buildDatabase(OFFLINE_DB_ID) + } + + private fun buildDatabase(id: Int?): MetaDatabase { + return Room.databaseBuilder( UApp.applicationContext(), MetaDatabase::class.java, - METADATA_DB + 0 + METADATA_DB + id ) - .fallbackToDestructiveMigrationOnDowngrade() + .fallbackToDestructiveMigration() .build() } @@ -177,8 +162,24 @@ class ActiveServerProvider( } companion object { - const val METADATA_DB = "$DB_FILENAME-meta-" + const val OFFLINE_DB_ID = -1 + const val OFFLINE_DB_INDEX = 0 + + val OFFLINE_DB = ServerSetting( + id = OFFLINE_DB_ID, + index = OFFLINE_DB_INDEX, + name = UApp.applicationContext().getString(R.string.main_offline), + url = "http://localhost", + userName = "", + password = "", + jukeboxByDefault = false, + allowSelfSignedCertificate = false, + ldapSupport = false, + musicFolderId = "", + minimumApiVersion = null + ) + val liveActiveServerId: MutableLiveData = MutableLiveData(getActiveServerId()) /** @@ -186,7 +187,7 @@ class ActiveServerProvider( * @return True, if the "Offline" mode is selected */ fun isOffline(): Boolean { - return getActiveServerId() < 1 + return getActiveServerId() == OFFLINE_DB_ID } /** diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt index 2eecb7d7..125f9a31 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/AppDatabase.kt @@ -9,7 +9,11 @@ import androidx.sqlite.db.SupportSQLiteDatabase * Room Database to be used to store global data for the whole app. * This could be settings or data that are not specific to any remote music database */ -@Database(entities = [ServerSetting::class], version = 4) +@Database( + entities = [ServerSetting::class], + version = 4, + exportSchema = true +) abstract class AppDatabase : RoomDatabase() { /** @@ -175,3 +179,89 @@ val MIGRATION_4_3: Migration = object : Migration(4, 3) { ) } } + +val MIGRATION_4_5: Migration = object : Migration(4, 5) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `_new_ServerSetting` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `index` INTEGER NOT NULL, + `name` TEXT NOT NULL, + `url` TEXT NOT NULL, + `color` INTEGER, + `userName` TEXT NOT NULL, + `password` TEXT NOT NULL, + `jukeboxByDefault` INTEGER NOT NULL, + `allowSelfSignedCertificate` INTEGER NOT NULL, + `ldapSupport` INTEGER NOT NULL, + `musicFolderId` TEXT, + `minimumApiVersion` TEXT, + `chatSupport` INTEGER, + `bookmarkSupport` INTEGER, + `shareSupport` INTEGER, + `podcastSupport` INTEGER + ) + """.trimIndent() + ) + database.execSQL( + """ + INSERT INTO `_new_ServerSetting` ( + `ldapSupport`,`musicFolderId`,`color`,`index`,`userName`,`minimumApiVersion`, + `jukeboxByDefault`,`url`,`password`,`shareSupport`,`bookmarkSupport`,`name`, + `podcastSupport`,`id`,`allowSelfSignedCertificate`,`chatSupport` + ) + SELECT `ldapSupport`,`musicFolderId`,`color`,`index`,`userName`, + `minimumApiVersion`,`jukeboxByDefault`,`url`,`password`,`shareSupport`, + `bookmarkSupport`,`name`,`podcastSupport`,`id`,`allowSelfSignedCertificate`, + `chatSupport` + FROM `ServerSetting` + """.trimIndent() + ) + database.execSQL("DROP TABLE `ServerSetting`") + database.execSQL("ALTER TABLE `_new_ServerSetting` RENAME TO `ServerSetting`") + } +} + +val MIGRATION_5_4: Migration = object : Migration(5, 4) { + override fun migrate(database: SupportSQLiteDatabase) { + database.execSQL( + """ + CREATE TABLE IF NOT EXISTS `_new_ServerSetting` ( + `id` INTEGER PRIMARY KEY NOT NULL, + `index` INTEGER NOT NULL, + `name` TEXT NOT NULL, + `url` TEXT NOT NULL, + `color` INTEGER, + `userName` TEXT NOT NULL, + `password` TEXT NOT NULL, + `jukeboxByDefault` INTEGER NOT NULL, + `allowSelfSignedCertificate` INTEGER NOT NULL, + `ldapSupport` INTEGER NOT NULL, + `musicFolderId` TEXT, + `minimumApiVersion` TEXT, + `chatSupport` INTEGER, + `bookmarkSupport` INTEGER, + `shareSupport` INTEGER, + `podcastSupport` INTEGER + ) + """.trimIndent() + ) + database.execSQL( + """ + INSERT INTO `_new_ServerSetting` ( + `ldapSupport`,`musicFolderId`,`color`,`index`,`userName`,`minimumApiVersion`, + `jukeboxByDefault`,`url`,`password`,`shareSupport`,`bookmarkSupport`,`name`, + `podcastSupport`,`id`,`allowSelfSignedCertificate`,`chatSupport` + ) + SELECT `ldapSupport`,`musicFolderId`,`color`,`index`,`userName`, + `minimumApiVersion`,`jukeboxByDefault`,`url`,`password`,`shareSupport`, + `bookmarkSupport`,`name`,`podcastSupport`,`id`,`allowSelfSignedCertificate`, + `chatSupport` + FROM `ServerSetting` + """.trimIndent() + ) + database.execSQL("DROP TABLE `ServerSetting`") + database.execSQL("ALTER TABLE `_new_ServerSetting` RENAME TO `ServerSetting`") + } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ServerSetting.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ServerSetting.kt index e3fb9b04..e3bf722c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ServerSetting.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/data/ServerSetting.kt @@ -39,7 +39,4 @@ data class ServerSetting( constructor() : this ( -1, 0, "", "", null, "", "", false, false, false, null, null ) - constructor(name: String, url: String) : this( - -1, 0, name, url, null, "", "", false, false, false, null, null - ) } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt index adce9869..fcbc0f58 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/AppPermanentStorageModule.kt @@ -12,6 +12,8 @@ import org.moire.ultrasonic.data.MIGRATION_2_3 import org.moire.ultrasonic.data.MIGRATION_3_2 import org.moire.ultrasonic.data.MIGRATION_3_4 import org.moire.ultrasonic.data.MIGRATION_4_3 +import org.moire.ultrasonic.data.MIGRATION_4_5 +import org.moire.ultrasonic.data.MIGRATION_5_4 import org.moire.ultrasonic.model.ServerSettingsModel import org.moire.ultrasonic.util.Settings @@ -36,6 +38,8 @@ val appPermanentStorage = module { .addMigrations(MIGRATION_3_2) .addMigrations(MIGRATION_3_4) .addMigrations(MIGRATION_4_3) + .addMigrations(MIGRATION_4_5) + .addMigrations(MIGRATION_5_4) .build() } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt index f5827d1f..d676e0ee 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ServerSelectorFragment.kt @@ -15,6 +15,7 @@ import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import org.moire.ultrasonic.R +import org.moire.ultrasonic.adapters.ServerRowAdapter import org.moire.ultrasonic.data.ActiveServerProvider import org.moire.ultrasonic.fragment.EditServerFragment.Companion.EDIT_SERVER_INTENT_INDEX import org.moire.ultrasonic.model.ServerSettingsModel @@ -103,11 +104,10 @@ class ServerSelectorFragment : Fragment() { super.onResume() val serverList = serverSettingsModel.getServerList() serverList.observe( - this, - { t -> - serverRowAdapter!!.setData(t.toTypedArray()) - } - ) + this + ) { t -> + serverRowAdapter!!.setData(t.toTypedArray()) + } } /** diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/model/ServerSettingsModel.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/model/ServerSettingsModel.kt index 2f520617..99ea5890 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/model/ServerSettingsModel.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/model/ServerSettingsModel.kt @@ -1,11 +1,9 @@ package org.moire.ultrasonic.model import android.app.Application -import android.content.SharedPreferences import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope -import androidx.preference.PreferenceManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -29,46 +27,6 @@ class ServerSettingsModel( private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - /** - * This function will try and convert settings from the Preferences to the Database - * @return True, if the migration was executed, False otherwise - */ - fun migrateFromPreferences(): Boolean { - var migrated = true - - runBlocking { - val rowCount = repository.count() - - if (rowCount == null || rowCount == 0) { - // First time load up the server settings from the Preferences - val dbServerList = mutableListOf() - val context = getApplication().applicationContext - val settings = PreferenceManager.getDefaultSharedPreferences(context) - val serverNum = settings.getInt(PREFERENCES_KEY_ACTIVE_SERVERS, 0) - - if (serverNum != 0) { - var index = 1 - for (x in 1 until serverNum + 1) { - val newServerSetting = loadServerSettingFromPreferences(x, index, settings) - if (newServerSetting != null) { - dbServerList.add(newServerSetting) - repository.insert(newServerSetting) - index++ - Timber.i( - "Imported server from Preferences to Database: %s", - newServerSetting.name - ) - } - } - } else { - migrated = false - } - } - } - - return migrated - } - /** * Retrieves the list of the configured servers from the database. * This function is asynchronous, uses LiveData to provide the Setting. @@ -192,40 +150,6 @@ class ServerSettingsModel( return demo.id } - /** - * Reads up a Server Setting stored in the obsolete Preferences - */ - private fun loadServerSettingFromPreferences( - preferenceId: Int, - serverId: Int, - settings: SharedPreferences - ): ServerSetting? { - val url = settings.getString(PREFERENCES_KEY_SERVER_URL + preferenceId, "") - val userName = settings.getString(PREFERENCES_KEY_USERNAME + preferenceId, "") - val isMigrated = settings.getBoolean(PREFERENCES_KEY_SERVER_MIGRATED + preferenceId, false) - - if (url.isNullOrEmpty() || userName.isNullOrEmpty() || isMigrated) return null - setServerMigrated(settings, preferenceId) - - return ServerSetting( - preferenceId, - serverId, - settings.getString(PREFERENCES_KEY_SERVER_NAME + preferenceId, "")!!, - url, - null, - userName, - settings.getString(PREFERENCES_KEY_PASSWORD + preferenceId, "")!!, - settings.getBoolean(PREFERENCES_KEY_JUKEBOX_BY_DEFAULT + preferenceId, false), - settings.getBoolean( - PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + preferenceId, - false - ), - settings.getBoolean(PREFERENCES_KEY_LDAP_SUPPORT + preferenceId, false), - settings.getString(PREFERENCES_KEY_MUSIC_FOLDER_ID + preferenceId, null), - null - ) - } - /** * Checks if there are any missing indexes in the ServerSetting list * For displaying the Server Settings in a ListView, it is mandatory that their indexes @@ -263,25 +187,7 @@ class ServerSettingsModel( return indexesInDatabase } - private fun setServerMigrated(settings: SharedPreferences, preferenceId: Int) { - val editor = settings.edit() - editor.putBoolean(PREFERENCES_KEY_SERVER_MIGRATED + preferenceId, true) - editor.apply() - } - companion object { - private const val PREFERENCES_KEY_SERVER_MIGRATED = "serverMigrated" - // These constants were removed from Constants.java as they are deprecated and only used here - private const val PREFERENCES_KEY_JUKEBOX_BY_DEFAULT = "jukeboxEnabled" - private const val PREFERENCES_KEY_SERVER_NAME = "serverName" - private const val PREFERENCES_KEY_SERVER_URL = "serverUrl" - private const val PREFERENCES_KEY_ACTIVE_SERVERS = "activeServers" - private const val PREFERENCES_KEY_USERNAME = "username" - private const val PREFERENCES_KEY_PASSWORD = "password" - private const val PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE = "allowSSCertificate" - private const val PREFERENCES_KEY_LDAP_SUPPORT = "enableLdapSupport" - private const val PREFERENCES_KEY_MUSIC_FOLDER_ID = "musicFolderId" - private val DEMO_SERVER_CONFIG = ServerSetting( id = 0, index = 0, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaybackStateSerializer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaybackStateSerializer.kt index 3b9b2aa0..7115140a 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaybackStateSerializer.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/PlaybackStateSerializer.kt @@ -83,6 +83,8 @@ class PlaybackStateSerializer : KoinComponent { lock.lock() deserializeNow(afterDeserialized) setup.set(true) + } catch (all: Exception) { + Timber.e(all, "Had a problem deserializing:") } finally { lock.unlock() }