ultrasonic-app-subsonic-and.../ultrasonic/src/main/kotlin/org/moire/ultrasonic/model/ServerSettingsModel.kt

304 lines
11 KiB
Kotlin

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
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.moire.ultrasonic.R
import org.moire.ultrasonic.app.UApp
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ServerSetting
import org.moire.ultrasonic.data.ServerSettingDao
import timber.log.Timber
/**
* ViewModel to be used in Activities which will handle Server Settings
*/
class ServerSettingsModel(
private val repository: ServerSettingDao,
private val activeServerProvider: ActiveServerProvider,
application: Application
) : AndroidViewModel(application) {
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.
*/
fun getServerList(): LiveData<List<ServerSetting>> {
// This check should run before returning any result
runBlocking {
if (areIndexesMissing()) {
reindexSettings()
}
}
return repository.loadAllServerSettings()
}
/**
* Retrieves a single Server Setting by its index
* This function is asynchronous, uses LiveData to provide the Setting.
*/
fun getServerSetting(index: Int): LiveData<ServerSetting?> {
return repository.getLiveServerSettingByIndex(index)
}
/**
* Moves a Setting up in the Server List by decreasing its index
*/
fun moveItemUp(index: Int) {
if (index <= 1) return
viewModelScope.launch {
val itemToBeMoved = repository.findByIndex(index)
val previousItem = repository.findByIndex(index - 1)
if (itemToBeMoved != null && previousItem != null) {
itemToBeMoved.index = previousItem.index
previousItem.index = index
repository.update(itemToBeMoved, previousItem)
activeServerProvider.invalidateCache()
}
}
}
/**
* Moves a Setting down in the Server List by increasing its index
*/
fun moveItemDown(index: Int) {
viewModelScope.launch {
if (index < repository.getMaxIndex() ?: 0) {
val itemToBeMoved = repository.findByIndex(index)
val nextItem = repository.findByIndex(index + 1)
if (itemToBeMoved != null && nextItem != null) {
itemToBeMoved.index = nextItem.index
nextItem.index = index
repository.update(itemToBeMoved, nextItem)
activeServerProvider.invalidateCache()
}
}
}
}
/**
* Removes a Setting from the database
*/
fun deleteItem(index: Int) {
if (index == 0) return
viewModelScope.launch {
val itemToBeDeleted = repository.findByIndex(index)
if (itemToBeDeleted != null) {
repository.delete(itemToBeDeleted)
Timber.d("deleteItem deleted index: $index")
reindexSettings()
activeServerProvider.invalidateCache()
}
}
}
/**
* Updates a Setting in the database
*/
fun updateItem(serverSetting: ServerSetting?) {
if (serverSetting == null) return
appScope.launch {
repository.update(serverSetting)
activeServerProvider.invalidateCache()
Timber.d("updateItem updated server setting: $serverSetting")
}
}
/**
* Inserts a new Setting into the database
*/
fun saveNewItem(serverSetting: ServerSetting?) {
if (serverSetting == null) return
appScope.launch {
serverSetting.index = (repository.count() ?: 0) + 1
serverSetting.id = (repository.getMaxId() ?: 0) + 1
repository.insert(serverSetting)
Timber.d("saveNewItem saved server setting: $serverSetting")
}
}
/**
* Inserts a new Setting into the database
* @return The id of the demo server
*/
fun addDemoServer(): Int {
val demo = DEMO_SERVER_CONFIG.copy()
runBlocking {
demo.index = (repository.count() ?: 0) + 1
demo.id = (repository.getMaxId() ?: 0) + 1
repository.insert(demo)
Timber.d("Added demo server")
}
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
* aren't missing. Ideally the indexes are continuous, but some circumstances (e.g.
* concurrency or migration errors) may get them out of order.
* This would make the List Adapter crash, so it is best to prepare and check the list.
*/
private suspend fun areIndexesMissing(): Boolean {
for (i in 1 until getMaximumIndexToCheck() + 1) {
if (repository.findByIndex(i) == null) return true
}
return false
}
/**
* This function updates all the Server Settings in the DB so their indexing is continuous.
*/
private suspend fun reindexSettings() {
var newIndex = 1
for (i in 1 until getMaximumIndexToCheck() + 1) {
val setting = repository.findByIndex(i)
if (setting != null) {
setting.index = newIndex
newIndex++
repository.update(setting)
Timber.d("reindexSettings saved $setting")
}
}
}
private suspend fun getMaximumIndexToCheck(): Int {
val rowsInDatabase = repository.count() ?: 0
val indexesInDatabase = repository.getMaxIndex() ?: 0
if (rowsInDatabase > indexesInDatabase) return rowsInDatabase
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,
name = UApp.applicationContext().getString(R.string.server_menu_demo),
url = "https://demo.ampache.dev",
userName = "ultrasonic_demo",
password = "W7DumQ3ZUR89Se3",
jukeboxByDefault = false,
allowSelfSignedCertificate = false,
ldapSupport = false,
musicFolderId = null,
minimumApiVersion = "1.13.0",
chatSupport = true,
bookmarkSupport = true,
shareSupport = true,
podcastSupport = true
)
}
}