Skip to content

Commit

Permalink
Give user more insight into sync status (#394)
Browse files Browse the repository at this point in the history
* Show last sync time in preferences

* Show toast with error when syncing fails
  • Loading branch information
chvp authored Jun 21, 2022
1 parent c537d63 commit f404e91
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,36 @@ package me.vanpetegem.accentor.data.preferences

import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations.map
import dagger.hilt.android.qualifiers.ApplicationContext
import java.time.Instant
import javax.inject.Inject
import me.vanpetegem.accentor.util.intLiveData
import me.vanpetegem.accentor.util.longLiveData
import me.vanpetegem.accentor.util.stringLiveData

const val CONVERSION_ID_KEY = "conversion_id"
const val IMAGE_CACHE_SIZE_KEY = "image_cache_size"
const val MUSIC_CACHE_SIZE_KEY = "music_cache_size"
const val LAST_SYNC_FINISHED = "last_sync_finished"

class PreferencesDataSource @Inject constructor(@ApplicationContext private val context: Context) {
private val sharedPreferences = context.getSharedPreferences("me.vanpetegem.accentor.preferences", Context.MODE_PRIVATE)

private val conversionIdData = sharedPreferences.intLiveData(CONVERSION_ID_KEY)
private val imageCacheSizeData = sharedPreferences.longLiveData(IMAGE_CACHE_SIZE_KEY, 1024L * 1024L * 1024L)
private val musicCacheSizeData = sharedPreferences.longLiveData(MUSIC_CACHE_SIZE_KEY, 10L * 1024L * 1024L * 1024L)
private val lastSyncFinishedData = sharedPreferences.stringLiveData(LAST_SYNC_FINISHED)

val conversionId: LiveData<Int> = conversionIdData
val imageCacheSize: LiveData<Long> = imageCacheSizeData
val musicCacheSize: LiveData<Long> = musicCacheSizeData
val lastSyncFinished: LiveData<Instant> = map(lastSyncFinishedData) {
it?.let { Instant.parse(it) }
}

fun setConversionId(id: Int) = sharedPreferences.edit().putInt(CONVERSION_ID_KEY, id).apply()
fun setImageCacheSize(size: Long) = sharedPreferences.edit().putLong(IMAGE_CACHE_SIZE_KEY, size).apply()
fun setMusicCacheSize(size: Long) = sharedPreferences.edit().putLong(MUSIC_CACHE_SIZE_KEY, size).apply()
fun setLastSyncFinished(time: Instant) = sharedPreferences.edit().putString(LAST_SYNC_FINISHED, time.toString()).apply()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package me.vanpetegem.accentor.ui.main
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
Expand Down Expand Up @@ -120,6 +121,14 @@ fun Content(mainViewModel: MainViewModel = viewModel(), playerViewModel: PlayerV
}
}

val latestError by mainViewModel.latestError.observeAsState()
LaunchedEffect(latestError) {
val error = latestError?.get()
if (error != null) {
Toast.makeText(context, error, Toast.LENGTH_LONG).show()
}
}

PlayerOverlay(navController) {
NavHost(navController = navController, startDestination = "home") {
composable("home") { Base(navController, mainViewModel, playerViewModel) { Home(navController, playerViewModel) } }
Expand Down
47 changes: 30 additions & 17 deletions app/src/main/java/me/vanpetegem/accentor/ui/main/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Transformations.map
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.Instant
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
Expand All @@ -17,9 +18,12 @@ import me.vanpetegem.accentor.data.artists.ArtistRepository
import me.vanpetegem.accentor.data.authentication.AuthenticationRepository
import me.vanpetegem.accentor.data.codecconversions.CodecConversionRepository
import me.vanpetegem.accentor.data.plays.PlayRepository
import me.vanpetegem.accentor.data.preferences.PreferencesDataSource
import me.vanpetegem.accentor.data.tracks.TrackRepository
import me.vanpetegem.accentor.data.users.User
import me.vanpetegem.accentor.data.users.UserRepository
import me.vanpetegem.accentor.ui.util.Event
import me.vanpetegem.accentor.util.Result

@HiltViewModel
class MainViewModel @Inject constructor(
Expand All @@ -31,55 +35,64 @@ class MainViewModel @Inject constructor(
private val trackRepository: TrackRepository,
private val codecConversionRepository: CodecConversionRepository,
private val playRepository: PlayRepository,
private val preferencesDataSource: PreferencesDataSource,
) : AndroidViewModel(application) {
private val refreshing = MutableLiveData<Int>(0)
val isRefreshing: LiveData<Boolean> = map(refreshing) { if (it != null) it > 0 else false }
private var errorSinceLastRefresh: Boolean = false

private val _latestError = MutableLiveData<Event<String>?>(null)
val latestError: LiveData<Event<String>?> = _latestError

val currentUser: LiveData<User?> = userRepository.currentUser
val loginState: LiveData<Boolean> = authenticationRepository.isLoggedIn

fun refresh() {
if ((refreshing.value ?: 0) > 0) return

errorSinceLastRefresh = false

refreshing.value?.let { refreshing.value = it + 1 }
viewModelScope.launch(IO) {
codecConversionRepository.refresh {
withContext(Main) { refreshing.value?.let { refreshing.value = it - 1 } }
}
codecConversionRepository.refresh { decrementRefresh(it) }
}

refreshing.value?.let { refreshing.value = it + 1 }
viewModelScope.launch(IO) {
userRepository.refresh {
withContext(Main) { refreshing.value?.let { refreshing.value = it - 1 } }
}
userRepository.refresh { decrementRefresh(it) }
}

refreshing.value?.let { refreshing.value = it + 1 }
viewModelScope.launch(IO) {
trackRepository.refresh {
withContext(Main) { refreshing.value?.let { refreshing.value = it - 1 } }
}
trackRepository.refresh { decrementRefresh(it) }
}

refreshing.value?.let { refreshing.value = it + 1 }
viewModelScope.launch(IO) {
artistRepository.refresh {
withContext(Main) { refreshing.value?.let { refreshing.value = it - 1 } }
}
artistRepository.refresh { decrementRefresh(it) }
}

refreshing.value?.let { refreshing.value = it + 1 }
viewModelScope.launch(IO) {
albumRepository.refresh {
withContext(Main) { refreshing.value?.let { refreshing.value = it - 1 } }
}
albumRepository.refresh { decrementRefresh(it) }
}

refreshing.value?.let { refreshing.value = it + 1 }
viewModelScope.launch(IO) {
playRepository.refresh {
withContext(Main) { refreshing.value?.let { refreshing.value = it - 1 } }
playRepository.refresh { decrementRefresh(it) }
}
}

suspend fun decrementRefresh(result: Result<Unit>) {
withContext(Main) {
refreshing.value?.let { refreshing.value = it - 1 }
if (result is Result.Error) {
errorSinceLastRefresh = true
_latestError.value = Event(result.exception.message!!)
}

if (refreshing.value == 0 && !errorSinceLastRefresh) {
withContext(IO) { preferencesDataSource.setLastSyncFinished(Instant.now()) }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package me.vanpetegem.accentor.ui.preferences

import android.app.Activity
import android.os.Bundle
import android.text.format.DateUtils
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
Expand Down Expand Up @@ -75,12 +76,17 @@ fun Content(preferencesViewModel: PreferencesViewModel = viewModel()) {
) { innerPadding ->
val currentUser by preferencesViewModel.currentUser.observeAsState()
val server by preferencesViewModel.server.observeAsState()
val lastSyncFinished by preferencesViewModel.lastSyncFinished.observeAsState()
val imageCacheSize by preferencesViewModel.imageCacheSize.observeAsState()
val musicCacheSize by preferencesViewModel.musicCacheSize.observeAsState()
val conversion by preferencesViewModel.conversion.observeAsState()
val possibleConversions by preferencesViewModel.possibleConversions.observeAsState()
val formattedTime = lastSyncFinished?.let {
DateUtils.formatDateTime(context, it.toEpochMilli(), DateUtils.FORMAT_SHOW_TIME or DateUtils.FORMAT_SHOW_DATE)
} ?: stringResource(R.string.not_finished_yet)
Column(modifier = Modifier.padding(innerPadding)) {
Setting(stringResource(R.string.logged_in_as, "${currentUser?.name}"), server!!)
Setting(stringResource(R.string.last_sync_finished), formattedTime)
Header(stringResource(R.string.playback_settings))
var musicCacheOpen by remember { mutableStateOf(false) }
var newMusicCacheValue by remember { mutableStateOf("${musicCacheSize!! / 1024L / 1024L}") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Transformations.map
import androidx.lifecycle.Transformations.switchMap
import dagger.hilt.android.lifecycle.HiltViewModel
import java.time.Instant
import javax.inject.Inject
import me.vanpetegem.accentor.data.authentication.AuthenticationRepository
import me.vanpetegem.accentor.data.codecconversions.CodecConversion
Expand All @@ -28,6 +29,7 @@ class PreferencesViewModel @Inject constructor(
val server: LiveData<String> = authenticationRepository.server
val imageCacheSize: LiveData<Long> = preferencesDataSource.imageCacheSize
val musicCacheSize: LiveData<Long> = preferencesDataSource.musicCacheSize
val lastSyncFinished: LiveData<Instant> = preferencesDataSource.lastSyncFinished
val conversion: LiveData<CodecConversion> = switchMap(codecConversionRepository.allCodecConversionsById) { ccsMap ->
switchMap(codecConversionRepository.allCodecConversions) { ccs ->
map(conversionId) { it?.let { ccsMap[it] } ?: ccs.firstOrNull() }
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/java/me/vanpetegem/accentor/ui/util/Event.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package me.vanpetegem.accentor.ui.util

open class Event<out T>(private val content: T) {

var handled = false
private set

fun get(): T? {
if (handled) {
return null
}
handled = true
return content
}

fun peek(): T = content
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
<string name="playback_settings">Playback settings</string>
<string name="about">About</string>
<string name="logged_in_as">Logged in as %s</string>
<string name="last_sync_finished">Last sync finished</string>
<string name="not_finished_yet">Not finished yet</string>
<string name="version_info">Accentor version %s</string>
<string name="music_cache_size">Music cache size</string>
<string name="image_cache_size">Image cache size</string>
Expand Down

0 comments on commit f404e91

Please sign in to comment.