Use viewmodels with LiveData.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2022-12-24 12:12:19 +01:00
parent 5e0c80be17
commit 0e31908c7a
15 changed files with 555 additions and 407 deletions

View File

@@ -3,21 +3,22 @@ package me.zobrist.tichucounter
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.text.InputType
import android.view.Menu
import android.view.MenuItem
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.ScrollView
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.core.widget.doOnTextChanged
import com.google.gson.Gson
import dagger.hilt.android.AndroidEntryPoint
import me.zobrist.tichucounter.databinding.ActivityMainBinding
import me.zobrist.tichucounter.domain.History
import me.zobrist.tichucounter.domain.Round
import me.zobrist.tichucounter.domain.Tichu
import me.zobrist.tichucounter.domain.getAbsoluteDifference
import me.zobrist.tichucounter.fragments.HistoryListViewModel
import me.zobrist.tichucounter.fragments.KeyboardViewModel
import java.util.*
import javax.inject.Inject
@@ -28,8 +29,13 @@ class MainActivity : AppCompatActivity() {
@Inject
lateinit var history: History
lateinit var currentRound: Round
private var currentRound: Round = Round(null, null)
private val keyboardViewModel: KeyboardViewModel by viewModels()
private val historyListViewModel: HistoryListViewModel by viewModels()
private var ignoreNextUpdate: Boolean = false
private var systemLocale = Locale.getDefault()
@@ -41,19 +47,15 @@ class MainActivity : AppCompatActivity() {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
binding.contentMain.keyboard.inputTeamA.setRawInputType(InputType.TYPE_NULL)
binding.contentMain.keyboard.inputTeamB.setRawInputType(InputType.TYPE_NULL)
binding.contentMain.keyboard.inputTeamA.requestFocus()
disableSubmitButton()
updateTheme(this.getSharedPreferences("Settings", MODE_PRIVATE).getInt("Theme", 2))
keepScreenOn(
this.getSharedPreferences("Settings", MODE_PRIVATE)
.getBoolean("Screen_On", false)
this.getSharedPreferences("Settings", MODE_PRIVATE).getBoolean("Screen_On", false)
)
val json = this.getSharedPreferences("Settings", MODE_PRIVATE)
.getString("history", "{\"scores\":[]}")
history = Gson().fromJson(json, History::class.java)
@@ -63,9 +65,58 @@ class MainActivity : AppCompatActivity() {
binding.contentMain.nameTeamB.setText(
this.getSharedPreferences("Settings", MODE_PRIVATE).getString("nameTeamB", "TeamB")
)
updateView()
this.setListeners()
keyboardViewModel.scoreA.observe(this, androidx.lifecycle.Observer { value ->
val tichu = Tichu()
val oldValue = currentRound.scoreA
currentRound.scoreA = value
if (ignoreNextUpdate) {
ignoreNextUpdate = false
} else {
if (currentRound.scoreA?.let { oldValue?.getAbsoluteDifference(it) } != 100) {
ignoreNextUpdate = true
currentRound.scoreB = tichu.calculateOtherScore(value)
keyboardViewModel.setScoreB(currentRound.scoreB)
}
keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound))
}
})
keyboardViewModel.scoreB.observe(this, androidx.lifecycle.Observer { value ->
val tichu = Tichu()
val oldValue = currentRound.scoreB
currentRound.scoreB = value
if (ignoreNextUpdate) {
ignoreNextUpdate = false
} else {
if (currentRound.scoreB?.let { oldValue?.getAbsoluteDifference(it) } != 100) {
ignoreNextUpdate = true
currentRound.scoreA = tichu.calculateOtherScore(value)
keyboardViewModel.setScoreA(currentRound.scoreA)
}
keyboardViewModel.setSubmitButtonEnable(tichu.isValidRound(currentRound))
}
})
keyboardViewModel.submitButtonClicked.observe(this, androidx.lifecycle.Observer {
historyListViewModel.logRound(currentRound)
keyboardViewModel.setScoreA(null)
keyboardViewModel.setScoreB(null)
})
historyListViewModel.totalScoreA.observe(this, androidx.lifecycle.Observer { value ->
binding.contentMain.scoreA.text = value.toString()
})
historyListViewModel.totalScoreB.observe(this, androidx.lifecycle.Observer { value ->
binding.contentMain.scoreB.text = value.toString()
})
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -79,264 +130,12 @@ class MainActivity : AppCompatActivity() {
}
private fun setListeners() {
binding.contentMain.keyboard.inputTeamA.setOnFocusChangeListener { _, b ->
if (b) {
hideKeyboard()
}
}
binding.contentMain.keyboard.inputTeamB.setOnFocusChangeListener { _, b ->
if (b) {
hideKeyboard()
}
}
binding.contentMain.keyboard.inputTeamA.doOnTextChanged { text, _, _, _ ->
if (binding.contentMain.keyboard.inputTeamA.isFocused) {
if (binding.contentMain.keyboard.inputTeamA.text.isNotEmpty()) {
if (updateOnChange) {
currentRound = try {
Round(text.toString().toInt(), true)
} catch (e: java.lang.Exception) {
Round(1, 1)
}
binding.contentMain.keyboard.inputTeamB.setText(currentRound.scoreB.toString())
} else {
updateOnChange = true
}
} else {
binding.contentMain.keyboard.inputTeamA.text.clear()
binding.contentMain.keyboard.inputTeamB.text.clear()
}
}
if (currentRound.isValidRound() && binding.contentMain.keyboard.inputTeamA.text.isNotEmpty() && binding.contentMain.keyboard.inputTeamB.text.isNotEmpty()) {
enableSubmitButton()
} else {
disableSubmitButton()
}
}
binding.contentMain.keyboard.inputTeamB.doOnTextChanged { text, _, _, _ ->
if (binding.contentMain.keyboard.inputTeamB.isFocused) {
if (binding.contentMain.keyboard.inputTeamB.text.isNotEmpty()) {
if (updateOnChange) {
currentRound = try {
Round(text.toString().toInt(), false)
} catch (e: java.lang.Exception) {
Round(1, 1)
}
binding.contentMain.keyboard.inputTeamA.setText(currentRound.scoreA.toString())
} else {
updateOnChange = true
}
} else {
binding.contentMain.keyboard.inputTeamA.text.clear()
binding.contentMain.keyboard.inputTeamB.text.clear()
}
}
if (currentRound.isValidRound() && binding.contentMain.keyboard.inputTeamA.text.isNotEmpty() && binding.contentMain.keyboard.inputTeamB.text.isNotEmpty()) {
enableSubmitButton()
} else {
disableSubmitButton()
}
}
binding.contentMain.keyboard.buttonAdd100.setOnClickListener {
giveFocusToAIfNone()
if (binding.contentMain.keyboard.inputTeamA.isFocused) {
currentRound.scoreA = try {
binding.contentMain.keyboard.inputTeamA.text.toString().toInt() + 100
} catch (e: Exception) {
currentRound.scoreB = 0
binding.contentMain.keyboard.inputTeamB.setText(currentRound.scoreB.toString())
100
}
updateOnChange = false
binding.contentMain.keyboard.inputTeamA.setText(currentRound.scoreA.toString())
}
if (binding.contentMain.keyboard.inputTeamB.isFocused) {
currentRound.scoreB = try {
binding.contentMain.keyboard.inputTeamB.text.toString().toInt() + 100
} catch (e: Exception) {
currentRound.scoreA = 0
binding.contentMain.keyboard.inputTeamA.setText(currentRound.scoreA.toString())
100
}
updateOnChange = false
binding.contentMain.keyboard.inputTeamB.setText(currentRound.scoreB.toString())
}
}
binding.contentMain.keyboard.buttonSub100.setOnClickListener {
giveFocusToAIfNone()
if (binding.contentMain.keyboard.inputTeamA.isFocused) {
currentRound.scoreA = try {
binding.contentMain.keyboard.inputTeamA.text.toString().toInt() - 100
} catch (e: Exception) {
currentRound.scoreB = 0
binding.contentMain.keyboard.inputTeamB.setText(currentRound.scoreB.toString())
-100
}
updateOnChange = false
binding.contentMain.keyboard.inputTeamA.setText(currentRound.scoreA.toString())
}
if (binding.contentMain.keyboard.inputTeamB.isFocused) {
currentRound.scoreB = try {
binding.contentMain.keyboard.inputTeamB.text.toString().toInt() - 100
} catch (e: Exception) {
currentRound.scoreA = 0
binding.contentMain.keyboard.inputTeamA.setText(currentRound.scoreA.toString())
-100
}
updateOnChange = false
binding.contentMain.keyboard.inputTeamB.setText(currentRound.scoreB.toString())
}
}
binding.contentMain.keyboard.button0.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('0')
}
binding.contentMain.keyboard.button1.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('1')
}
binding.contentMain.keyboard.button2.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('2')
}
binding.contentMain.keyboard.button3.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('3')
}
binding.contentMain.keyboard.button4.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('4')
}
binding.contentMain.keyboard.button5.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('5')
}
binding.contentMain.keyboard.button6.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('6')
}
binding.contentMain.keyboard.button7.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('7')
}
binding.contentMain.keyboard.button8.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('8')
}
binding.contentMain.keyboard.button9.setOnClickListener {
giveFocusToAIfNone()
appendToFocusedInput('9')
}
binding.contentMain.keyboard.buttonInv.setOnClickListener {
val tempInt: Int
giveFocusToAIfNone()
if (binding.contentMain.keyboard.inputTeamA.isFocused) {
if (binding.contentMain.keyboard.inputTeamA.text.toString() == "-") {
binding.contentMain.keyboard.inputTeamA.text.clear()
} else if (binding.contentMain.keyboard.inputTeamA.text.isNotEmpty()) {
tempInt = binding.contentMain.keyboard.inputTeamA.text.toString().toInt() * -1
binding.contentMain.keyboard.inputTeamA.setText(tempInt.toString())
} else {
updateOnChange = false
appendToFocusedInput('-')
currentRound = Round(1, 1)
}
} else if (binding.contentMain.keyboard.inputTeamB.isFocused) {
if (binding.contentMain.keyboard.inputTeamB.text.toString() == "-") {
binding.contentMain.keyboard.inputTeamB.text.clear()
} else if (binding.contentMain.keyboard.inputTeamB.text.isNotEmpty()) {
tempInt = binding.contentMain.keyboard.inputTeamB.text.toString().toInt() * -1
binding.contentMain.keyboard.inputTeamB.setText(tempInt.toString())
} else {
updateOnChange = false
appendToFocusedInput('-')
currentRound = Round(1, 1)
}
}
}
binding.contentMain.keyboard.buttonBack.setOnClickListener {
giveFocusToAIfNone()
if (binding.contentMain.keyboard.inputTeamA.isFocused) {
if (binding.contentMain.keyboard.inputTeamA.text.isNotEmpty()) {
val string = binding.contentMain.keyboard.inputTeamA.text.toString()
binding.contentMain.keyboard.inputTeamA.setText(string.substring(0, string.length - 1))
}
} else if (binding.contentMain.keyboard.inputTeamB.isFocused) {
if (binding.contentMain.keyboard.inputTeamB.text.isNotEmpty()) {
val string = binding.contentMain.keyboard.inputTeamB.text.toString()
binding.contentMain.keyboard.inputTeamB.setText(string.substring(0, string.length - 1))
}
}
}
binding.contentMain.keyboard.submit.setOnClickListener {
giveFocusToAIfNone()
if (binding.contentMain.keyboard.inputTeamA.text.isNotEmpty() && binding.contentMain.keyboard.inputTeamB.text.isNotEmpty()) {
history.logRound(
Round(
binding.contentMain.keyboard.inputTeamA.text.toString().toInt(),
binding.contentMain.keyboard.inputTeamB.text.toString().toInt()
)
)
updateView()
binding.contentMain.keyboard.inputTeamA.text.clear()
binding.contentMain.keyboard.inputTeamB.text.clear()
disableSubmitButton()
binding.contentMain.scrollHistory.scrollViewHistory.fullScroll(ScrollView.FOCUS_DOWN)
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
menu.findItem(R.id.action_screenOn).isChecked =
this.getSharedPreferences("Settings", MODE_PRIVATE)
.getBoolean("Screen_On", false)
this.getSharedPreferences("Settings", MODE_PRIVATE).getBoolean("Screen_On", false)
return true
}
@@ -345,14 +144,11 @@ class MainActivity : AppCompatActivity() {
return when (item.itemId) {
R.id.action_clear -> {
val builder = AlertDialog.Builder(this)
builder.setMessage(getString(R.string.confirmClear))
.setTitle(R.string.clear)
.setCancelable(false)
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
builder.setMessage(getString(R.string.confirmClear)).setTitle(R.string.clear)
.setCancelable(false).setPositiveButton(getString(R.string.yes)) { dialog, _ ->
dialog.dismiss()
clearAll()
}
.setNegativeButton(getString(R.string.no)) { dialog, _ ->
}.setNegativeButton(getString(R.string.no)) { dialog, _ ->
dialog.cancel()
}
@@ -360,7 +156,7 @@ class MainActivity : AppCompatActivity() {
true
}
R.id.action_undo -> {
undoLastRound()
historyListViewModel.revertLastRound()
true
}
R.id.action_theme -> {
@@ -380,59 +176,11 @@ class MainActivity : AppCompatActivity() {
}
}
private fun hideKeyboard() {
val imm: InputMethodManager =
getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)
}
private fun giveFocusToAIfNone() {
if (!binding.contentMain.keyboard.inputTeamA.isFocused && !binding.contentMain.keyboard.inputTeamB.isFocused) {
binding.contentMain.keyboard.inputTeamA.requestFocus()
}
}
private fun undoLastRound() {
history.revertLastRound()
updateView()
}
private fun updateView() {
binding.contentMain.scoreA.text = history.getScoreA().toString()
binding.contentMain.scoreB.text = history.getScoreB().toString()
binding.contentMain.scrollHistory.historyA?.text = history.getHistoryA()
binding.contentMain.scrollHistory.historyB?.text = history.getHistoryB()
}
private fun clearAll() {
binding.contentMain.scrollHistory.historyA?.text = ""
binding.contentMain.scrollHistory.historyB?.text = ""
binding.contentMain.keyboard.inputTeamA.text.clear()
binding.contentMain.keyboard.inputTeamB.text.clear()
binding.contentMain.scoreA.text = "0"
binding.contentMain.scoreB.text = "0"
history.clearAll()
}
private fun appendToFocusedInput(toAppend: Char) {
if (binding.contentMain.keyboard.inputTeamA.isFocused) {
binding.contentMain.keyboard.inputTeamA.text.append(toAppend)
} else if (binding.contentMain.keyboard.inputTeamB.isFocused) {
binding.contentMain.keyboard.inputTeamB.text.append(toAppend)
}
}
private fun enableSubmitButton() {
binding.contentMain.keyboard.submit?.imageAlpha = 255 // 0 being transparent and 255 being opaque
binding.contentMain.keyboard.submit?.isEnabled = true
}
private fun disableSubmitButton() {
binding.contentMain.keyboard.submit?.imageAlpha = 60 // 0 being transparent and 255 being opaque
binding.contentMain.keyboard.submit?.isEnabled = false
}
private fun chooseThemeDialog() {
@@ -444,8 +192,7 @@ class MainActivity : AppCompatActivity() {
getString(R.string.android_default_text)
)
val checkedItem =
this.getSharedPreferences("Settings", MODE_PRIVATE).getInt("Theme", 2)
val checkedItem = this.getSharedPreferences("Settings", MODE_PRIVATE).getInt("Theme", 2)
val prefs = this.getSharedPreferences("Settings", MODE_PRIVATE).edit()
@@ -470,8 +217,7 @@ class MainActivity : AppCompatActivity() {
builder.setTitle(getString(R.string.choose_language_text))
val languagesMap = mapOf(
getString(R.string.english) to "en",
getString(R.string.german) to "de"
getString(R.string.english) to "en", getString(R.string.german) to "de"
)
val languagesDisplayKeys = languagesMap.keys.toTypedArray()

View File

@@ -11,7 +11,7 @@ class History {
fun getScoreA(): Int {
var tempScore = 0
scores.forEach {
tempScore += it.scoreA
tempScore += it.scoreA!!
}
return tempScore
}
@@ -19,7 +19,7 @@ class History {
fun getScoreB(): Int {
var tempScore = 0
scores.forEach {
tempScore += it.scoreB
tempScore += it.scoreB!!
}
return tempScore
}

View File

@@ -1,9 +1,16 @@
package me.zobrist.tichucounter.domain
import kotlin.math.abs
fun Int.isMultipleOf5(): Boolean {
return (this % 5) == 0
}
fun Int.isMultipleOf100(): Boolean {
return (this % 100) == 0
}
fun Int.getAbsoluteDifference(other: Int): Int {
return abs(this - other)
}

View File

@@ -2,36 +2,5 @@ package me.zobrist.tichucounter.domain
import java.io.Serializable
class Round() : Serializable {
var scoreA: Int = 0
var scoreB: Int = 0
constructor(score: Int, isScoreA: Boolean) : this() {
if (isScoreA) {
scoreA = score
scoreB = calculateOtherScore(scoreA)
} else {
scoreB = score
scoreA = calculateOtherScore(scoreB)
}
}
constructor(scoreA: Int, scoreB: Int) : this() {
this.scoreA = scoreA
this.scoreB = scoreB
}
fun calculateOtherScore(score: Int): Int {
if (score.isMultipleOf100() && score != 0) {
return 0
}
if (score in 101..125) {
return 0 - (score % 100)
}
return 100 - (score % 100)
}
fun isValidRound(): Boolean {
return (scoreA.isMultipleOf5()) && scoreB.isMultipleOf5() && (scoreA + scoreB).isMultipleOf100()
}
data class Round(var scoreA: Int?, var scoreB: Int?) : Serializable {
}

View File

@@ -0,0 +1,27 @@
package me.zobrist.tichucounter.domain
class Tichu {
fun calculateOtherScore(score: Int?): Int? {
if (score == null) {
return null
}
if (!score.isMultipleOf5()) {
return null
}
if (score.isMultipleOf100() && score != 0) {
return 0
}
if (score in 101..125) {
return 0 - (score % 100)
}
return 100 - (score % 100)
}
fun isValidRound(round: Round): Boolean {
if (round.scoreA == null || round.scoreB == null) {
return false
}
return (round.scoreA!!.isMultipleOf5()) && round.scoreB!!.isMultipleOf5() && (round.scoreA!! + round.scoreB!!).isMultipleOf100()
}
}

View File

@@ -1,32 +1,58 @@
package me.zobrist.tichucounter.fragments
import androidx.lifecycle.ViewModelProvider
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import me.zobrist.tichucounter.R
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.fragment.app.Fragment
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.FragmentKeyboardBinding
class HistoryList : Fragment() {
private var _binding: FragmentHistoryListBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
companion object {
fun newInstance() = HistoryList()
}
private lateinit var viewModel: HistoryListViewModel
private val viewModel: HistoryListViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_history_list, container, false)
_binding = FragmentHistoryListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(HistoryListViewModel::class.java)
// TODO: Use the ViewModel
viewModel.historyA.observe(viewLifecycleOwner, Observer { text ->
binding.historyA.text = text
})
viewModel.historyB.observe(viewLifecycleOwner, Observer { text ->
binding.historyB.text = text
})
}
}

View File

@@ -1,7 +1,100 @@
package me.zobrist.tichucounter.fragments
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import me.zobrist.tichucounter.domain.Round
class HistoryListViewModel : ViewModel() {
// TODO: Implement the ViewModel
}
private var scores = ArrayList<Round>()
private val _totalScoreA: MutableLiveData<Int> = MutableLiveData<Int>()
private val _totalScoreB: MutableLiveData<Int> = MutableLiveData<Int>()
private val _historyA: MutableLiveData<String> = MutableLiveData<String>()
private val _historyB: MutableLiveData<String> = MutableLiveData<String>()
val totalScoreA: LiveData<Int>
get() {
return _totalScoreA
}
val totalScoreB: LiveData<Int>
get() {
return _totalScoreB
}
val historyA: LiveData<String>
get() {
return _historyA
}
val historyB: LiveData<String>
get() {
return _historyB
}
private fun getScoreA() {
var tempScore = 0
scores.forEach {
tempScore += it.scoreA!!
}
_totalScoreA.value = tempScore
}
private fun getScoreB() {
var tempScore = 0
scores.forEach {
tempScore += it.scoreB!!
}
_totalScoreB.value = tempScore
}
private fun getHistoryA() {
var tempHistory = String()
scores.forEach {
tempHistory += it.scoreA.toString() + "\n"
}
_historyA.value = tempHistory
}
private fun getHistoryB() {
var tempHistory = String()
scores.forEach {
tempHistory += it.scoreB.toString() + "\n"
}
_historyB.value = tempHistory
}
fun logRound(round: Round) {
scores.add(round.copy())
getHistoryA()
getHistoryB()
getScoreA()
getScoreB()
}
fun revertLastRound() {
if (scores.isNotEmpty()) {
scores.removeAt(scores.size - 1)
}
getHistoryA()
getHistoryB()
getScoreA()
getScoreB()
}
fun clearAll() {
scores.clear()
}
fun isEmpty(): Boolean {
return scores.isEmpty()
}
}

View File

@@ -1,35 +1,265 @@
package me.zobrist.tichucounter.fragments
import androidx.lifecycle.ViewModelProvider
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ScrollView
import androidx.core.widget.doOnTextChanged
import me.zobrist.tichucounter.R
import me.zobrist.tichucounter.domain.Round
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import dagger.hilt.android.AndroidEntryPoint
import me.zobrist.tichucounter.databinding.FragmentKeyboardBinding
class Keyboard : Fragment() {
private var _binding: FragmentKeyboardBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private var unhandledNegation: Boolean = false
companion object {
fun newInstance() = Keyboard()
}
private lateinit var viewModel: KeyboardViewModel
private val viewModel: KeyboardViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_keyboard, container, false)
_binding = FragmentKeyboardBinding.inflate(inflater, container, false)
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(KeyboardViewModel::class.java)
// TODO: Use the ViewModel
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
binding.inputTeamA.setRawInputType(InputType.TYPE_NULL)
binding.inputTeamB.setRawInputType(InputType.TYPE_NULL)
binding.inputTeamA.requestFocus()
disableSubmitButton()
viewModel.enableSubmitButton.observe(viewLifecycleOwner, Observer { enabled ->
if (enabled) enableSubmitButton() else disableSubmitButton()
})
viewModel.scoreA.observe(viewLifecycleOwner, Observer { value ->
updateScore(binding.inputTeamA, value)
})
viewModel.scoreB.observe(viewLifecycleOwner, Observer { value ->
updateScore(binding.inputTeamB, value)
})
setListeners()
}
private fun setListeners() {
binding.inputTeamA.setOnFocusChangeListener { _, b ->
if (b) {
hideKeyboard()
}
}
binding.inputTeamB.setOnFocusChangeListener { _, b ->
if (b) {
hideKeyboard()
}
}
binding.buttonAdd100.setOnClickListener {
var value = getActiveValue()
value = if (value != null) {
value + 100
} else {
100
}
setActiveValue(value)
}
binding.buttonSub100.setOnClickListener {
var value = getActiveValue()
value = if (value != null) {
value!! - 100
} else {
-100
}
setActiveValue(value)
}
binding.button0.setOnClickListener {
appendToFocusedScore(0)
}
binding.button1.setOnClickListener {
appendToFocusedScore(1)
}
binding.button2.setOnClickListener {
appendToFocusedScore(2)
}
binding.button3.setOnClickListener {
appendToFocusedScore(3)
}
binding.button4.setOnClickListener {
appendToFocusedScore(4)
}
binding.button5.setOnClickListener {
appendToFocusedScore(5)
}
binding.button6.setOnClickListener {
appendToFocusedScore(6)
}
binding.button7.setOnClickListener {
appendToFocusedScore(7)
}
binding.button8.setOnClickListener {
appendToFocusedScore(8)
}
binding.button9.setOnClickListener {
appendToFocusedScore(9)
}
binding.buttonInv.setOnClickListener {
var value = getActiveValue()
if (value == null) {
if (getActiveText() == "-") {
setActiveText("")
unhandledNegation = false
} else {
setActiveText("-")
unhandledNegation = true
}
} else {
value = value?.times(-1)
setActiveValue(value)
}
}
binding.buttonBack.setOnClickListener {
var value = getActiveValue()
if (value != null) {
value = try {
value.toString().dropLast(1).toInt()
} catch (e: Exception) {
null
}
}
setActiveValue(value)
}
binding.submit.setOnClickListener {
viewModel.submitButtonClicked()
}
}
private fun hideKeyboard() {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.hideSoftInputFromWindow(view?.windowToken, 0)
}
private fun giveFocusToAIfNone() {
if (!binding.inputTeamA.isFocused && !binding.inputTeamB.isFocused) {
binding.inputTeamA.requestFocus()
}
}
private fun updateScore(field: EditText, score: Int?) {
if (score == null) {
field.setText("")
return
}
var text = try {
score.toString()
} catch (e: Exception) {
""
}
field.setText(text)
}
private fun appendToFocusedScore(toAppend: Int) {
var value = getActiveValue()
if (value != null) {
value = value.times(10)
value = value.plus(toAppend)
} else {
value = toAppend
if (unhandledNegation) {
value = value.times(-1)
unhandledNegation = false
}
}
setActiveValue(value)
}
private fun getActiveValue(): Int? {
giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) {
return viewModel.scoreA.value
}
return viewModel.scoreB.value
}
private fun setActiveValue(value: Int?) {
giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) {
viewModel.setScoreA(value)
} else {
viewModel.setScoreB(value)
}
}
private fun getActiveText(): String {
giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) {
return binding.inputTeamA.text.toString()
}
return binding.inputTeamB.text.toString()
}
private fun setActiveText(value: String) {
giveFocusToAIfNone()
if (binding.inputTeamA.isFocused) {
binding.inputTeamA.setText(value)
} else {
binding.inputTeamB.setText(value)
}
}
private fun enableSubmitButton() {
binding.submit.imageAlpha = 255 // 0 being transparent and 255 being opaque
binding.submit.isEnabled = true
}
private fun disableSubmitButton() {
binding.submit.imageAlpha = 60 // 0 being transparent and 255 being opaque
binding.submit.isEnabled = false
}
}

