Use single live event to prevent false trigger after rotation. remove hilt (for the moment)
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2022-12-27 08:57:02 +01:00
parent 637a34efd7
commit 4e6193501b
7 changed files with 103 additions and 63 deletions

View File

@@ -22,13 +22,8 @@ import me.zobrist.tichucounter.fragments.KeyboardViewModel
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private var updateOnChange: Boolean = true
@Inject
lateinit var history: History
private var currentRound: Round = Round(null, null) private var currentRound: Round = Round(null, null)
private val keyboardViewModel: KeyboardViewModel by viewModels() private val keyboardViewModel: KeyboardViewModel by viewModels()
@@ -58,7 +53,6 @@ class MainActivity : AppCompatActivity() {
val json = this.getSharedPreferences("Settings", MODE_PRIVATE) val json = this.getSharedPreferences("Settings", MODE_PRIVATE)
.getString("history", "{\"scores\":[]}") .getString("history", "{\"scores\":[]}")
history = Gson().fromJson(json, History::class.java)
binding.contentMain.nameTeamA.setText( binding.contentMain.nameTeamA.setText(
this.getSharedPreferences("Settings", MODE_PRIVATE).getString("nameTeamA", "TeamA") this.getSharedPreferences("Settings", MODE_PRIVATE).getString("nameTeamA", "TeamA")
) )
@@ -66,7 +60,7 @@ class MainActivity : AppCompatActivity() {
this.getSharedPreferences("Settings", MODE_PRIVATE).getString("nameTeamB", "TeamB") this.getSharedPreferences("Settings", MODE_PRIVATE).getString("nameTeamB", "TeamB")
) )
keyboardViewModel.scoreA.observe(this, androidx.lifecycle.Observer { value -> keyboardViewModel.scoreA.observe(this) { value ->
val tichu = Tichu() val tichu = Tichu()
val oldValue = currentRound.scoreA val oldValue = currentRound.scoreA
@@ -83,9 +77,9 @@ class MainActivity : AppCompatActivity() {
keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound)) keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound))
} }
}) }
keyboardViewModel.scoreB.observe(this, androidx.lifecycle.Observer { value -> keyboardViewModel.scoreB.observe(this) { value ->
val tichu = Tichu() val tichu = Tichu()
val oldValue = currentRound.scoreB val oldValue = currentRound.scoreB
@@ -102,28 +96,27 @@ class MainActivity : AppCompatActivity() {
keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound)) keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound))
} }
}) }
keyboardViewModel.submitButtonClicked.observe(this, androidx.lifecycle.Observer { keyboardViewModel.submitButtonClicked.observe(this) {
historyListViewModel.logRound(currentRound) historyListViewModel.logRound(currentRound)
keyboardViewModel.setScoreA(null) keyboardViewModel.setScoreA(null)
keyboardViewModel.setScoreB(null) keyboardViewModel.setScoreB(null)
}) }
historyListViewModel.totalScoreA.observe(this, androidx.lifecycle.Observer { value -> historyListViewModel.totalScoreA.observe(this) { value ->
binding.contentMain.scoreA.text = value.toString() binding.contentMain.scoreA.text = value.toString()
}) }
historyListViewModel.totalScoreB.observe(this, androidx.lifecycle.Observer { value -> historyListViewModel.totalScoreB.observe(this) { value ->
binding.contentMain.scoreB.text = value.toString() binding.contentMain.scoreB.text = value.toString()
}) }
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
val prefs = this.getSharedPreferences("Settings", MODE_PRIVATE).edit() val prefs = this.getSharedPreferences("Settings", MODE_PRIVATE).edit()
prefs.putString("history", Gson().toJson(history))
prefs.putString("nameTeamA", binding.contentMain.nameTeamA.text.toString()) prefs.putString("nameTeamA", binding.contentMain.nameTeamA.text.toString())
prefs.putString("nameTeamB", binding.contentMain.nameTeamB.text.toString()) prefs.putString("nameTeamB", binding.contentMain.nameTeamB.text.toString())
prefs.apply() prefs.apply()
@@ -177,8 +170,7 @@ class MainActivity : AppCompatActivity() {
} }
private fun clearAll() { private fun clearAll() {
historyListViewModel.clearAll()
history.clearAll()
} }

View File

@@ -1,19 +1,12 @@
package me.zobrist.tichucounter.fragments package me.zobrist.tichucounter.fragments
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import dagger.hilt.android.AndroidEntryPoint
import me.zobrist.tichucounter.databinding.FragmentHistoryListBinding import me.zobrist.tichucounter.databinding.FragmentHistoryListBinding
import me.zobrist.tichucounter.databinding.FragmentKeyboardBinding
class HistoryList : Fragment() { class HistoryList : Fragment() {
@@ -46,13 +39,17 @@ class HistoryList : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
viewModel.historyA.observe(viewLifecycleOwner, Observer { text -> viewModel.historyA.observe(viewLifecycleOwner) { text ->
binding.historyA.text = text binding.historyA.text = text
}) }
viewModel.historyB.observe(viewLifecycleOwner, Observer { text -> viewModel.historyB.observe(viewLifecycleOwner) { text ->
binding.historyB.text = text binding.historyB.text = text
}) }
viewModel.scrollDown.observe(viewLifecycleOwner) {
binding.scrollViewHistory.smoothScrollTo(0, binding.scrollViewHistory.height)
}
} }
} }

