parametrage delai réponse

This commit is contained in:
ImAliant 2024-01-06 17:25:08 +01:00
parent cb9a063e68
commit 920bd24742
6 changed files with 179 additions and 65 deletions

View file

@ -24,5 +24,7 @@ const val STATS_TOTAL_GOOD = "total_good"
/** DataStore clef lié au nombre de questions mal répondu */ /** DataStore clef lié au nombre de questions mal répondu */
const val STATS_TOTAL_BAD = "total_bad" const val STATS_TOTAL_BAD = "total_bad"
const val DELAY = "delay"
const val HOUR = "hour" const val HOUR = "hour"
const val MINUTE = "minute" const val MINUTE = "minute"

View file

@ -16,6 +16,7 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -48,6 +49,7 @@ fun PlayScreen(
val correction by model.evaluatedAnswer val correction by model.evaluatedAnswer
var giveup by model.showAnswer var giveup by model.showAnswer
val gameEnded by model.end val gameEnded by model.end
val delay by model.delay.collectAsState(initial = 3000)
val cpt by model.compteurSb val cpt by model.compteurSb
if (correction != null) { if (correction != null) {
@ -72,7 +74,7 @@ fun PlayScreen(
} }
// Update timer if needed // Update timer if needed
if (!model.isDelayElapsed() && question != null) { if (!model.isDelayElapsed(delay) && question != null) {
model.updateTime(System.currentTimeMillis()) model.updateTime(System.currentTimeMillis())
} }
@ -109,7 +111,7 @@ fun PlayScreen(
} }
Button( Button(
enabled = model.isDelayElapsed(), enabled = model.isDelayElapsed(delay),
onClick = { giveup = true }) { onClick = { giveup = true }) {
Text(text = context.getString(R.string.see_answer)) Text(text = context.getString(R.string.see_answer))
} }

View file

@ -1,7 +1,9 @@
package fr.uparis.diamantkennel.memorisationapplication package fr.uparis.diamantkennel.memorisationapplication
import android.content.Context
import android.os.Build import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -13,12 +15,14 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TimePicker import androidx.compose.material3.TimePicker
import androidx.compose.material3.TimePickerLayoutType import androidx.compose.material3.TimePickerLayoutType
@ -27,12 +31,15 @@ import androidx.compose.material3.rememberTimePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -51,15 +58,17 @@ fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel(
var deletionDBRequest by model.deletionDB var deletionDBRequest by model.deletionDB
var cleanStatRequest by model.deletionStat var cleanStatRequest by model.deletionStat
var choiceTimeNotifRequest by model.notif var choiceTimeNotifRequest by model.notif
var choiceDelayRequest by model.delayRequest
var permissionNotif by model.gavePermissionNow var permissionNotif by model.gavePermissionNow
val prefConfig = runBlocking { model.prefConfig.first() } val prefConfigTime = runBlocking { model.prefConfigTime.first() }
val state = rememberTimePickerState( val stateTime = rememberTimePickerState(
initialHour = prefConfig.hour, initialHour = prefConfigTime.hour,
initialMinute = prefConfig.minute, initialMinute = prefConfigTime.minute,
is24Hour = true is24Hour = true
) )
model.checkPermission(context) model.checkPermission(context)
val permissionLauncher = rememberLauncherForActivityResult( val permissionLauncher = rememberLauncherForActivityResult(
@ -80,9 +89,16 @@ fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel(
if (choiceTimeNotifRequest) { if (choiceTimeNotifRequest) {
ChoiceTimeNotifDialog( ChoiceTimeNotifDialog(
{ model.choiceTimeNotif(state, context) }, { model.choiceTimeNotif(stateTime, context) },
{ choiceTimeNotifRequest = false }, { choiceTimeNotifRequest = false },
state stateTime
)
}
if (choiceDelayRequest) {
ChoiceDelayDialog(
{ model.choiceDelay(it) },
{ choiceDelayRequest = false },
) )
} }
@ -92,54 +108,89 @@ fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel(
) { ) {
Stats(model) Stats(model)
Spacer(modifier = Modifier.padding(top = 10.dp)) AddSpacedDivider()
Divider(color = Color.Gray)
Spacer(modifier = Modifier.padding(top = 10.dp))
Text(text = context.getString(R.string.notification), fontSize = 30.sp) NotificationSettings(context, model, permissionNotif, permissionLauncher) {
Row( choiceTimeNotifRequest = true
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(
enabled = !permissionNotif,
onClick = {
model.requestNotificationPermission(permissionLauncher)
}
) {
Text(text = context.getString(R.string.permission_button))
}
Button(
enabled = permissionNotif,
onClick = {
choiceTimeNotifRequest = true
}
) {
Text(text = context.getString(R.string.time_notif_button))
}
} }
Spacer(modifier = Modifier.padding(top = 10.dp)) AddSpacedDivider()
Divider(color = Color.Gray)
Spacer(modifier = Modifier.padding(top = 10.dp))
Text(text = context.getString(R.string.gestion), fontSize = 30.sp) GestionSettings(context, { deletionDBRequest = true }, { cleanStatRequest = true })
Row(
modifier = Modifier.fillMaxWidth(), AddSpacedDivider()
horizontalArrangement = Arrangement.SpaceEvenly
) { GameSettings(context) { choiceDelayRequest = true }
Button( }
onClick = { deletionDBRequest = true }, }
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
) { @RequiresApi(Build.VERSION_CODES.TIRAMISU)
Text(text = context.getString(R.string.main_button_deletebase)) @Composable
fun NotificationSettings(
context: Context,
model: SettingsViewModel,
permissionNotif: Boolean,
permissionLauncher: ActivityResultLauncher<String>,
onTimeNotifButtonClick: () -> Unit,
) {
Text(text = context.getString(R.string.notification), fontSize = 30.sp)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(
enabled = !permissionNotif,
onClick = {
model.requestNotificationPermission(permissionLauncher)
} }
) {
Text(text = context.getString(R.string.permission_button))
}
Button(
enabled = permissionNotif,
onClick = onTimeNotifButtonClick
) {
Text(text = context.getString(R.string.time_notif_button))
}
}
}
Button( @Composable
onClick = { cleanStatRequest = true }, fun GestionSettings(context: Context, deletionDBRequest: () -> Unit, cleanStatRequest: () -> Unit) {
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red)) Text(text = context.getString(R.string.gestion), fontSize = 30.sp)
) { Row(
Text(text = context.getString(R.string.clean_stat_button)) modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Button(
onClick = deletionDBRequest,
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
) {
Text(text = context.getString(R.string.main_button_deletebase))
}
Button(
onClick = cleanStatRequest,
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
) {
Text(text = context.getString(R.string.clean_stat_button))
}
}
}
@Composable
fun GameSettings(context: Context, onDelayButtonClick: () -> Unit) {
Text(text = context.getString(R.string.game), fontSize = 30.sp)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Column (
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
){
Button(onClick = onDelayButtonClick) {
Text(text = context.getString(R.string.choice_delay_button))
} }
} }
} }
@ -223,3 +274,38 @@ fun ChoiceTimeNotifDialog(confirm: () -> Unit, dismiss: () -> Unit, state: TimeP
} }
} }
} }
@Composable
fun ChoiceDelayDialog(confirm: (Int) -> Unit, dismiss: () -> Unit) {
var value by remember { mutableStateOf("") }
AlertDialog(onDismissRequest = dismiss,
title = { Text(text = LocalContext.current.getString(R.string.choice_delay)) },
text = {
OutlinedTextField(
value = value,
onValueChange = {
value = if (it.toIntOrNull() != null) { it } else ""
},
label = { Text(text = LocalContext.current.getString(R.string.enter_integer)) },
keyboardOptions = KeyboardOptions.Default.copy(
keyboardType = KeyboardType.Number
),
singleLine = true
)
},
confirmButton = {
Button(onClick = { if (value.isNotBlank()) { confirm(value.toInt()) } }) {
Text(text = LocalContext.current.getString(R.string.confirm))
}
}
)
}
@Composable
fun AddSpacedDivider() {
Spacer(modifier = Modifier.padding(top = 20.dp))
Divider(color = Color.Gray)
Spacer(modifier = Modifier.padding(top = 20.dp))
}

