Fix budget loading when returning from background
This commit is contained in:
parent
cce6a3f371
commit
1b0afe3753
11 changed files with 88 additions and 67 deletions
|
@ -66,9 +66,7 @@ dependencies {
|
|||
implementation 'com.google.android.material:material:1.1.0'
|
||||
implementation 'androidx.emoji:emoji-bundled:1.0.0'
|
||||
implementation 'com.github.BlacKCaT27:CurrencyEditText:2.0.2'
|
||||
def lifecycle_version = "1.1.1"
|
||||
// ViewModel and LiveData
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
// Dagger
|
||||
implementation "com.google.dagger:dagger:$dagger"
|
||||
kapt "com.google.dagger:dagger-compiler:$dagger"
|
||||
|
|
|
@ -1,18 +1,13 @@
|
|||
package com.wbrawner.budget.ui
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.common.user.User
|
||||
import com.wbrawner.budget.common.user.UserRepository
|
||||
import com.wbrawner.budget.lib.repository.KEY_DEFAULT_BUDGET
|
||||
import javax.inject.Inject
|
||||
|
||||
class SplashViewModel : ViewModel() {
|
||||
@Inject
|
||||
lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
@Inject
|
||||
lateinit var budgetRepository: BudgetRepository
|
||||
|
||||
|
@ -43,15 +38,7 @@ class SplashViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
private suspend fun loadBudgetData() {
|
||||
// TODO: This would fail for a new user that has no budgets
|
||||
val budgets = budgetRepository.findAll()
|
||||
val budgetId = sharedPreferences.getLong(KEY_DEFAULT_BUDGET, budgets.first().id!!)
|
||||
budgetRepository.currentBudget = try {
|
||||
budgetRepository.findById(budgetId)
|
||||
} catch (e: Exception) {
|
||||
// For some reason we can't find the default budget id, so fallback to the first budget
|
||||
budgets.first()
|
||||
}
|
||||
budgetRepository.prefetchData()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class CategoryListFragment : ListWithAddButtonFragment<Category, CategoryListVie
|
|||
override val noItemsStringRes: Int = R.string.categories_no_data
|
||||
override val viewModel: CategoryListViewModel by viewModels()
|
||||
override fun reloadItems() {
|
||||
viewModel.getCategories()
|
||||
viewModel.getCategories(viewLifecycleOwner)
|
||||
}
|
||||
|
||||
override fun bindData(data: Category): BindableData<Category> = BindableData(data, CATEGORY_VIEW)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.wbrawner.budget.ui.categories
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.AsyncViewModel
|
||||
|
@ -19,15 +21,17 @@ class CategoryListViewModel : ViewModel(), AsyncViewModel<List<Category>> {
|
|||
@Inject
|
||||
lateinit var categoryRepo: CategoryRepository
|
||||
|
||||
fun getCategories() {
|
||||
val budgetId = budgetRepo.currentBudget?.id
|
||||
if (budgetId == null) {
|
||||
state.postValue(AsyncState.Error("Invalid budget ID"))
|
||||
return
|
||||
}
|
||||
launch {
|
||||
categoryRepo.findAll(arrayOf(budgetId)).toList()
|
||||
}
|
||||
fun getCategories(lifecycleOwner: LifecycleOwner) {
|
||||
budgetRepo.currentBudget.observe(lifecycleOwner, Observer {
|
||||
val budgetId = budgetRepo.currentBudget.value?.id
|
||||
if (budgetId == null) {
|
||||
state.postValue(AsyncState.Error("Invalid budget ID"))
|
||||
return@Observer
|
||||
}
|
||||
launch {
|
||||
categoryRepo.findAll(arrayOf(budgetId)).toList()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun getBalance(category: Category): Long {
|
||||
|
|
|
@ -56,7 +56,7 @@ class OverviewFragment : Fragment() {
|
|||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
viewModel.loadOverview()
|
||||
viewModel.loadOverview(viewLifecycleOwner)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package com.wbrawner.budget.ui.overview
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.*
|
||||
import com.wbrawner.budget.AsyncState
|
||||
import com.wbrawner.budget.common.budget.Budget
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
|
@ -20,33 +18,34 @@ class OverviewViewModel : ViewModel() {
|
|||
@Inject
|
||||
lateinit var transactionRepo: TransactionRepository
|
||||
|
||||
fun loadOverview() {
|
||||
val budget = budgetRepo.currentBudget
|
||||
if (budget == null) {
|
||||
state.postValue(AsyncState.Error("Invalid Budget ID"))
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
state.postValue(AsyncState.Loading)
|
||||
try {
|
||||
// TODO: Load expected and actual income/expense amounts as well
|
||||
var balance = 0L
|
||||
transactionRepo.findAll(listOf(budget.id!!)).forEach {
|
||||
Log.d("OverviewViewModel", "${it.title} - ${it.amount}")
|
||||
if (it.expense) {
|
||||
balance -= it.amount
|
||||
} else {
|
||||
balance += it.amount
|
||||
}
|
||||
}
|
||||
state.postValue(AsyncState.Success(OverviewState(
|
||||
budget,
|
||||
balance
|
||||
)))
|
||||
} catch (e: Exception) {
|
||||
state.postValue(AsyncState.Error(e))
|
||||
fun loadOverview(lifecycleOwner: LifecycleOwner) {
|
||||
budgetRepo.currentBudget.observe(lifecycleOwner, Observer { budget ->
|
||||
if (budget == null) {
|
||||
state.postValue(AsyncState.Error("Invalid Budget ID"))
|
||||
return@Observer
|
||||
}
|
||||
}
|
||||
viewModelScope.launch {
|
||||
state.postValue(AsyncState.Loading)
|
||||
try {
|
||||
// TODO: Load expected and actual income/expense amounts as well
|
||||
var balance = 0L
|
||||
transactionRepo.findAll(listOf(budget.id!!)).forEach {
|
||||
Log.d("OverviewViewModel", "${it.title} - ${it.amount}")
|
||||
if (it.expense) {
|
||||
balance -= it.amount
|
||||
} else {
|
||||
balance += it.amount
|
||||
}
|
||||
}
|
||||
state.postValue(AsyncState.Success(OverviewState(
|
||||
budget,
|
||||
balance
|
||||
)))
|
||||
} catch (e: Exception) {
|
||||
state.postValue(AsyncState.Error(e))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ class TransactionListViewModel : ViewModel(), AsyncViewModel<List<Transaction>>
|
|||
override val state: MutableLiveData<AsyncState<List<Transaction>>> = MutableLiveData(AsyncState.Loading)
|
||||
|
||||
fun getTransactions(
|
||||
budgetId: Long? = budgetRepository.currentBudget?.id,
|
||||
budgetId: Long? = budgetRepository.currentBudget.value?.id,
|
||||
categoryId: Long? = null,
|
||||
start: Calendar? = null,
|
||||
end: Calendar? = null
|
||||
|
|
|
@ -38,6 +38,7 @@ dependencies {
|
|||
kapt "com.google.dagger:dagger-compiler:$rootProject.ext.dagger"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package com.wbrawner.budget.lib.repository
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.wbrawner.budget.common.budget.Budget
|
||||
import com.wbrawner.budget.common.budget.BudgetRepository
|
||||
import com.wbrawner.budget.lib.network.BudgetApiService
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
const val KEY_DEFAULT_BUDGET = "defaultBudget"
|
||||
|
||||
|
@ -16,18 +19,38 @@ class NetworkBudgetRepository(
|
|||
) : BudgetRepository {
|
||||
private val mutex = Mutex()
|
||||
private val budgets: MutableSet<Budget> = mutableSetOf()
|
||||
private val currentBudgetRef = AtomicReference<Budget>()
|
||||
override var currentBudget: Budget?
|
||||
get() = currentBudgetRef.get()
|
||||
set(value) {
|
||||
currentBudgetRef.set(value)
|
||||
override val currentBudget: LiveData<Budget?> = MutableLiveData()
|
||||
|
||||
init {
|
||||
currentBudget.observeForever { budget ->
|
||||
sharedPreferences.edit().apply {
|
||||
value?.id?.let {
|
||||
budget?.id?.let {
|
||||
putLong(KEY_DEFAULT_BUDGET, it)
|
||||
} ?: remove(KEY_DEFAULT_BUDGET)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
GlobalScope.launch {
|
||||
prefetchData()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun prefetchData() {
|
||||
val budgets = try {
|
||||
findAll()
|
||||
} catch (e: Exception) {
|
||||
emptyList<Budget>()
|
||||
}
|
||||
if (budgets.isEmpty()) return
|
||||
val budgetId = sharedPreferences.getLong(KEY_DEFAULT_BUDGET, budgets.first().id!!)
|
||||
val budget = try {
|
||||
findById(budgetId)
|
||||
} catch (e: Exception) {
|
||||
// For some reason we can't find the default budget id, so fallback to the first budget
|
||||
budgets.first()
|
||||
}
|
||||
(currentBudget as MutableLiveData).postValue(budget)
|
||||
}
|
||||
|
||||
override suspend fun create(newItem: Budget): Budget =
|
||||
apiService.newBudget(NewBudgetRequest(newItem)).apply {
|
||||
|
@ -44,12 +67,16 @@ class NetworkBudgetRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun findById(id: Long): Budget = mutex.withLock {
|
||||
override suspend fun findById(id: Long, setCurrent: Boolean): Budget = (mutex.withLock {
|
||||
budgets.firstOrNull { it.id == id }
|
||||
} ?: apiService.getBudget(id).apply {
|
||||
mutex.withLock {
|
||||
budgets.add(this)
|
||||
}
|
||||
}).apply {
|
||||
if (setCurrent) {
|
||||
(currentBudget as MutableLiveData).postValue(this)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun update(updatedItem: Budget): Budget =
|
||||
|
|
|
@ -5,6 +5,7 @@ buildscript {
|
|||
ext.dagger = '2.23.1'
|
||||
ext.hyperion = '0.9.27'
|
||||
ext.kotlin_version = '1.3.72'
|
||||
ext.lifecycle_version = "2.2.0"
|
||||
ext.moshi = '1.8.0'
|
||||
ext.retrofit = '2.6.0'
|
||||
ext.room_version = "2.2.5"
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
package com.wbrawner.budget.common.budget
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.wbrawner.budget.common.Repository
|
||||
|
||||
interface BudgetRepository : Repository<Budget, Long> {
|
||||
var currentBudget: Budget?
|
||||
val currentBudget: LiveData<Budget?>
|
||||
override suspend fun findById(id: Long): Budget = findById(id, false)
|
||||
suspend fun findById(id: Long, setCurrent: Boolean = false): Budget
|
||||
suspend fun prefetchData()
|
||||
suspend fun getBalance(id: Long): Long
|
||||
}
|
Loading…
Reference in a new issue