View File

@@ -1,5 +1,6 @@
package me.zobrist.tichucounter.fragments package me.zobrist.tichucounter.fragments
import SingleLiveEvent
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@@ -13,6 +14,7 @@ class HistoryListViewModel : ViewModel() {
private val _totalScoreB: MutableLiveData<Int> = MutableLiveData<Int>() private val _totalScoreB: MutableLiveData<Int> = MutableLiveData<Int>()
private val _historyA: MutableLiveData<String> = MutableLiveData<String>() private val _historyA: MutableLiveData<String> = MutableLiveData<String>()
private val _historyB: MutableLiveData<String> = MutableLiveData<String>() private val _historyB: MutableLiveData<String> = MutableLiveData<String>()
private val _scrollDown: SingleLiveEvent<Boolean> = SingleLiveEvent()
@@ -36,6 +38,11 @@ class HistoryListViewModel : ViewModel() {
return _historyB return _historyB
} }
val scrollDown: LiveData<Boolean>
get() {
return _scrollDown
}
private fun getScoreA() { private fun getScoreA() {
var tempScore = 0 var tempScore = 0
scores.forEach { scores.forEach {
@@ -68,33 +75,33 @@ class HistoryListViewModel : ViewModel() {
_historyB.value = tempHistory _historyB.value = tempHistory
} }
fun logRound(round: Round) { private fun updateAll() {
scores.add(round.copy())
getHistoryA() getHistoryA()
getHistoryB() getHistoryB()
getScoreA() getScoreA()
getScoreB() getScoreB()
scrollDown()
}
fun logRound(round: Round) {
scores.add(round.copy())
updateAll()
} }
fun revertLastRound() { fun revertLastRound() {
if (scores.isNotEmpty()) { if (scores.isNotEmpty()) {
scores.removeAt(scores.size - 1) scores.removeAt(scores.size - 1)
} }
getHistoryA() updateAll()
getHistoryB() }
getScoreA()
getScoreB() private fun scrollDown() {
_scrollDown.value = true
} }
fun clearAll() { fun clearAll() {
scores.clear() scores.clear()
} updateAll()
}
fun isEmpty(): Boolean {
return scores.isEmpty()
}
} }

View File

@@ -11,7 +11,6 @@ import android.widget.EditText
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import dagger.hilt.android.AndroidEntryPoint
import me.zobrist.tichucounter.databinding.FragmentKeyboardBinding import me.zobrist.tichucounter.databinding.FragmentKeyboardBinding
class Keyboard : Fragment() { class Keyboard : Fragment() {
@@ -56,13 +55,13 @@ class Keyboard : Fragment() {
if (enabled) enableSubmitButton() else disableSubmitButton() if (enabled) enableSubmitButton() else disableSubmitButton()
}) })
viewModel.scoreA.observe(viewLifecycleOwner, Observer { value -> viewModel.scoreA.observe(viewLifecycleOwner) { value ->
updateScore(binding.inputTeamA, value) updateScore(binding.inputTeamA, value)
}) }
viewModel.scoreB.observe(viewLifecycleOwner, Observer { value -> viewModel.scoreB.observe(viewLifecycleOwner) { value ->
updateScore(binding.inputTeamB, value) updateScore(binding.inputTeamB, value)
}) }
setListeners() setListeners()

View File

@@ -1,16 +1,15 @@
package me.zobrist.tichucounter.fragments package me.zobrist.tichucounter.fragments
import SingleLiveEvent
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
class KeyboardViewModel : ViewModel() { class KeyboardViewModel : ViewModel() {
private val _scoreA: MutableLiveData<Int?> = MutableLiveData<Int?>() private val _scoreA: MutableLiveData<Int?> = MutableLiveData()
private val _scoreB: MutableLiveData<Int?> = MutableLiveData<Int?>() private val _scoreB: MutableLiveData<Int?> = MutableLiveData()
private val _enableSubmitButton: MutableLiveData<Boolean> = MutableLiveData<Boolean>() private val _enableSubmitButton: MutableLiveData<Boolean> = MutableLiveData()
private val _submitButtonClicked: MutableLiveData<Boolean> = MutableLiveData<Boolean>() private val _submitButtonClicked: SingleLiveEvent<Boolean> = SingleLiveEvent()
val scoreA: LiveData<Int?> val scoreA: LiveData<Int?>
get() { get() {

View File

@@ -0,0 +1,53 @@
import android.util.Log
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean
/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
*
*
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
*
*
* Note that only one observer is going to be notified of changes.
*/
class SingleLiveEvent<T> : MutableLiveData<T>() {
private val pending = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, Observer { t ->
if (pending.compareAndSet(true, false)) {
observer.onChanged(t)
}
})
}
@MainThread
override fun setValue(t: T?) {
pending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
companion object {
private val TAG = "SingleLiveEvent"
}
}

View File

@@ -1,7 +0,0 @@
package me.zobrist.tichucounter.framework
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class TichuCounterApplication : Application()