From c97d98704bcadbb43f208e92f828ef4f03ac6c7a Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Sat, 26 Aug 2023 13:00:30 +0200 Subject: [PATCH 1/2] Update view with victory points setting. Add victory points to settings adapter. --- .../me/zobrist/tichucounter/MainActivity.kt | 12 +-- .../tichucounter/domain/SettingsAdapter.kt | 56 ++++++++--- .../ui/composables/DropDownMenu.kt | 16 ++-- .../tichucounter/ui/counter/CounterView.kt | 1 + .../ui/counter/CounterViewModel.kt | 17 +++- .../tichucounter/ui/settings/SettingsView.kt | 92 +++++++++++-------- .../ui/settings/SettingsViewModel.kt | 7 ++ app/src/main/res/values-de/strings.xml | 4 +- app/src/main/res/values/strings.xml | 3 + 9 files changed, 146 insertions(+), 62 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt index f42db16..1f62ef8 100644 --- a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt +++ b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt @@ -45,7 +45,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import me.zobrist.tichucounter.domain.DrawerItem -import me.zobrist.tichucounter.domain.ISettingsChangeListener +import me.zobrist.tichucounter.domain.ISystemSettingsChangeListener import me.zobrist.tichucounter.domain.KeepScreenOn import me.zobrist.tichucounter.domain.Language import me.zobrist.tichucounter.domain.ReviewService @@ -70,7 +70,7 @@ import me.zobrist.tichucounter.ui.settings.SettingsViewModel import javax.inject.Inject @AndroidEntryPoint -class MainActivity : AppCompatActivity(), ISettingsChangeListener { +class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { @Inject lateinit var settingsAdapter: SettingsAdapter @@ -192,7 +192,6 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener { var topBarState by remember { mutableStateOf(TopBarState()) } var snackbarHostState by remember { mutableStateOf(SnackbarHostState()) } - val scope = rememberCoroutineScope() Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, @@ -211,7 +210,7 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener { startDestination = Route.COUNTER.name, modifier = Modifier.padding(paddings) ) { - composable(Route.COUNTER.name) { + this.composable(Route.COUNTER.name.toString()) { var expanded by remember { mutableStateOf(false) } @@ -231,15 +230,16 @@ class MainActivity : AppCompatActivity(), ISettingsChangeListener { mainViewModel.activeGameHasRounds, { expanded = true } ) { + val newGameTranslated = stringResource(R.string.newGame) DropDownMenu( - mapOf("new" to R.string.newGame), + listOf(newGameTranslated), "", expanded, ) { expanded = false it?.let { when (it) { - "new" -> mainViewModel.newGame() + newGameTranslated -> mainViewModel.newGame() } } } diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt index 6f7e074..52862d0 100644 --- a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt +++ b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt @@ -17,12 +17,19 @@ enum class Language(val value: LocaleListCompat) { enum class KeepScreenOn(val value: Boolean) { ON(true), OFF(false) } -interface ISettingsChangeListener { +typealias VictoryPoints = Int + +interface ISettingsChangeListener +interface ISystemSettingsChangeListener : ISettingsChangeListener { fun onLanguageChanged(language: Language) fun onThemeChanged(theme: Theme) fun onScreenOnChanged(keepOn: KeepScreenOn) } +interface IGameSettingsChangeListener : ISettingsChangeListener { + fun onVictoryPointsChanged(victoryPoints: Int) +} + @Singleton class SettingsAdapter @Inject constructor(@ApplicationContext private val context: Context) { @@ -37,9 +44,9 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex var keepScreenOn: KeepScreenOn private set - var reviewDialogShownDate: Date - get() = Date(sharedPreferences.getLong("reviewDialogShownDate", 0)) - set(value) = updatePreference("reviewDialogShownDate", value.time) + + var victoryPoints: Int + private set init { language = try { @@ -59,14 +66,22 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex } catch (_: java.lang.Exception) { KeepScreenOn.OFF } + + victoryPoints = sharedPreferences.getInt(VictoryPoints::class.simpleName, 1000) } fun registerOnChangeListener(listener: ISettingsChangeListener) { listenerList.add(listener) - listener.onThemeChanged(theme) - listener.onLanguageChanged(language) - listener.onScreenOnChanged(keepScreenOn) + if (listener is ISystemSettingsChangeListener) { + listener.onThemeChanged(theme) + listener.onLanguageChanged(language) + listener.onScreenOnChanged(keepScreenOn) + } + + if (listener is IGameSettingsChangeListener) { + listener.onVictoryPointsChanged(victoryPoints) + } } fun unregisterOnChangeListener(listener: ISettingsChangeListener?) { @@ -93,6 +108,12 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex notifyListeners(setting) } + fun setVictoryPoints(setting: Int) { + this.victoryPoints = setting + updatePreference(VictoryPoints::class.simpleName, setting) + notifyListeners(setting) + } + private fun updatePreference(name: String?, value: String) { val editor = sharedPreferences.edit() editor.putString(name, value) @@ -105,22 +126,33 @@ class SettingsAdapter @Inject constructor(@ApplicationContext private val contex editor.apply() } + private fun updatePreference(name: String?, value: Int) { + val editor = sharedPreferences.edit() + editor.putInt(name, value) + editor.apply() + } + private fun notifyListeners(language: Language) { - listenerList.forEach { + listenerList.filterIsInstance().forEach { it.onLanguageChanged(language) } } private fun notifyListeners(theme: Theme) { - listenerList.forEach { + listenerList.filterIsInstance().forEach { it.onThemeChanged(theme) } } - private fun notifyListeners(keepScreenOn: KeepScreenOn) { - listenerList.forEach { - it.onScreenOnChanged(keepScreenOn) + private fun notifyListeners(victoryPoints: VictoryPoints) { + listenerList.filterIsInstance().forEach { + it.onVictoryPointsChanged(victoryPoints) } } + private fun notifyListeners(keepScreenOn: KeepScreenOn) { + listenerList.filterIsInstance().forEach { + it.onScreenOnChanged(keepScreenOn) + } + } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/composables/DropDownMenu.kt b/app/src/main/java/me/zobrist/tichucounter/ui/composables/DropDownMenu.kt index 12a9700..6042af4 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/composables/DropDownMenu.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/composables/DropDownMenu.kt @@ -7,25 +7,29 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource @Composable -fun DropDownMenu(map: Map, selected: T, expanded: Boolean, onSelected: (T?) -> Unit) { +fun DropDownMenu( + list: Collection, + selected: T, + expanded: Boolean, + onSelected: (T?) -> Unit +) { DropdownMenu( expanded = expanded, onDismissRequest = { onSelected(null) } ) { - map.forEach { + list.forEach { DropdownMenuItem( onClick = { - onSelected(it.key) + onSelected(it) }, trailingIcon = { - if (it.key == selected) { + if (it == selected) { Icon(Icons.Outlined.Check, null) } }, - text = { Text(stringResource(it.value)) }, + text = { Text(it.toString()) }, ) } } diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt index a338d72..e9d7da3 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt @@ -123,6 +123,7 @@ internal class PreviewViewModel : ICounterViewModel { listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") override val teamNameSuggestionsB: List = listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") + override val victoryPoints: Int = 1000 override fun focusLastInput() { } diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index f943a48..4439cf5 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.entity.Round +import me.zobrist.tichucounter.domain.IGameSettingsChangeListener import me.zobrist.tichucounter.domain.Tichu import me.zobrist.tichucounter.domain.digitCount import me.zobrist.tichucounter.domain.getTotalPoints @@ -58,6 +59,7 @@ interface ICounterViewModel : IKeyBoardViewModel { val teamNameB: String val teamNameSuggestionsA: List val teamNameSuggestionsB: List + val victoryPoints: Int fun updateNameA(value: String) fun updateNameB(value: String) @@ -67,7 +69,7 @@ interface ICounterViewModel : IKeyBoardViewModel { class CounterViewModel @Inject constructor( private val gameRepository: GameRepository ) : - ViewModel(), ICounterViewModel { + ViewModel(), ICounterViewModel, IGameSettingsChangeListener { override var roundScoreList by mutableStateOf(emptyList()) private set @@ -114,6 +116,9 @@ class CounterViewModel @Inject constructor( override var teamNameSuggestionsB by mutableStateOf(listOf()) private set + override var victoryPoints by mutableStateOf(0) + private set + override var activeValue: String get() { return if (isBFocused) { @@ -171,6 +176,12 @@ class CounterViewModel @Inject constructor( buildTeamNameSuggestions() + if (totalScoreA >= victoryPoints || totalScoreB >= victoryPoints) { + if (totalScoreA == totalScoreB) { + + } + } + } } } @@ -330,6 +341,10 @@ class CounterViewModel @Inject constructor( } } + override fun onVictoryPointsChanged(victoryPoints: Int) { + this.victoryPoints = victoryPoints + } + private fun deleteLastDigitActive() { if (activeValue != "") { activeValue = activeValue.dropLast(1) diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsView.kt index bcdddf4..83ffacb 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsView.kt @@ -44,6 +44,7 @@ val themeMap = mapOf( Theme.LIGHT to R.string.light ) +val victoryPointsList = listOf(500, 1000, 1500, 2000) @Composable fun SettingsView(viewModel: SettingsViewModel) { @@ -51,9 +52,11 @@ fun SettingsView(viewModel: SettingsViewModel) { viewModel.screenOn.value, viewModel.language, viewModel.theme, + viewModel.victoryPoints, { viewModel.updateScreenOn(it) }, { viewModel.updateLanguage(it) }, - { viewModel.updateTheme(it) }) + { viewModel.updateTheme(it) }, + { viewModel.updateVictoryPoints(it) }) } @Composable @@ -61,11 +64,22 @@ fun SettingsView( valueScreenOn: Boolean = true, valueLanguage: Language = Language.ENGLISH, valueTheme: Theme = Theme.DARK, + valueVictoryPoints: Int = 1000, updateScreenOn: (KeepScreenOn) -> Unit = {}, updateLanguage: (Language) -> Unit = {}, - updateTheme: (Theme) -> Unit = {} + updateTheme: (Theme) -> Unit = {}, + updateVictoryPoints: (Int) -> Unit = {} ) { - Column { + Column( + Modifier + .padding(20.dp) + ) { + + Text( + text = stringResource(R.string.display), + style = MaterialTheme.typography.headlineMedium + ) + BooleanSetting( stringResource(R.string.keep_screen_on), valueScreenOn @@ -82,6 +96,18 @@ fun SettingsView( themeMap, valueTheme, ) { updateTheme(it) } + + Text( + text = stringResource(R.string.game), + style = MaterialTheme.typography.headlineMedium + ) + + + ListSetting( + stringResource(R.string.victory_points), + victoryPointsList, + valueVictoryPoints + ) { updateVictoryPoints(it) } } } @@ -90,7 +116,7 @@ fun BooleanSetting(name: String, value: Boolean, updateValue: (Boolean) -> Unit) Row( Modifier - .padding(20.dp) + .padding(bottom = 15.dp, top = 5.dp) .fillMaxWidth() ) { Column(Modifier.weight(5f)) { @@ -119,21 +145,32 @@ fun BooleanSetting(name: String, value: Boolean, updateValue: (Boolean) -> Unit) @Composable fun StringSetting(name: String, map: Map, selected: T, onSelected: (T) -> Unit) { + val translated = map.map { it.key to stringResource(it.value) }.toMap() + val getValue = map.map { stringResource(it.value) to it.key }.toMap() + + ListSetting( + name, + translated.values, + translated[selected] + ) { getValue[it]?.let { it1 -> onSelected(it1) } } +} + +@Composable +fun ListSetting(name: String, list: Collection, selected: T, onSelected: (T) -> Unit) { + var expanded by remember { mutableStateOf(false) } Row( Modifier .fillMaxWidth() - .padding(20.dp) + .padding(bottom = 15.dp, top = 5.dp) .clickable { expanded = true }) { Column(Modifier.weight(5f)) { Text(name, style = MaterialTheme.typography.bodyLarge, overflow = TextOverflow.Ellipsis) - map[selected]?.let { - Text( - stringResource(it), - style = MaterialTheme.typography.labelLarge - ) - } + Text( + selected.toString(), + style = MaterialTheme.typography.labelLarge + ) } Column(Modifier.weight(1f)) { @@ -142,15 +179,15 @@ fun StringSetting(name: String, map: Map, selected: T, onSelected: ( contentDescription = null, modifier = Modifier.align(End) ) - } - DropDownMenu( - map, - selected, - expanded, - ) { - expanded = false - it?.let { onSelected(it) } + DropDownMenu( + list, + selected, + expanded, + ) { + expanded = false + it?.let { onSelected(it) } + } } } } @@ -167,20 +204,3 @@ fun SettingsViewPreview() { } } } - -@Preview(name = "Light Mode") -@Preview(name = "Dark Mode", uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -@Composable -fun StringSettingPreview() { - - AppTheme { - Surface { - DropDownMenu( - themeMap, - Theme.LIGHT, - true, - ) {} - } - } -} - diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt index b37e1bd..b3657b3 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/settings/SettingsViewModel.kt @@ -24,6 +24,9 @@ class SettingsViewModel @Inject constructor(private val settings: SettingsAdapte var screenOn by mutableStateOf(settings.keepScreenOn) private set + var victoryPoints by mutableStateOf(settings.victoryPoints) + private set + fun updateLanguage(language: Language) { settings.setLanguage(language) this.language = settings.language @@ -39,4 +42,8 @@ class SettingsViewModel @Inject constructor(private val settings: SettingsAdapte screenOn = settings.keepScreenOn } + fun updateVictoryPoints(value: Int) { + settings.setVictoryPoints(value) + victoryPoints = settings.victoryPoints + } } \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d2138cd..82c3530 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -29,5 +29,7 @@ RÜCKGÄNGIG Spiel aktiviert. WEITERSPIELEN - + Anzeige + Spiel + Siegespunkte \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d18461..ff61cf2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,4 +33,7 @@ UNDO Game activated. CONTINUE PLAYING + Display + Game + Victory points \ No newline at end of file From 2599e3320a28f5813a94e3f2fde755e8b7ed248a Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Sat, 26 Aug 2023 15:20:43 +0200 Subject: [PATCH 2/2] Add alerts for winning and draw. --- .../me/zobrist/tichucounter/MainActivity.kt | 3 +- .../tichucounter/domain/ReviewService.kt | 18 +---- .../tichucounter/domain/SettingsAdapter.kt | 1 - .../zobrist/tichucounter/ui/MainViewModel.kt | 2 - .../tichucounter/ui/counter/CounterView.kt | 73 ++++++++++++++++++- .../ui/counter/CounterViewModel.kt | 53 ++++++++++---- app/src/main/res/values-de/strings.xml | 6 +- app/src/main/res/values/strings.xml | 8 +- 8 files changed, 127 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt index 1f62ef8..ca5b423 100644 --- a/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt +++ b/app/src/main/java/me/zobrist/tichucounter/MainActivity.kt @@ -40,7 +40,6 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.google.android.play.core.review.ReviewManager import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -230,7 +229,7 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener { mainViewModel.activeGameHasRounds, { expanded = true } ) { - val newGameTranslated = stringResource(R.string.newGame) + val newGameTranslated = stringResource(R.string.new_game) DropDownMenu( listOf(newGameTranslated), "", diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/ReviewService.kt b/app/src/main/java/me/zobrist/tichucounter/domain/ReviewService.kt index 63a1507..bc2c806 100644 --- a/app/src/main/java/me/zobrist/tichucounter/domain/ReviewService.kt +++ b/app/src/main/java/me/zobrist/tichucounter/domain/ReviewService.kt @@ -2,27 +2,17 @@ package me.zobrist.tichucounter.domain import android.app.Activity import android.content.Context -import androidx.fragment.app.FragmentActivity import androidx.preference.PreferenceManager import com.google.android.play.core.review.ReviewManagerFactory -import com.google.android.play.core.review.testing.FakeReviewManager -import dagger.hilt.android.internal.Contexts import dagger.hilt.android.qualifiers.ActivityContext -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.android.scopes.ActivityScoped -import dagger.hilt.android.scopes.FragmentScoped -import dagger.hilt.android.scopes.ViewScoped -import java.time.Duration -import java.time.Period import java.util.Date import javax.inject.Inject -import javax.inject.Singleton class ReviewService @Inject constructor(@ActivityContext private val appContext: Context) { private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext) - private val THREE_MONTHS : Long = 7776000000 + private val THREE_MONTHS: Long = 7776000000 private var requestCalled: Int get() = sharedPreferences.getInt("requestCalled", 0) @@ -43,10 +33,8 @@ class ReviewService @Inject constructor(@ActivityContext private val appContext: fun request() { requestCalled += 1 - if(requestCalled >= 3) - { - if(nextReviewedDate.time < System.currentTimeMillis()) - { + if (requestCalled >= 3) { + if (nextReviewedDate.time < System.currentTimeMillis()) { requestCalled = 0 nextReviewedDate = Date(System.currentTimeMillis() + THREE_MONTHS) diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt index 52862d0..e011f75 100644 --- a/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt +++ b/app/src/main/java/me/zobrist/tichucounter/domain/SettingsAdapter.kt @@ -4,7 +4,6 @@ import android.content.Context import androidx.core.os.LocaleListCompat import androidx.preference.PreferenceManager import dagger.hilt.android.qualifiers.ApplicationContext -import java.util.Date import javax.inject.Inject import javax.inject.Singleton diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt index 022f07b..db9d2b4 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt @@ -7,10 +7,8 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.scopes.ActivityScoped import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.entity.Round -import me.zobrist.tichucounter.domain.ReviewService import me.zobrist.tichucounter.repository.GameRepository import javax.inject.Inject diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt index e9d7da3..1c6c68d 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt @@ -3,7 +3,13 @@ package me.zobrist.tichucounter.ui.counter import android.content.res.Configuration import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.EmojiEvents +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -12,7 +18,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview +import me.zobrist.tichucounter.R import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.ui.AppTheme @@ -23,6 +31,18 @@ fun Counter(viewModel: ICounterViewModel = PreviewViewModel()) { var orientation by remember { mutableStateOf(Configuration.ORIENTATION_PORTRAIT) } orientation = LocalConfiguration.current.orientation + if (viewModel.showVictoryDialog) { + GameVictoryDialog( + viewModel.totalScoreA, + viewModel.totalScoreB, + viewModel.teamNameA, + viewModel.teamNameB, + { viewModel.victoryDialogExecuted(false) }) + { + viewModel.victoryDialogExecuted(true) + } + } + Surface { if (orientation == Configuration.ORIENTATION_LANDSCAPE) { Landscape(viewModel) @@ -102,6 +122,54 @@ fun CounterViewPreview() { } } +@Preview() +@Composable +fun GameVictoryDialog( + pointsA: Int = 2000, + pointsB: Int = 50, + nameA: String = "nameA", + nameB: String = "nameB", + onDismiss: () -> Unit = {}, + onNewGame: () -> Unit = {}, +) { + + val winner = if (pointsA > pointsB) { + nameA + } else { + nameB + } + + val message = if (pointsA == pointsB) { + stringResource(R.string.draw_message, winner, 100) + } else { + stringResource(R.string.victory_message) + } + + val title = if (pointsA == pointsB) { + stringResource(R.string.draw_title) + } else { + stringResource(R.string.victory_title, winner) + } + + AlertDialog( + onDismissRequest = { onDismiss() }, + dismissButton = { + TextButton({ onDismiss() }) { + Text(stringResource(R.string.continue_play)) + } + }, + confirmButton = { + TextButton({ onNewGame() }) { + Text(stringResource(R.string.new_game)) + } + }, + icon = { Icon(Icons.Outlined.EmojiEvents, null) }, + title = { Text(title) }, + text = { Text(message) } + + ) +} + internal class PreviewViewModel : ICounterViewModel { override var roundScoreList: List = listOf(Round(1, 10, 90), Round(1, 50, 50), Round(1, 70, 30)) @@ -123,7 +191,7 @@ internal class PreviewViewModel : ICounterViewModel { listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") override val teamNameSuggestionsB: List = listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") - override val victoryPoints: Int = 1000 + override var showVictoryDialog: Boolean = false override fun focusLastInput() { } @@ -156,6 +224,9 @@ internal class PreviewViewModel : ICounterViewModel { override fun updateNameB(value: String) { } + override fun victoryDialogExecuted(result: Boolean) { + } + override fun updateFocusStateA(state: Boolean) { } diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index 4439cf5..e56330f 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -11,8 +11,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import me.zobrist.tichucounter.data.entity.Game import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.domain.IGameSettingsChangeListener +import me.zobrist.tichucounter.domain.SettingsAdapter import me.zobrist.tichucounter.domain.Tichu import me.zobrist.tichucounter.domain.digitCount import me.zobrist.tichucounter.domain.getTotalPoints @@ -59,15 +61,17 @@ interface ICounterViewModel : IKeyBoardViewModel { val teamNameB: String val teamNameSuggestionsA: List val teamNameSuggestionsB: List - val victoryPoints: Int + val showVictoryDialog: Boolean fun updateNameA(value: String) fun updateNameB(value: String) + fun victoryDialogExecuted(result: Boolean) } @HiltViewModel class CounterViewModel @Inject constructor( - private val gameRepository: GameRepository + private val gameRepository: GameRepository, + private val settings: SettingsAdapter ) : ViewModel(), ICounterViewModel, IGameSettingsChangeListener { @@ -115,8 +119,7 @@ class CounterViewModel @Inject constructor( override var teamNameSuggestionsB by mutableStateOf(listOf()) private set - - override var victoryPoints by mutableStateOf(0) + override var showVictoryDialog by mutableStateOf(false) private set override var activeValue: String @@ -160,11 +163,14 @@ class CounterViewModel @Inject constructor( private var distinctTeamNames = listOf() + private var victoryDialogShown = false + + private var lastGame: Game? = null + init { viewModelScope.launch { gameRepository.getActiveGameFlow().collect { if (it != null) { - val score = it.getTotalPoints() roundScoreList = it.rounds @@ -176,12 +182,16 @@ class CounterViewModel @Inject constructor( buildTeamNameSuggestions() - if (totalScoreA >= victoryPoints || totalScoreB >= victoryPoints) { - if (totalScoreA == totalScoreB) { - - } + if (it.game.uid != lastGame?.uid) { + victoryDialogShown = false + lastGame = it.game } + if (!victoryDialogShown) { + if (totalScoreA >= settings.victoryPoints || totalScoreB >= settings.victoryPoints) { + showVictoryDialog = true + } + } } } } @@ -193,6 +203,12 @@ class CounterViewModel @Inject constructor( buildTeamNameSuggestions() } } + + settings.registerOnChangeListener(this) + } + + override fun onCleared() { + settings.unregisterOnChangeListener(this) } override fun focusLastInput() { @@ -301,6 +317,17 @@ class CounterViewModel @Inject constructor( } } + override fun victoryDialogExecuted(result: Boolean) { + showVictoryDialog = false + victoryDialogShown = true + + if (result) { + viewModelScope.launch { + gameRepository.newGame() + } + } + } + override fun updateFocusStateA(state: Boolean) { isAFocused = state if (state) { @@ -341,10 +368,6 @@ class CounterViewModel @Inject constructor( } } - override fun onVictoryPointsChanged(victoryPoints: Int) { - this.victoryPoints = victoryPoints - } - private fun deleteLastDigitActive() { if (activeValue != "") { activeValue = activeValue.dropLast(1) @@ -378,4 +401,8 @@ class CounterViewModel @Inject constructor( return filtered.sorted().sortedBy { it.length }.take(10) } + + override fun onVictoryPointsChanged(victoryPoints: Int) { + victoryDialogShown = false + } } \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 82c3530..be1fb4e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -12,7 +12,7 @@ Einstellungen Ein Aus - Neues Spiel + Neues Spiel Verlauf löschen Wirklich den gesamten Verlauf löschen? Diese Aktion kann nicht rückgängig gemacht werden. Abbrechen @@ -32,4 +32,8 @@ Anzeige Spiel Siegespunkte + %1$s hat gewonnen + Sieht aus, als ob ihr ein neues Spiel starten solltet, um das endgültig zu klären. + Unentschieden + Herzliche Gratulation! Wie wäre es mit einer Revanche? \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ff61cf2..f477b30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,7 +15,7 @@ Settings On Off - New Game + New Game Delete history You really want to delete the history? This action can\'t be undone. Cancel @@ -28,7 +28,7 @@ About Contact us Play Store - Continue + Continue game Game deleted. UNDO Game activated. @@ -36,4 +36,8 @@ Display Game Victory points + %1$s won the game + Looks like you should start a new game to settle this for good. + Draw + Congratulations! How about a rematch? \ No newline at end of file