diff --git a/mobile/src/main/java/es/hegocre/scorecounter/ScoreViewModel.kt b/mobile/src/main/java/es/hegocre/scorecounter/ScoreViewModel.kt index 09559fd..38e1e19 100644 --- a/mobile/src/main/java/es/hegocre/scorecounter/ScoreViewModel.kt +++ b/mobile/src/main/java/es/hegocre/scorecounter/ScoreViewModel.kt @@ -2,6 +2,7 @@ package es.hegocre.scorecounter import android.app.Application import android.content.Context +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.toMutableStateList import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope @@ -26,6 +27,10 @@ class ScoreViewModel(application: Application) : AndroidViewModel(application) { val scores: List get() = _scores + private val _startingScore = mutableIntStateOf(_preferencesManager.getInt("starting_score", 0)) + val startingScore: Int + get() = _startingScore.intValue + private suspend fun saveScore() { withContext(Dispatchers.IO) { val scoresString = Json.encodeToString(_scores.toList()) @@ -33,8 +38,15 @@ class ScoreViewModel(application: Application) : AndroidViewModel(application) { } } - fun add() { - _scores.add(Score()) + suspend fun setStartingScore(startingScore: Int) { + _startingScore.intValue = startingScore + withContext(Dispatchers.IO) { + _preferencesManager.edit { putInt("starting_score", startingScore) } + } + } + + fun add(startingScore: Int = 0) { + _scores.add(Score(score = startingScore)) viewModelScope.launch { saveScore() } diff --git a/mobile/src/main/java/es/hegocre/scorecounter/ui/components/ScoreList.kt b/mobile/src/main/java/es/hegocre/scorecounter/ui/components/ScoreList.kt index 17995e8..c7274e6 100644 --- a/mobile/src/main/java/es/hegocre/scorecounter/ui/components/ScoreList.kt +++ b/mobile/src/main/java/es/hegocre/scorecounter/ui/components/ScoreList.kt @@ -4,6 +4,8 @@ import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -15,6 +17,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close @@ -36,6 +39,7 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -46,7 +50,9 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog @@ -55,6 +61,9 @@ import es.hegocre.scorecounter.ScoreViewModel import es.hegocre.scorecounter.model.Score import es.hegocre.scorecounter.ui.theme.ScoreCounterTheme import androidx.core.content.edit +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch @SuppressLint("ConfigurationScreenWidthHeight") @Composable @@ -62,6 +71,7 @@ fun ScoreList( scoreViewModel: ScoreViewModel ) { val context = LocalContext.current + val coroutineScope = rememberCoroutineScope() val scores = scoreViewModel.scores val scoresNum by remember { @@ -74,13 +84,41 @@ fun ScoreList( .getBoolean("isFirstLaunch", true) ) } + var showSetStartingScoreDialog by remember { mutableStateOf(false) } ScoreCounterTheme { val configuration = LocalConfiguration.current Scaffold( floatingActionButton = { - FloatingActionButton(onClick = { scoreViewModel.add() }) { + val interactionSource = remember { MutableInteractionSource() } + + val viewConfiguration = LocalViewConfiguration.current + + LaunchedEffect(interactionSource) { + var isLongClick = false + + interactionSource.interactions.collectLatest { interaction -> + when (interaction) { + is PressInteraction.Press -> { + isLongClick = false + delay(viewConfiguration.longPressTimeoutMillis) + isLongClick = true + showSetStartingScoreDialog = true + } + + is PressInteraction.Release -> { + if (isLongClick.not()) { + scoreViewModel.add(scoreViewModel.startingScore) + } + + } + + } + } + } + + FloatingActionButton(onClick = { }, interactionSource = interactionSource) { Icon( imageVector = Icons.Default.Add, contentDescription = "Add Score" @@ -150,8 +188,6 @@ fun ScoreList( } } - - if (showTutorialDialog) { context.getSharedPreferences("preferences", Context.MODE_PRIVATE) .edit { putBoolean("isFirstLaunch", false) } @@ -167,6 +203,20 @@ fun ScoreList( text = { Text(text = stringResource(id = R.string.dialog_tutorial_message)) } ) } + + if (showSetStartingScoreDialog) { + InputStartingScoreDialog( + currentStartingScore = scoreViewModel.startingScore, + onSetStartingScore = { + coroutineScope.launch { + scoreViewModel.setStartingScore(it.toIntOrNull() ?: 0) + } + }, + onDismissRequest = { + showSetStartingScoreDialog = false + } + ) + } } } @@ -299,6 +349,70 @@ fun InputPlayerNameDialog( } } + LaunchedEffect(key1 = Unit) { + requester.requestFocus() + } +} + +@Composable +fun InputStartingScoreDialog( + currentStartingScore: Int, + onSetStartingScore: (String) -> Unit, + onDismissRequest: (() -> Unit)? = null +) { + val requester = FocusRequester() + + val (startingScore, setStartingScore) = remember { mutableStateOf(currentStartingScore.toString()) } + + Dialog( + onDismissRequest = { onDismissRequest?.invoke() }, + ) { + Surface( + color = MaterialTheme.colorScheme.surface, + contentColor = contentColorFor(backgroundColor = MaterialTheme.colorScheme.surface), + shape = MaterialTheme.shapes.extraLarge, + tonalElevation = 6.dp, + ) { + Column(modifier = Modifier.padding(all = 24.dp)) { + Text( + text = stringResource(id = R.string.starting_score), + style = MaterialTheme.typography.headlineSmall, + modifier = Modifier.padding(bottom = 16.dp) + ) + + OutlinedTextField( + modifier = Modifier + .padding(bottom = 16.dp, top = 8.dp) + .focusRequester(requester), + value = startingScore, + onValueChange = { if (it.isBlank() || it.toIntOrNull() != null) setStartingScore(it) }, + keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number), + singleLine = true, + maxLines = 1, + label = { Text(text = stringResource(id = R.string.starting_score)) }, + placeholder = { Text(text = currentStartingScore.toString()) } + ) + + + TextButton( + onClick = { + if (startingScore.isBlank()) { + onSetStartingScore(currentStartingScore.toString()) + } else if (startingScore.toIntOrNull() != null) { + onSetStartingScore(startingScore) + } + onDismissRequest?.invoke() + }, + modifier = Modifier + .align(Alignment.End) + .padding(horizontal = 0.dp) + ) { + Text(text = stringResource(android.R.string.ok)) + } + } + } + } + LaunchedEffect(key1 = Unit) { requester.requestFocus() } diff --git a/mobile/src/main/res/values-ca/strings.xml b/mobile/src/main/res/values-ca/strings.xml index 7dbcc3d..b25dcd5 100755 --- a/mobile/src/main/res/values-ca/strings.xml +++ b/mobile/src/main/res/values-ca/strings.xml @@ -2,7 +2,8 @@ Marcador de puntuació Com utilitzar - Per canviar el marcador, premi a la part superior per incrementar-lo, la part inferior per reduir-lo, o mantingui premut per restablir-lo. + Per canviar el marcador, premi a la part superior per incrementar-lo, la part inferior per reduir-lo, o mantingui premut per restablir-lo. Per canviar la puntuació inicial, mantingui premut el botó +. Nom del jugador Jugador + Puntuació inicial \ No newline at end of file diff --git a/mobile/src/main/res/values-es/strings.xml b/mobile/src/main/res/values-es/strings.xml index 41d864f..47cd40c 100755 --- a/mobile/src/main/res/values-es/strings.xml +++ b/mobile/src/main/res/values-es/strings.xml @@ -2,7 +2,8 @@ Marcador de puntuación Como utililizar - Para cambiar el marcador, pulse en la parte superior para incrementarlo, la parte inferior para reducirlo, o mantenga pulsado para restablecerlo. + Para cambiar el marcador, pulse en la parte superior para incrementarlo, la parte inferior para reducirlo, o mantenga pulsado para restablecerlo. Para cambiar la puntuación inicial, mantenga pulsado el botón +. Nombre del jugador Jugador + Puntuación inicial \ No newline at end of file diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index c897b19..152242b 100755 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -1,7 +1,8 @@ Score Counter How to use - To change a score, press on its upper part to increment it, on its lower part to decrement it, or long press to reset it. + To change a score, press on its upper part to increment it, on its lower part to decrement it, or long press to reset it. Change the starting score by long pressing the + button. Player name Player + Starting score