Merge branch 'develop' into vector

This commit is contained in:
Óscar García Amor 2020-09-25 13:35:11 +02:00
commit 89ef73ccec
No known key found for this signature in database
GPG Key ID: E18B2370D3D566EE
11 changed files with 141 additions and 70 deletions

View File

@ -111,13 +111,14 @@ public class SelectGenreActivity extends SubsonicTabActivity implements AdapterV
@Override
protected List<Genre> doInBackground() throws Throwable
{
boolean refresh = getIntent().getBooleanExtra(Constants.INTENT_EXTRA_NAME_REFRESH, false);
MusicService musicService = MusicServiceFactory.getMusicService(SelectGenreActivity.this);
List<Genre> genres = new ArrayList<Genre>();
try
{
genres = musicService.getGenres(SelectGenreActivity.this, this);
genres = musicService.getGenres(refresh, SelectGenreActivity.this, this);
}
catch (Exception x)
{

View File

@ -405,14 +405,18 @@ public class CachedMusicService implements MusicService
}
@Override
public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception
{
checkSettingsChanged(context);
if (refresh)
{
cachedGenres.clear();
}
List<Genre> result = cachedGenres.get();
if (result == null)
{
result = musicService.getGenres(context, progressListener);
result = musicService.getGenres(refresh, context, progressListener);
cachedGenres.set(result);
}

View File

@ -53,7 +53,7 @@ public interface MusicService
boolean isLicenseValid(Context context, ProgressListener progressListener) throws Exception;
List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception;
List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception;
void star(String id, String albumId, String artistId, Context context, ProgressListener progressListener) throws Exception;

View File

@ -770,7 +770,7 @@ public class OfflineMusicService extends RESTMusicService
}
@Override
public List<Genre> getGenres(Context context, ProgressListener progressListener) throws Exception
public List<Genre> getGenres(boolean refresh, Context context, ProgressListener progressListener) throws Exception
{
throw new OfflineException("Getting Genres not available in offline mode");
}

View File

@ -829,7 +829,7 @@ public class RESTMusicService implements MusicService {
}
@Override
public List<Genre> getGenres(Context context,
public List<Genre> getGenres(boolean refresh, Context context,
ProgressListener progressListener) throws Exception {
updateProgressListener(progressListener, R.string.parser_reading);
Response<GenresResponse> response = subsonicAPIClient.getApi().getGenres().execute();

View File

@ -13,6 +13,7 @@ import com.google.android.material.textfield.TextInputLayout
import java.io.IOException
import java.net.MalformedURLException
import java.net.URL
import org.koin.android.ext.android.inject
import org.koin.android.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.BuildConfig
import org.moire.ultrasonic.R
@ -20,7 +21,9 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
import org.moire.ultrasonic.api.subsonic.response.SubsonicResponse
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ServerSetting
import org.moire.ultrasonic.service.MusicServiceFactory
import org.moire.ultrasonic.service.SubsonicRESTException
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.ErrorDialog
@ -41,6 +44,8 @@ internal class EditServerActivity : AppCompatActivity() {
}
private val serverSettingsModel: ServerSettingsModel by viewModel()
private val activeServerProvider: ActiveServerProvider by inject()
private var currentServerSetting: ServerSetting? = null
private var serverNameEditText: TextInputLayout? = null
@ -90,6 +95,13 @@ internal class EditServerActivity : AppCompatActivity() {
if (currentServerSetting != null) {
if (getFields()) {
serverSettingsModel.updateItem(currentServerSetting)
// Apply modifications if the current server was modified
if (
activeServerProvider.getActiveServer().id ==
currentServerSetting!!.id
) {
MusicServiceFactory.resetMusicService()
}
finish()
}
}

View File

@ -53,7 +53,7 @@ internal class ServerRowAdapter(
}
override fun getCount(): Int {
return if (manageMode) data.size - 1 else data.size
return if (manageMode) data.size else data.size + 1
}
override fun getItem(position: Int): Any {
@ -81,8 +81,15 @@ internal class ServerRowAdapter(
val image = vi?.findViewById<ImageView>(R.id.server_image)
val serverMenu = vi?.findViewById<ImageButton>(R.id.server_menu)
text?.text = data.single { setting -> setting.index == index }.name
description?.text = data.single { setting -> setting.index == index }.url
if (index == 0) {
text?.text = context.getString(R.string.main_offline)
description?.text = ""
} else {
val setting = data.singleOrNull { t -> t.index == index }
text?.text = setting?.name ?: ""
description?.text = setting?.url ?: ""
if (setting == null) serverMenu?.visibility = View.INVISIBLE
}
// Provide icons for the row
if (index == 0) {

View File

@ -144,8 +144,9 @@ internal class ServerSelectorActivity : AppCompatActivity() {
if (activeServerProvider.getActiveServer().index != index) {
service.clearIncomplete()
activeServerProvider.setActiveServerByIndex(index)
service.isJukeboxEnabled =
activeServerProvider.getActiveServer().jukeboxByDefault
}
service.isJukeboxEnabled = activeServerProvider.getActiveServer().jukeboxByDefault
}
}
Log.i(TAG, "Active server was set to: $index")

View File

@ -5,12 +5,10 @@ import android.content.SharedPreferences
import android.preference.PreferenceManager
import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.moire.ultrasonic.R
import org.moire.ultrasonic.data.ActiveServerProvider
import org.moire.ultrasonic.data.ServerSetting
import org.moire.ultrasonic.data.ServerSettingDao
@ -23,10 +21,10 @@ class ServerSettingsModel(
private val activeServerProvider: ActiveServerProvider,
private val context: Context
) : ViewModel() {
private var serverList: MutableLiveData<List<ServerSetting>> = MutableLiveData()
companion object {
private val TAG = ServerSettingsModel::class.simpleName
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"
@ -84,13 +82,13 @@ class ServerSettingsModel(
* This function is asynchronous, uses LiveData to provide the Setting.
*/
fun getServerList(): LiveData<List<ServerSetting>> {
viewModelScope.launch {
val dbServerList = repository.loadAllServerSettings().toMutableList()
dbServerList.add(0, ServerSetting(context.getString(R.string.main_offline), ""))
serverList.value = dbServerList
// This check should run before returning any result
runBlocking {
if (areIndexesMissing()) {
reindexSettings()
}
}
return serverList
return repository.loadAllServerSettings()
}
/**
@ -98,55 +96,47 @@ class ServerSettingsModel(
* This function is asynchronous, uses LiveData to provide the Setting.
*/
fun getServerSetting(index: Int): LiveData<ServerSetting?> {
val result = MutableLiveData<ServerSetting?>()
viewModelScope.launch {
val dbServer = repository.findByIndex(index)
result.value = dbServer
Log.d(TAG, "getServerSetting($index) returning $dbServer")
}
return result
return repository.getLiveServerSettingByIndex(index)
}
/**
* Moves a Setting up in the Server List by decreasing its index
*/
fun moveItemUp(index: Int) {
if (index == 1) return
val itemToBeMoved = serverList.value?.single { setting -> setting.index == index }
val previousItem = serverList.value?.single { setting -> setting.index == index - 1 }
itemToBeMoved?.index = previousItem!!.index
previousItem.index = index
if (index <= 1) return
viewModelScope.launch {
repository.update(itemToBeMoved!!, previousItem)
}
val itemToBeMoved = repository.findByIndex(index)
val previousItem = repository.findByIndex(index - 1)
activeServerProvider.invalidateCache()
// Notify the observers of the changed values
serverList.value = serverList.value
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) {
if (index == (serverList.value!!.size - 1)) return
val itemToBeMoved = serverList.value?.single { setting -> setting.index == index }
val nextItem = serverList.value?.single { setting -> setting.index == index + 1 }
itemToBeMoved?.index = nextItem!!.index
nextItem.index = index
viewModelScope.launch {
repository.update(itemToBeMoved!!, nextItem)
}
if (index < repository.getMaxIndex() ?: 0) {
val itemToBeMoved = repository.findByIndex(index)
val nextItem = repository.findByIndex(index + 1)
activeServerProvider.invalidateCache()
// Notify the observers of the changed values
serverList.value = serverList.value
if (itemToBeMoved != null && nextItem != null) {
itemToBeMoved.index = nextItem.index
nextItem.index = index
repository.update(itemToBeMoved, nextItem)
activeServerProvider.invalidateCache()
}
}
}
}
/**
@ -155,24 +145,15 @@ class ServerSettingsModel(
fun deleteItem(index: Int) {
if (index == 0) return
val newList = serverList.value!!.toMutableList()
val itemToBeDeleted = newList.single { setting -> setting.index == index }
newList.remove(itemToBeDeleted)
for (x in index + 1 until newList.size + 1) {
newList.single { setting -> setting.index == x }.index--
}
viewModelScope.launch {
repository.delete(itemToBeDeleted)
for (x in index until newList.size) {
repository.update(newList.single { setting -> setting.index == x })
val itemToBeDeleted = repository.findByIndex(index)
if (itemToBeDeleted != null) {
repository.delete(itemToBeDeleted)
Log.d(TAG, "deleteItem deleted index: $index")
reindexSettings()
activeServerProvider.invalidateCache()
}
}
activeServerProvider.invalidateCache()
serverList.value = newList
Log.d(TAG, "deleteItem deleted index: $index")
}
/**
@ -196,7 +177,7 @@ class ServerSettingsModel(
viewModelScope.launch {
serverSetting.index = (repository.count() ?: 0) + 1
serverSetting.id = serverSetting.index
serverSetting.id = (repository.getMaxId() ?: 0) + 1
repository.insert(serverSetting)
Log.d(TAG, "saveNewItem saved server setting: $serverSetting")
}
@ -212,8 +193,10 @@ class ServerSettingsModel(
): 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()) return null
if (url.isNullOrEmpty() || userName.isNullOrEmpty() || isMigrated) return null
setServerMigrated(settings, preferenceId)
return ServerSetting(
preferenceId,
@ -231,4 +214,47 @@ class ServerSettingsModel(
settings.getString(PREFERENCES_KEY_MUSIC_FOLDER_ID + preferenceId, 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
* are'nt 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)
Log.d(TAG, "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()
}
}

View File

@ -75,7 +75,7 @@ class ActiveServerProvider(
}
GlobalScope.launch(Dispatchers.IO) {
val serverId = repository.findByIndex(index)!!.id
val serverId = repository.findByIndex(index)?.id ?: 0
setActiveServerId(context, serverId)
}
}

View File

@ -1,5 +1,6 @@
package org.moire.ultrasonic.data
import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@ -35,7 +36,7 @@ interface ServerSettingDao {
* Loads all Server Settings from the table
*/
@Query("SELECT * FROM serverSetting")
suspend fun loadAllServerSettings(): Array<ServerSetting>
fun loadAllServerSettings(): LiveData<List<ServerSetting>>
/**
* Finds a Server Setting by its unique Id
@ -49,9 +50,28 @@ interface ServerSettingDao {
@Query("SELECT * FROM serverSetting WHERE [index] = :index")
suspend fun findByIndex(index: Int): ServerSetting?
/**
* Finds a Server Setting by its Index in the Select List
* @return LiveData of the ServerSetting
*/
@Query("SELECT * FROM serverSetting WHERE [index] = :index")
fun getLiveServerSettingByIndex(index: Int): LiveData<ServerSetting?>
/**
* Retrieves the count of rows in the table
*/
@Query("SELECT COUNT(*) FROM serverSetting")
suspend fun count(): Int?
/**
* Retrieves the greatest value of the Id column in the table
*/
@Query("SELECT MAX([id]) FROM serverSetting")
suspend fun getMaxId(): Int?
/**
* Retrieves the greatest value of the Index column in the table
*/
@Query("SELECT MAX([index]) FROM serverSetting")
suspend fun getMaxIndex(): Int?
}