Merge pull request #719 from ultrasonic/AlbumTrackCachePrepare

Add migration path for coming DB change
This commit is contained in:
tzugen 2022-03-31 12:20:27 +02:00 committed by GitHub
commit e77b5abd3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 145 additions and 149 deletions

View File

@ -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() {

View File

@ -1,4 +1,4 @@
package org.moire.ultrasonic.fragment
package org.moire.ultrasonic.adapters
import android.content.Context
import android.graphics.drawable.Drawable

View File

@ -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<Int> = 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
}
/**

View File

@ -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`")
}
}

View File

@ -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
)
}

View File

@ -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()
}

View File

@ -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())
}
}
/**

View File

@ -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<ServerSetting>()
val context = getApplication<Application>().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,

View File

@ -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()
}