Add alerts for winning and draw.
All checks were successful
Build Android / build (push) Successful in 8m35s
Build Android / build (pull_request) Successful in 8m23s

This commit is contained in:
2023-08-26 15:20:43 +02:00
parent c97d98704b
commit 2599e3320a
8 changed files with 127 additions and 37 deletions

View File

@@ -40,7 +40,6 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.play.core.review.ReviewManager
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -230,7 +229,7 @@ class MainActivity : AppCompatActivity(), ISystemSettingsChangeListener {
mainViewModel.activeGameHasRounds, mainViewModel.activeGameHasRounds,
{ expanded = true } { expanded = true }
) { ) {
val newGameTranslated = stringResource(R.string.newGame) val newGameTranslated = stringResource(R.string.new_game)
DropDownMenu( DropDownMenu(
listOf(newGameTranslated), listOf(newGameTranslated),
"", "",

View File

@@ -2,27 +2,17 @@ package me.zobrist.tichucounter.domain
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import androidx.fragment.app.FragmentActivity
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.play.core.review.ReviewManagerFactory 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.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 java.util.Date
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton
class ReviewService @Inject constructor(@ActivityContext private val appContext: Context) { class ReviewService @Inject constructor(@ActivityContext private val appContext: Context) {
private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext) private val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(appContext)
private val THREE_MONTHS : Long = 7776000000 private val THREE_MONTHS: Long = 7776000000
private var requestCalled: Int private var requestCalled: Int
get() = sharedPreferences.getInt("requestCalled", 0) get() = sharedPreferences.getInt("requestCalled", 0)
@@ -43,10 +33,8 @@ class ReviewService @Inject constructor(@ActivityContext private val appContext:
fun request() { fun request() {
requestCalled += 1 requestCalled += 1
if(requestCalled >= 3) if (requestCalled >= 3) {
{ if (nextReviewedDate.time < System.currentTimeMillis()) {
if(nextReviewedDate.time < System.currentTimeMillis())
{
requestCalled = 0 requestCalled = 0
nextReviewedDate = Date(System.currentTimeMillis() + THREE_MONTHS) nextReviewedDate = Date(System.currentTimeMillis() + THREE_MONTHS)

View File

@@ -4,7 +4,6 @@ import android.content.Context
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import java.util.Date
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton

View File

@@ -7,10 +7,8 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.scopes.ActivityScoped
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.ReviewService
import me.zobrist.tichucounter.repository.GameRepository import me.zobrist.tichucounter.repository.GameRepository
import javax.inject.Inject import javax.inject.Inject

View File

@@ -3,7 +3,13 @@ package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row 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.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -12,7 +18,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import me.zobrist.tichucounter.R
import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.ui.AppTheme import me.zobrist.tichucounter.ui.AppTheme
@@ -23,6 +31,18 @@ fun Counter(viewModel: ICounterViewModel = PreviewViewModel()) {
var orientation by remember { mutableStateOf(Configuration.ORIENTATION_PORTRAIT) } var orientation by remember { mutableStateOf(Configuration.ORIENTATION_PORTRAIT) }
orientation = LocalConfiguration.current.orientation orientation = LocalConfiguration.current.orientation
if (viewModel.showVictoryDialog) {
GameVictoryDialog(
viewModel.totalScoreA,
viewModel.totalScoreB,
viewModel.teamNameA,
viewModel.teamNameB,
{ viewModel.victoryDialogExecuted(false) })
{
viewModel.victoryDialogExecuted(true)
}
}
Surface { Surface {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) { if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
Landscape(viewModel) 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 { internal class PreviewViewModel : ICounterViewModel {
override var roundScoreList: List<Round> = override var roundScoreList: List<Round> =
listOf(Round(1, 10, 90), Round(1, 50, 50), Round(1, 70, 30)) 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") listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long")
override val teamNameSuggestionsB: List<String> = override val teamNameSuggestionsB: List<String> =
listOf("TeamA", "asdffd", "TeamB", "really really long Team Name that is way too long") 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() { override fun focusLastInput() {
} }
@@ -156,6 +224,9 @@ internal class PreviewViewModel : ICounterViewModel {
override fun updateNameB(value: String) { override fun updateNameB(value: String) {
} }
override fun victoryDialogExecuted(result: Boolean) {
}
override fun updateFocusStateA(state: Boolean) { override fun updateFocusStateA(state: Boolean) {
} }

View File

@@ -11,8 +11,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.zobrist.tichucounter.data.entity.Game
import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.IGameSettingsChangeListener import me.zobrist.tichucounter.domain.IGameSettingsChangeListener
import me.zobrist.tichucounter.domain.SettingsAdapter
import me.zobrist.tichucounter.domain.Tichu import me.zobrist.tichucounter.domain.Tichu
import me.zobrist.tichucounter.domain.digitCount import me.zobrist.tichucounter.domain.digitCount
import me.zobrist.tichucounter.domain.getTotalPoints import me.zobrist.tichucounter.domain.getTotalPoints
@@ -59,15 +61,17 @@ interface ICounterViewModel : IKeyBoardViewModel {
val teamNameB: String val teamNameB: String
val teamNameSuggestionsA: List<String> val teamNameSuggestionsA: List<String>
val teamNameSuggestionsB: List<String> val teamNameSuggestionsB: List<String>
val victoryPoints: Int val showVictoryDialog: Boolean
fun updateNameA(value: String) fun updateNameA(value: String)
fun updateNameB(value: String) fun updateNameB(value: String)
fun victoryDialogExecuted(result: Boolean)
} }
@HiltViewModel @HiltViewModel
class CounterViewModel @Inject constructor( class CounterViewModel @Inject constructor(
private val gameRepository: GameRepository private val gameRepository: GameRepository,
private val settings: SettingsAdapter
) : ) :
ViewModel(), ICounterViewModel, IGameSettingsChangeListener { ViewModel(), ICounterViewModel, IGameSettingsChangeListener {
@@ -115,8 +119,7 @@ class CounterViewModel @Inject constructor(
override var teamNameSuggestionsB by mutableStateOf(listOf<String>()) override var teamNameSuggestionsB by mutableStateOf(listOf<String>())
private set private set
override var showVictoryDialog by mutableStateOf(false)
override var victoryPoints by mutableStateOf(0)
private set private set
override var activeValue: String override var activeValue: String
@@ -160,11 +163,14 @@ class CounterViewModel @Inject constructor(
private var distinctTeamNames = listOf<String>() private var distinctTeamNames = listOf<String>()
private var victoryDialogShown = false
private var lastGame: Game? = null
init { init {
viewModelScope.launch { viewModelScope.launch {
gameRepository.getActiveGameFlow().collect { gameRepository.getActiveGameFlow().collect {
if (it != null) { if (it != null) {
val score = it.getTotalPoints() val score = it.getTotalPoints()
roundScoreList = it.rounds roundScoreList = it.rounds
@@ -176,12 +182,16 @@ class CounterViewModel @Inject constructor(
buildTeamNameSuggestions() buildTeamNameSuggestions()
if (totalScoreA >= victoryPoints || totalScoreB >= victoryPoints) { if (it.game.uid != lastGame?.uid) {
if (totalScoreA == totalScoreB) { 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() buildTeamNameSuggestions()
} }
} }
settings.registerOnChangeListener(this)
}
override fun onCleared() {
settings.unregisterOnChangeListener(this)
} }
override fun focusLastInput() { 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) { override fun updateFocusStateA(state: Boolean) {
isAFocused = state isAFocused = state
if (state) { if (state) {
@@ -341,10 +368,6 @@ class CounterViewModel @Inject constructor(
} }
} }
override fun onVictoryPointsChanged(victoryPoints: Int) {
this.victoryPoints = victoryPoints
}
private fun deleteLastDigitActive() { private fun deleteLastDigitActive() {
if (activeValue != "") { if (activeValue != "") {
activeValue = activeValue.dropLast(1) activeValue = activeValue.dropLast(1)
@@ -378,4 +401,8 @@ class CounterViewModel @Inject constructor(
return filtered.sorted().sortedBy { it.length }.take(10) return filtered.sorted().sortedBy { it.length }.take(10)
} }
override fun onVictoryPointsChanged(victoryPoints: Int) {
victoryDialogShown = false
}
} }

View File

@@ -12,7 +12,7 @@
<string name="menu_settings">Einstellungen</string> <string name="menu_settings">Einstellungen</string>
<string name="on">Ein</string> <string name="on">Ein</string>
<string name="off">Aus</string> <string name="off">Aus</string>
<string name="newGame">Neues Spiel</string> <string name="new_game">Neues Spiel</string>
<string name="delete_inactive_title">Verlauf löschen</string> <string name="delete_inactive_title">Verlauf löschen</string>
<string name="delete_inactive_text">Wirklich den gesamten Verlauf löschen? Diese Aktion kann nicht rückgängig gemacht werden.</string> <string name="delete_inactive_text">Wirklich den gesamten Verlauf löschen? Diese Aktion kann nicht rückgängig gemacht werden.</string>
<string name="cancel">Abbrechen</string> <string name="cancel">Abbrechen</string>
@@ -32,4 +32,8 @@
<string name="display">Anzeige</string> <string name="display">Anzeige</string>
<string name="game">Spiel</string> <string name="game">Spiel</string>
<string name="victory_points">Siegespunkte</string> <string name="victory_points">Siegespunkte</string>
<string name="victory_title">%1$s hat gewonnen</string>
<string name="draw_message">Sieht aus, als ob ihr ein neues Spiel starten solltet, um das endgültig zu klären.</string>
<string name="draw_title">Unentschieden</string>
<string name="victory_message">Herzliche Gratulation! Wie wäre es mit einer Revanche?</string>
</resources> </resources>

View File

@@ -15,7 +15,7 @@
<string name="menu_settings">Settings</string> <string name="menu_settings">Settings</string>
<string name="on">On</string> <string name="on">On</string>
<string name="off">Off</string> <string name="off">Off</string>
<string name="newGame">New Game</string> <string name="new_game">New Game</string>
<string name="delete_inactive_title">Delete history</string> <string name="delete_inactive_title">Delete history</string>
<string name="delete_inactive_text">You really want to delete the history? This action can\'t be undone.</string> <string name="delete_inactive_text">You really want to delete the history? This action can\'t be undone.</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
@@ -28,7 +28,7 @@
<string name="menu_about">About</string> <string name="menu_about">About</string>
<string name="contact_us">Contact us</string> <string name="contact_us">Contact us</string>
<string name="play_store" translatable="false">Play Store</string> <string name="play_store" translatable="false">Play Store</string>
<string name="continue_play">Continue</string> <string name="continue_play">Continue game</string>
<string name="delete_success">Game deleted.</string> <string name="delete_success">Game deleted.</string>
<string name="undo_question">UNDO</string> <string name="undo_question">UNDO</string>
<string name="activated_success">Game activated.</string> <string name="activated_success">Game activated.</string>
@@ -36,4 +36,8 @@
<string name="display">Display</string> <string name="display">Display</string>
<string name="game">Game</string> <string name="game">Game</string>
<string name="victory_points">Victory points</string> <string name="victory_points">Victory points</string>
<string name="victory_title">%1$s won the game</string>
<string name="draw_message">Looks like you should start a new game to settle this for good.</string>
<string name="draw_title">Draw</string>
<string name="victory_message">Congratulations! How about a rematch?</string>
</resources> </resources>