View File

@@ -1,7 +1,50 @@
package me.zobrist.tichucounter.fragments
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
class KeyboardViewModel : ViewModel() {
// TODO: Implement the ViewModel
class KeyboardViewModel: ViewModel() {
private val _scoreA: MutableLiveData<Int?> = MutableLiveData<Int?>()
private val _scoreB: MutableLiveData<Int?> = MutableLiveData<Int?>()
private val _enableSubmitButton: MutableLiveData<Boolean> = MutableLiveData<Boolean>()
private val _submitButtonClicked: MutableLiveData<Boolean> = MutableLiveData<Boolean>()
val scoreA: LiveData<Int?>
get() {
return _scoreA
}
val scoreB: LiveData<Int?>
get() {
return _scoreB
}
val enableSubmitButton: LiveData<Boolean>
get() {
return _enableSubmitButton
}
val submitButtonClicked: LiveData<Boolean>
get() {
return _submitButtonClicked
}
fun setScoreA(score: Int?) {
_scoreA.value = score
}
fun setScoreB(score: Int?) {
_scoreB.value = score
}
fun setSubmitButtonEnable(enabled: Boolean) {
_enableSubmitButton.value = enabled
}
fun submitButtonClicked() {
_submitButtonClicked.value = true
}
}

View File

@@ -4,6 +4,4 @@ import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class TichuCounterApplication : Application() {
}
class TichuCounterApplication : Application()