View file

@ -11,21 +11,26 @@ import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_BAD
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_DONE import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_DONE
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_GOOD import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_GOOD
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_TRIED import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_TRIED
import fr.uparis.diamantkennel.memorisationapplication.DELAY
import fr.uparis.diamantkennel.memorisationapplication.data.Question import fr.uparis.diamantkennel.memorisationapplication.data.Question
import fr.uparis.diamantkennel.memorisationapplication.dataStore import fr.uparis.diamantkennel.memorisationapplication.dataStore
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class PlayViewModel(application: Application) : AndroidViewModel(application) { class PlayViewModel(application: Application) : AndroidViewModel(application) {
private val dao = (application as MemoApplication).database.memoDao() private val dao = (application as MemoApplication).database.memoDao()
private var questions = mutableStateOf<List<Question>>(listOf()) private var questions = mutableStateOf<List<Question>>(listOf())
private val stats = application.dataStore private val datastore = application.dataStore
private val statsKeyTotal = intPreferencesKey(STATS_TOTAL_TRIED) private val statsKeyTotal = intPreferencesKey(STATS_TOTAL_TRIED)
private val statsKeyTotalDone = intPreferencesKey(STATS_TOTAL_DONE) private val statsKeyTotalDone = intPreferencesKey(STATS_TOTAL_DONE)
private val statsKeyTotalGood = intPreferencesKey(STATS_TOTAL_GOOD) private val statsKeyTotalGood = intPreferencesKey(STATS_TOTAL_GOOD)
private val statsKeyTotalBad = intPreferencesKey(STATS_TOTAL_BAD) private val statsKeyTotalBad = intPreferencesKey(STATS_TOTAL_BAD)
private val delayKey= intPreferencesKey(DELAY)
val delay = datastore.data.map { it[delayKey] ?: 3000 }
var currentQuestion = mutableStateOf<Question?>(null) var currentQuestion = mutableStateOf<Question?>(null)
private var index = mutableStateOf(0) private var index = mutableStateOf(0)
var proposedAnswer = mutableStateOf("") var proposedAnswer = mutableStateOf("")
@ -42,7 +47,7 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
dao.loadQuestions(setId).collect { questionList -> dao.loadQuestions(setId).collect { questionList ->
questions.value = questionList.shuffled() questions.value = questionList.shuffled()
if (questions.value.isNotEmpty()) { if (questions.value.isNotEmpty()) {
stats.edit { it[statsKeyTotal] = (it[statsKeyTotal] ?: 0) + 1 } datastore.edit { it[statsKeyTotal] = (it[statsKeyTotal] ?: 0) + 1 }
} }
updateQuestion() updateQuestion()
} }
@ -58,7 +63,7 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
/* Fin des questions */ /* Fin des questions */
end.value = true end.value = true
viewModelScope.launch { viewModelScope.launch {
stats.edit { datastore.edit {
it[statsKeyTotalDone] = (it[statsKeyTotalDone] ?: 0) + 1 it[statsKeyTotalDone] = (it[statsKeyTotalDone] ?: 0) + 1
} }
} }
@ -114,7 +119,7 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
when (evaluatedAnswer.value!!) { when (evaluatedAnswer.value!!) {
AnswerType.GOOD -> { AnswerType.GOOD -> {
viewModelScope.launch { viewModelScope.launch {
stats.edit { datastore.edit {
it[statsKeyTotalGood] = (it[statsKeyTotalGood] ?: 0) + 1 it[statsKeyTotalGood] = (it[statsKeyTotalGood] ?: 0) + 1
} }
} }
@ -123,7 +128,7 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
AnswerType.BAD -> { AnswerType.BAD -> {
viewModelScope.launch { viewModelScope.launch {
stats.edit { datastore.edit {
it[statsKeyTotalBad] = (it[statsKeyTotalBad] ?: 0) + 1 it[statsKeyTotalBad] = (it[statsKeyTotalBad] ?: 0) + 1
} }
} }
@ -132,7 +137,7 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
} }
} }
fun isDelayElapsed() = currentTime.value - timestampQuestion.value >= 3000 fun isDelayElapsed(delay: Int) = currentTime.value - timestampQuestion.value >= delay
fun updateTime(time: Long) { fun updateTime(time: Long) {
currentTime.value = time currentTime.value = time

View file

@ -17,6 +17,7 @@ import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import fr.uparis.diamantkennel.memorisationapplication.HOUR import fr.uparis.diamantkennel.memorisationapplication.HOUR
import fr.uparis.diamantkennel.memorisationapplication.MINUTE import fr.uparis.diamantkennel.memorisationapplication.MINUTE
import fr.uparis.diamantkennel.memorisationapplication.DELAY
import fr.uparis.diamantkennel.memorisationapplication.MemoApplication import fr.uparis.diamantkennel.memorisationapplication.MemoApplication
import fr.uparis.diamantkennel.memorisationapplication.RappelWorker import fr.uparis.diamantkennel.memorisationapplication.RappelWorker
import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_BAD import fr.uparis.diamantkennel.memorisationapplication.STATS_TOTAL_BAD
@ -34,7 +35,7 @@ import java.util.concurrent.TimeUnit
class SettingsViewModel(application: Application) : AndroidViewModel(application) { class SettingsViewModel(application: Application) : AndroidViewModel(application) {
private val dao = (application as MemoApplication).database.memoDao() private val dao = (application as MemoApplication).database.memoDao()
private val stats = application.dataStore private val datastore = application.dataStore
private val statsKeyTotal = intPreferencesKey(STATS_TOTAL_TRIED) private val statsKeyTotal = intPreferencesKey(STATS_TOTAL_TRIED)
private val statsKeyTotalDone = intPreferencesKey(STATS_TOTAL_DONE) private val statsKeyTotalDone = intPreferencesKey(STATS_TOTAL_DONE)
private val statsKeyTotalGood = intPreferencesKey(STATS_TOTAL_GOOD) private val statsKeyTotalGood = intPreferencesKey(STATS_TOTAL_GOOD)
@ -43,16 +44,19 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
private val notifH = intPreferencesKey(HOUR) private val notifH = intPreferencesKey(HOUR)
private val notifM = intPreferencesKey(MINUTE) private val notifM = intPreferencesKey(MINUTE)
val statTotal = stats.data.map { it[statsKeyTotal] ?: 0 } private val delay = intPreferencesKey(DELAY)
val statTotalDone = stats.data.map { it[statsKeyTotalDone] ?: 0 }
val statTotalGood = stats.data.map { it[statsKeyTotalGood] ?: 0 }
val statTotalBad = stats.data.map { it[statsKeyTotalBad] ?: 0 }
var prefConfig = stats.data.map { TimeConfig(it[notifH] ?: 8, it[notifM] ?: 0) } val statTotal = datastore.data.map { it[statsKeyTotal] ?: 0 }
val statTotalDone = datastore.data.map { it[statsKeyTotalDone] ?: 0 }
val statTotalGood = datastore.data.map { it[statsKeyTotalGood] ?: 0 }
val statTotalBad = datastore.data.map { it[statsKeyTotalBad] ?: 0 }
var prefConfigTime = datastore.data.map { TimeConfig(it[notifH] ?: 8, it[notifM] ?: 0) }
val deletionDB = mutableStateOf(false) val deletionDB = mutableStateOf(false)
val deletionStat = mutableStateOf(false) val deletionStat = mutableStateOf(false)
var notif = mutableStateOf(false) var notif = mutableStateOf(false)
val delayRequest = mutableStateOf(false)
val gavePermissionNow = mutableStateOf(false) val gavePermissionNow = mutableStateOf(false)
@ -66,7 +70,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
fun cleanStats() { fun cleanStats() {
deletionStat.value = false deletionStat.value = false
viewModelScope.launch { viewModelScope.launch {
stats.edit { datastore.edit {
it[statsKeyTotal] = 0 it[statsKeyTotal] = 0
it[statsKeyTotalDone] = 0 it[statsKeyTotalDone] = 0
it[statsKeyTotalGood] = 0 it[statsKeyTotalGood] = 0
@ -86,6 +90,15 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
schedule(newConfig, context) schedule(newConfig, context)
} }
fun choiceDelay(value: Int) {
delayRequest.value = false
viewModelScope.launch {
datastore.edit {
it[delay] = value
}
}
}
fun winrate(good: Int, bad: Int): Int { fun winrate(good: Int, bad: Int): Int {
val total = good + bad val total = good + bad
if (total == 0) { if (total == 0) {
@ -107,7 +120,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
private fun save(config: TimeConfig) { private fun save(config: TimeConfig) {
viewModelScope.launch { viewModelScope.launch {
stats.edit { datastore.edit {
it[notifH] = config.hour it[notifH] = config.hour
it[notifM] = config.minute it[notifM] = config.minute
} }

View file

@ -39,6 +39,7 @@
<string name="delete_db_desc">Voulez-vous supprimer la base de données ?</string> <string name="delete_db_desc">Voulez-vous supprimer la base de données ?</string>
<string name="yes">Oui</string> <string name="yes">Oui</string>
<string name="no">Non</string> <string name="no">Non</string>
<string name="validate">Valider</string>
<string name="bravo">Bravo</string> <string name="bravo">Bravo</string>
<string name="set_ended">Le set de questions est terminé !</string> <string name="set_ended">Le set de questions est terminé !</string>
<string name="stats">Statistiques</string> <string name="stats">Statistiques</string>
@ -60,4 +61,9 @@
<string name="gestion">Gestion</string> <string name="gestion">Gestion</string>
<string name="notification">Notifications</string> <string name="notification">Notifications</string>
<string name="confirm">Confirmer</string> <string name="confirm">Confirmer</string>
<string name="game">Parametre du jeu</string>
<string name="choice_delay_button">Délai avant solution</string>
<string name="reset_game_settings_button">Réinitialiser les paramètres du jeu</string>
<string name="choice_delay">Choix du délai avant solution</string>
<string name="enter_integer">Entrez un entier (en ms)</string>
</resources> </resources>