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

304 lines
11 KiB
Kotlin
Raw Normal View History

package org.moire.ultrasonic.model
2021-04-21 23:01:59 +02:00
import android.app.Application
import android.content.SharedPreferences
2021-04-21 23:01:59 +02:00
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
2020-09-19 11:56:10 +02:00
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
2020-09-30 14:47:59 +02:00
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,
2021-04-21 23:01:59 +02:00
application: Application
) : AndroidViewModel(application) {
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
/**
2020-09-19 11:56:10 +02:00
* This function will try and convert settings from the Preferences to the Database
* @return True, if the migration was executed, False otherwise
*/
2020-09-19 11:56:10 +02:00
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>()
2021-04-21 23:01:59 +02:00
val context = getApplication<Application>().applicationContext
val settings = PreferenceManager.getDefaultSharedPreferences(context)
val serverNum = settings.getInt(PREFERENCES_KEY_ACTIVE_SERVERS, 0)
if (serverNum != 0) {
2020-09-22 20:18:36 +02:00
var index = 1
for (x in 1 until serverNum + 1) {
2020-09-22 21:50:53 +02:00
val newServerSetting = loadServerSettingFromPreferences(x, index, settings)
2020-09-19 11:56:10 +02:00
if (newServerSetting != null) {
dbServerList.add(newServerSetting)
repository.insert(newServerSetting)
2020-09-22 20:18:36 +02:00
index++
2020-09-30 14:47:59 +02:00
Timber.i(
"Imported server from Preferences to Database: %s",
newServerSetting.name
2020-09-19 11:56:10 +02:00
)
}
}
2020-09-19 11:56:10 +02:00
} else {
migrated = false
}
}
2020-09-19 11:56:10 +02:00
}
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()
2020-09-24 18:20:59 +02:00
}
}
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)
2020-09-30 14:47:59 +02:00
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()
2020-09-30 14:47:59 +02:00
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
2020-09-24 18:20:59 +02:00
serverSetting.id = (repository.getMaxId() ?: 0) + 1
repository.insert(serverSetting)
2020-09-30 14:47:59 +02:00
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(
2020-09-22 21:50:53 +02:00
preferenceId: Int,
serverId: Int,
settings: SharedPreferences
2020-09-19 11:56:10 +02:00
): ServerSetting? {
2020-09-22 21:50:53 +02:00
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)
2020-09-19 11:56:10 +02:00
if (url.isNullOrEmpty() || userName.isNullOrEmpty() || isMigrated) return null
setServerMigrated(settings, preferenceId)
2020-09-19 11:56:10 +02:00
return ServerSetting(
preferenceId,
2020-09-22 21:50:53 +02:00
serverId,
settings.getString(PREFERENCES_KEY_SERVER_NAME + preferenceId, "")!!,
2020-09-19 11:56:10 +02:00
url,
null,
2020-09-19 11:56:10 +02:00
userName,
2020-09-22 21:50:53 +02:00
settings.getString(PREFERENCES_KEY_PASSWORD + preferenceId, "")!!,
settings.getBoolean(PREFERENCES_KEY_JUKEBOX_BY_DEFAULT + preferenceId, false),
2020-09-22 22:01:33 +02:00
settings.getBoolean(
PREFERENCES_KEY_ALLOW_SELF_SIGNED_CERTIFICATE + preferenceId,
false
),
2020-09-22 21:50:53 +02:00
settings.getBoolean(PREFERENCES_KEY_LDAP_SUPPORT + preferenceId, false),
settings.getString(PREFERENCES_KEY_MUSIC_FOLDER_ID + preferenceId, null),
null
)
}
2020-09-24 18:20:59 +02:00
/**
* 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.
2020-09-24 18:20:59 +02:00
* 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
2020-09-24 18:20:59 +02:00
}
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)
2020-09-30 14:47:59 +02:00
Timber.d("reindexSettings saved $setting")
}
2020-09-24 18:20:59 +02:00
}
}
private suspend fun getMaximumIndexToCheck(): Int {
val rowsInDatabase = repository.count() ?: 0
val indexesInDatabase = repository.getMaxIndex() ?: 0
if (rowsInDatabase > indexesInDatabase) return rowsInDatabase
return indexesInDatabase
2020-09-24 18:20:59 +02:00
}
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
)
}
}