Merge branch 'parametrage' into 'master'
Parametrage See merge request diamant-kennel/mememorisation_mobile_groupe11!1
This commit is contained in:
commit
4f25c2d266
13 changed files with 509 additions and 149 deletions
|
@ -51,14 +51,14 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("androidx.core:core-ktx:1.9.0")
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||||
implementation("androidx.activity:activity-compose:1.8.0")
|
implementation("androidx.activity:activity-compose:1.8.1")
|
||||||
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
|
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
|
||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
implementation("androidx.compose.ui:ui-graphics")
|
implementation("androidx.compose.ui:ui-graphics")
|
||||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||||
implementation("androidx.compose.material3:material3")
|
implementation("androidx.compose.material3:material3:1.1.2")
|
||||||
implementation("androidx.core:core-ktx:1.12.0")
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
||||||
|
|
||||||
|
|
|
@ -23,3 +23,8 @@ 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 MINUTE = "minute"
|
||||||
|
|
|
@ -68,7 +68,7 @@ fun HomeScreen(
|
||||||
|
|
||||||
val errorEntry by model.error
|
val errorEntry by model.error
|
||||||
|
|
||||||
if (errorEntry != null) {
|
ShowDialog(errorEntry != null) {
|
||||||
ErrorDialog(
|
ErrorDialog(
|
||||||
when (errorEntry!!) {
|
when (errorEntry!!) {
|
||||||
ErrorsAjout.BAD_ENTRY -> context.getString(R.string.error_bad_entry)
|
ErrorsAjout.BAD_ENTRY -> context.getString(R.string.error_bad_entry)
|
||||||
|
@ -76,25 +76,30 @@ fun HomeScreen(
|
||||||
}, model::cleanErrors
|
}, model::cleanErrors
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ShowDialog(creationRequest) { CreationDialog(model::dismissCreation, model) }
|
||||||
if (creationRequest) {
|
ShowDialog(importationRequest) { ImportDialog(model::dismissImportation, model) }
|
||||||
CreationDialog(
|
ShowDialog(deletionRequest) {
|
||||||
dismiss = model::dismissCreation, model = model
|
DeletionDialog(model::dismissDeleteOne, model::deleteSelected)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importationRequest) {
|
Home(
|
||||||
ImportDialog(
|
padding,
|
||||||
dismiss = model::dismissImportation, model = model
|
navController,
|
||||||
|
model,
|
||||||
|
setOfQuestions,
|
||||||
|
currentSelection
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deletionRequest) {
|
@Composable
|
||||||
DeletionDialog(
|
private fun Home(
|
||||||
model::dismissDeleteOne,
|
padding: PaddingValues,
|
||||||
model::deleteSelected
|
navController: NavController,
|
||||||
)
|
model: HomeViewModel,
|
||||||
}
|
setOfQuestions: List<SetOfQuestions> = listOf(),
|
||||||
|
currentSelection: SetQuestions? = null,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(padding), horizontalAlignment = Alignment.CenterHorizontally
|
modifier = Modifier.padding(padding), horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
@ -146,7 +151,6 @@ private fun ActionRow(context: Context, model: HomeViewModel, navController: Nav
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CreationDialog(
|
fun CreationDialog(
|
||||||
dismiss: () -> Unit, model: HomeViewModel = viewModel()
|
dismiss: () -> Unit, model: HomeViewModel = viewModel()
|
||||||
|
@ -172,7 +176,7 @@ fun CreationDialog(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ImportDialog(
|
fun ImportDialog(
|
||||||
dismiss: () -> Unit, model: HomeViewModel
|
dismiss: () -> Unit, model: HomeViewModel
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package fr.uparis.diamantkennel.memorisationapplication
|
package fr.uparis.diamantkennel.memorisationapplication
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.BottomNavigation
|
import androidx.compose.material.BottomNavigation
|
||||||
|
@ -39,6 +41,7 @@ import fr.uparis.diamantkennel.memorisationapplication.ui.theme.MemorisationAppl
|
||||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = STATS)
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = STATS)
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContent {
|
setContent {
|
||||||
|
@ -54,18 +57,20 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreenMainActivity() {
|
fun MainScreenMainActivity() {
|
||||||
MainScreen()
|
MainScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreenPreview() {
|
fun MainScreenPreview() {
|
||||||
MainScreen()
|
MainScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen() {
|
fun MainScreen() {
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
|
@ -97,7 +102,7 @@ fun MainScreen() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composable(SETTINGS) { SettingsScreen(padding) }
|
composable(SETTINGS) { SettingsScreen() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,3 +140,14 @@ fun BottomBar(navController: NavHostController) =
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T: Any> ShowDialog(
|
||||||
|
condition: Boolean,
|
||||||
|
dialog: @Composable () -> T
|
||||||
|
) {
|
||||||
|
if (condition) {
|
||||||
|
dialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,10 @@ import android.app.Application
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.work.PeriodicWorkRequest
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import fr.uparis.diamantkennel.memorisationapplication.data.QuestionsDB
|
import fr.uparis.diamantkennel.memorisationapplication.data.QuestionsDB
|
||||||
import java.util.Calendar
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
const val CHANNEL_ID = "MY_CHANNEL_ID"
|
const val CHANNEL_ID = "REMINDERS"
|
||||||
|
|
||||||
class MemoApplication : Application() {
|
class MemoApplication : Application() {
|
||||||
val database: QuestionsDB by lazy { QuestionsDB.getDataBase(this) }
|
val database: QuestionsDB by lazy { QuestionsDB.getDataBase(this) }
|
||||||
|
@ -20,10 +15,6 @@ class MemoApplication : Application() {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
createChannel(this)
|
createChannel(this)
|
||||||
|
|
||||||
if (this.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
schedule()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createChannel(c: Context) {
|
private fun createChannel(c: Context) {
|
||||||
|
@ -39,25 +30,4 @@ class MemoApplication : Application() {
|
||||||
notificationManager.createNotificationChannel(channel)
|
notificationManager.createNotificationChannel(channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun schedule() {
|
|
||||||
val wm = WorkManager.getInstance(this)
|
|
||||||
wm.cancelAllWork()
|
|
||||||
wm.enqueue(request(10, 45))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun request(h: Int, m: Int): PeriodicWorkRequest {
|
|
||||||
val now = Calendar.getInstance()
|
|
||||||
val target = Calendar.getInstance().apply {
|
|
||||||
set(Calendar.HOUR_OF_DAY, h)
|
|
||||||
set(Calendar.MINUTE, m)
|
|
||||||
}
|
|
||||||
if (target.before(now))
|
|
||||||
target.add(Calendar.DAY_OF_YEAR, 1)
|
|
||||||
val delta = target.timeInMillis - now.timeInMillis
|
|
||||||
|
|
||||||
return PeriodicWorkRequest.Builder(RappelWorker::class.java, 1, TimeUnit.DAYS)
|
|
||||||
.setInitialDelay(delta, TimeUnit.MILLISECONDS)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,6 @@ import fr.uparis.diamantkennel.memorisationapplication.ui.ActionModifySet
|
||||||
import fr.uparis.diamantkennel.memorisationapplication.ui.ModifySetViewModel
|
import fr.uparis.diamantkennel.memorisationapplication.ui.ModifySetViewModel
|
||||||
import kotlin.text.Typography.ellipsis
|
import kotlin.text.Typography.ellipsis
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ModifySetScreen(
|
fun ModifySetScreen(
|
||||||
padding: PaddingValues,
|
padding: PaddingValues,
|
||||||
|
@ -47,20 +46,33 @@ fun ModifySetScreen(
|
||||||
model.setId.value = idSet
|
model.setId.value = idSet
|
||||||
model.updateQuestionList(idSet)
|
model.updateQuestionList(idSet)
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
val currentSelection by model.selection
|
val currentSelection by model.selection
|
||||||
val questions by model.questions.collectAsState(listOf())
|
val questions by model.questions.collectAsState(listOf())
|
||||||
var action by model.action
|
val action by model.action
|
||||||
|
|
||||||
if (action == ActionModifySet.AJOUT || action == ActionModifySet.MODIFICATION) {
|
ShowDialog(action == ActionModifySet.AJOUT || action == ActionModifySet.MODIFICATION) {
|
||||||
AjoutModifDialog(action, currentSelection, model::ajoutQuestion)
|
AjoutModifDialog(action, currentSelection, model::ajoutQuestion, model::dismissAction)
|
||||||
{ action = ActionModifySet.AUCUN }
|
}
|
||||||
|
ShowDialog(action == ActionModifySet.SUPPRIMER) {
|
||||||
|
RemoveDialog(model::removeQuestion, model::dismissAction)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action == ActionModifySet.SUPPRIMER) {
|
Modify(
|
||||||
RemoveDialog(model::removeQuestion)
|
padding = padding,
|
||||||
{ action = ActionModifySet.AUCUN }
|
model = model,
|
||||||
}
|
questions = questions,
|
||||||
|
currentSelection = currentSelection
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Modify(
|
||||||
|
padding: PaddingValues,
|
||||||
|
model: ModifySetViewModel,
|
||||||
|
questions: List<Question>,
|
||||||
|
currentSelection: Question?
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(padding),
|
||||||
|
@ -71,13 +83,13 @@ fun ModifySetScreen(
|
||||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
|
||||||
Button(
|
Button(
|
||||||
enabled = currentSelection != null,
|
enabled = currentSelection != null,
|
||||||
onClick = { action = ActionModifySet.MODIFICATION }) {
|
onClick = model::modifAction) {
|
||||||
Text(text = context.getString(R.string.modify))
|
Text(text = context.getString(R.string.modify))
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(2.dp))
|
Spacer(modifier = Modifier.padding(2.dp))
|
||||||
|
|
||||||
Button(onClick = { action = ActionModifySet.AJOUT }) {
|
Button(onClick = model::ajoutAction) {
|
||||||
Text(text = context.getString(R.string.add))
|
Text(text = context.getString(R.string.add))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +97,7 @@ fun ModifySetScreen(
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
enabled = currentSelection != null,
|
enabled = currentSelection != null,
|
||||||
onClick = { action = ActionModifySet.SUPPRIMER }) {
|
onClick = model::supprAction) {
|
||||||
Text(text = context.getString(R.string.modify_button_delete))
|
Text(text = context.getString(R.string.modify_button_delete))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,14 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.SnackbarDuration
|
import androidx.compose.material3.SnackbarDuration
|
||||||
import androidx.compose.material3.SnackbarHostState
|
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.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
@ -26,10 +25,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
|
import fr.uparis.diamantkennel.memorisationapplication.data.Question
|
||||||
import fr.uparis.diamantkennel.memorisationapplication.ui.AnswerType
|
import fr.uparis.diamantkennel.memorisationapplication.ui.AnswerType
|
||||||
import fr.uparis.diamantkennel.memorisationapplication.ui.PlayViewModel
|
import fr.uparis.diamantkennel.memorisationapplication.ui.PlayViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayScreen(
|
fun PlayScreen(
|
||||||
padding: PaddingValues,
|
padding: PaddingValues,
|
||||||
|
@ -41,40 +40,54 @@ fun PlayScreen(
|
||||||
// First update the list of questions
|
// First update the list of questions
|
||||||
model.updateQuestionList(idSet)
|
model.updateQuestionList(idSet)
|
||||||
|
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val question by model.currentQuestion
|
val question by model.currentQuestion
|
||||||
val reponse by model.proposedAnswer
|
val reponse by model.proposedAnswer
|
||||||
val correction by model.evaluatedAnswer
|
val correction by model.evaluatedAnswer
|
||||||
var giveup by model.showAnswer
|
val giveup by model.showAnswer
|
||||||
val gameEnded by model.end
|
val gameEnded by model.end
|
||||||
|
val delay by model.delay.collectAsState(initial = 5000)
|
||||||
|
|
||||||
val cpt by model.compteurSb
|
val cpt by model.compteurSb
|
||||||
if (correction != null) {
|
|
||||||
LaunchedEffect(cpt) {
|
SnackbarAnswer(
|
||||||
model.sbUpdate()
|
model,
|
||||||
snackbarHostState.showSnackbar(
|
snackbarHostState,
|
||||||
when (correction!!) {
|
cpt,
|
||||||
AnswerType.GOOD -> context.getString(R.string.good_answer)
|
correction
|
||||||
AnswerType.BAD -> context.getString(R.string.bad_answer)
|
|
||||||
}, duration = SnackbarDuration.Short
|
|
||||||
)
|
)
|
||||||
model.resetAfterSb()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gameEnded) {
|
ShowDialog(gameEnded) { EndDialog { navController.navigate(HOME) } }
|
||||||
EndDialog { navController.navigate(HOME) }
|
ShowDialog(giveup && question != null) {
|
||||||
}
|
|
||||||
|
|
||||||
if (giveup && question != null) {
|
|
||||||
SolutionDialog(question!!.reponse, model::newQuestion)
|
SolutionDialog(question!!.reponse, model::newQuestion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update timer if needed
|
// Update timer if needed
|
||||||
if (!model.isDelayElapsed() && question != null) {
|
model.updateTimer(delay)
|
||||||
model.updateTime(System.currentTimeMillis())
|
|
||||||
}
|
Play(
|
||||||
|
padding,
|
||||||
|
navController,
|
||||||
|
idSet,
|
||||||
|
model,
|
||||||
|
question,
|
||||||
|
reponse,
|
||||||
|
correction,
|
||||||
|
delay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Play(
|
||||||
|
padding: PaddingValues,
|
||||||
|
navController: NavController,
|
||||||
|
idSet: Int,
|
||||||
|
model: PlayViewModel,
|
||||||
|
question: Question?,
|
||||||
|
reponse: String,
|
||||||
|
correction: AnswerType?,
|
||||||
|
delay: Int
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(padding),
|
||||||
|
@ -85,7 +98,7 @@ fun PlayScreen(
|
||||||
Text(context.getString(R.string.no_question), fontSize = 30.sp)
|
Text(context.getString(R.string.no_question), fontSize = 30.sp)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Text(text = question!!.enonce, fontSize = 30.sp, textAlign = TextAlign.Center)
|
Text(text = question.enonce, fontSize = 30.sp, textAlign = TextAlign.Center)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(top = 20.dp))
|
Spacer(modifier = Modifier.padding(top = 20.dp))
|
||||||
|
|
||||||
|
@ -109,8 +122,9 @@ fun PlayScreen(
|
||||||
}
|
}
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
enabled = model.isDelayElapsed(),
|
enabled = model.isDelayElapsed(delay),
|
||||||
onClick = { giveup = true }) {
|
onClick = model::giveUp
|
||||||
|
) {
|
||||||
Text(text = context.getString(R.string.see_answer))
|
Text(text = context.getString(R.string.see_answer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,3 +157,25 @@ fun EndDialog(next: () -> Unit) =
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
Button(onClick = next) { Text(text = LocalContext.current.getString(R.string.ok)) }
|
Button(onClick = next) { Text(text = LocalContext.current.getString(R.string.ok)) }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SnackbarAnswer(
|
||||||
|
model: PlayViewModel,
|
||||||
|
snackbarHostState: SnackbarHostState,
|
||||||
|
cpt: Int,
|
||||||
|
correction: AnswerType?
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
if (correction != null) {
|
||||||
|
LaunchedEffect(cpt) {
|
||||||
|
model.sbUpdate()
|
||||||
|
snackbarHostState.showSnackbar(
|
||||||
|
when (correction) {
|
||||||
|
AnswerType.GOOD -> context.getString(R.string.good_answer)
|
||||||
|
AnswerType.BAD -> context.getString(R.string.bad_answer)
|
||||||
|
}, duration = SnackbarDuration.Short
|
||||||
|
)
|
||||||
|
model.resetAfterSb()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,45 +1,113 @@
|
||||||
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
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
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.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.Divider
|
import androidx.compose.material3.Divider
|
||||||
|
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.TimePickerLayoutType
|
||||||
|
import androidx.compose.material3.TimePickerState
|
||||||
|
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.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import fr.uparis.diamantkennel.memorisationapplication.ui.SettingsViewModel
|
import fr.uparis.diamantkennel.memorisationapplication.ui.SettingsViewModel
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel()) {
|
fun SettingsScreen(model: SettingsViewModel = viewModel()) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
var deletionDBRequest by model.deletionDB
|
var deletionDBRequest by model.deletionDB
|
||||||
var cleanStatRequest by model.deletionStat
|
var cleanStatRequest by model.deletionStat
|
||||||
var permissionNotif by model.gavePermissionNow
|
var choiceTimeNotifRequest by model.notif
|
||||||
|
var choiceDelayRequest by model.delayRequest
|
||||||
|
|
||||||
|
val prefConfigTime = runBlocking { model.prefConfigTime.first() }
|
||||||
|
|
||||||
|
val stateTime = rememberTimePickerState(
|
||||||
|
initialHour = prefConfigTime.hour,
|
||||||
|
initialMinute = prefConfigTime.minute,
|
||||||
|
is24Hour = true
|
||||||
|
)
|
||||||
|
|
||||||
model.checkPermission(context)
|
model.checkPermission(context)
|
||||||
|
|
||||||
|
ShowDialog(deletionDBRequest) { DeletionDBDialog(model::deleteDb, model::dismissDeletionDB) }
|
||||||
|
ShowDialog(cleanStatRequest) { CleanStatDialog(model::cleanStats, model::dismissDeletionStat) }
|
||||||
|
ShowDialog(choiceTimeNotifRequest) {
|
||||||
|
ChoiceTimeNotifDialog(
|
||||||
|
{ model.choiceTimeNotif(stateTime, context) },
|
||||||
|
model::dismissNotif,
|
||||||
|
stateTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ShowDialog(choiceDelayRequest) {
|
||||||
|
ChoiceDelayDialog(
|
||||||
|
{ model.choiceDelay(it) },
|
||||||
|
model::dismissDelayRequest
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings(
|
||||||
|
model,
|
||||||
|
{ choiceTimeNotifRequest = true },
|
||||||
|
{ deletionDBRequest = true },
|
||||||
|
{ cleanStatRequest = true },
|
||||||
|
{ choiceDelayRequest = true },
|
||||||
|
model::resetDelay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
|
@Composable
|
||||||
|
fun Settings(
|
||||||
|
model: SettingsViewModel = viewModel(),
|
||||||
|
onTimeNotifButtonClick: () -> Unit,
|
||||||
|
deletionDBRequest: () -> Unit,
|
||||||
|
cleanStatRequest: () -> Unit,
|
||||||
|
onDelayButtonClick: () -> Unit,
|
||||||
|
onResetButtonClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
var permissionNotif by model.gavePermissionNow
|
||||||
val permissionLauncher = rememberLauncherForActivityResult(
|
val permissionLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.RequestPermission(),
|
contract = ActivityResultContracts.RequestPermission(),
|
||||||
) {
|
) {
|
||||||
|
@ -48,44 +116,34 @@ fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deletionDBRequest) {
|
|
||||||
DeletionDBDialog(model::deleteDb) { deletionDBRequest = false }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cleanStatRequest) {
|
|
||||||
CleanStatDialog(model::cleanStats) { cleanStatRequest = false }
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Stats(model)
|
Stats(model = model)
|
||||||
|
AddSpacedDivider()
|
||||||
|
NotificationSettings(context, model, permissionNotif, permissionLauncher, onTimeNotifButtonClick)
|
||||||
|
AddSpacedDivider()
|
||||||
|
GestionSettings(context, deletionDBRequest, cleanStatRequest)
|
||||||
|
AddSpacedDivider()
|
||||||
|
GameSettings(context, onDelayButtonClick, onResetButtonClick)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.padding(top = 10.dp))
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
|
||||||
Divider(color = Color.Gray)
|
@Composable
|
||||||
Spacer(modifier = Modifier.padding(top = 10.dp))
|
fun NotificationSettings(
|
||||||
|
context: Context,
|
||||||
Text(text = "Gestion", fontSize = 30.sp)
|
model: SettingsViewModel,
|
||||||
|
permissionNotif: Boolean,
|
||||||
|
permissionLauncher: ActivityResultLauncher<String>,
|
||||||
|
onTimeNotifButtonClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Text(text = context.getString(R.string.notification), fontSize = 30.sp)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly
|
horizontalArrangement = Arrangement.SpaceEvenly
|
||||||
) {
|
) {
|
||||||
Button(
|
|
||||||
onClick = { deletionDBRequest = true },
|
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
|
|
||||||
) {
|
|
||||||
Text(text = context.getString(R.string.main_button_deletebase))
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
|
||||||
onClick = { cleanStatRequest = true },
|
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
|
|
||||||
) {
|
|
||||||
Text(text = context.getString(R.string.clean_stat_button))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Button(
|
Button(
|
||||||
enabled = !permissionNotif,
|
enabled = !permissionNotif,
|
||||||
onClick = {
|
onClick = {
|
||||||
|
@ -94,6 +152,59 @@ fun SettingsScreen(padding: PaddingValues, model: SettingsViewModel = viewModel(
|
||||||
) {
|
) {
|
||||||
Text(text = context.getString(R.string.permission_button))
|
Text(text = context.getString(R.string.permission_button))
|
||||||
}
|
}
|
||||||
|
Button(
|
||||||
|
enabled = permissionNotif,
|
||||||
|
onClick = onTimeNotifButtonClick
|
||||||
|
) {
|
||||||
|
Text(text = context.getString(R.string.time_notif_button))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun GestionSettings(context: Context, deletionDBRequest: () -> Unit, cleanStatRequest: () -> Unit) {
|
||||||
|
Text(text = context.getString(R.string.gestion), fontSize = 30.sp)
|
||||||
|
Row(
|
||||||
|
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, onResetButtonClick: () -> 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))
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
onClick = onResetButtonClick,
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = colorResource(id = R.color.red))
|
||||||
|
) {
|
||||||
|
Text(text = context.getString(R.string.reset_button))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,3 +255,71 @@ fun CleanStatDialog(confirm: () -> Unit, dismiss: () -> Unit) =
|
||||||
Button(onClick = confirm) { Text(text = LocalContext.current.getString(R.string.yes)) }
|
Button(onClick = confirm) { Text(text = LocalContext.current.getString(R.string.yes)) }
|
||||||
},
|
},
|
||||||
dismissButton = { Button(onClick = dismiss) { Text(text = LocalContext.current.getString(R.string.no)) } })
|
dismissButton = { Button(onClick = dismiss) { Text(text = LocalContext.current.getString(R.string.no)) } })
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ChoiceTimeNotifDialog(confirm: () -> Unit, dismiss: () -> Unit, state: TimePickerState) {
|
||||||
|
Dialog(onDismissRequest = dismiss) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(550.dp),
|
||||||
|
shape = RoundedCornerShape(16.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = LocalContext.current.getString(R.string.time_notif),
|
||||||
|
fontSize = 30.sp,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
|
||||||
|
TimePicker(state = state, layoutType = TimePickerLayoutType.Vertical)
|
||||||
|
|
||||||
|
Button(onClick = confirm) {
|
||||||
|
Text(text = LocalContext.current.getString(R.string.confirm))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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()) } else dismiss()
|
||||||
|
}) {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package fr.uparis.diamantkennel.memorisationapplication
|
||||||
|
|
||||||
|
data class TimeConfig(
|
||||||
|
val hour: Int = 8,
|
||||||
|
val minute: Int = 0
|
||||||
|
)
|
|
@ -63,4 +63,18 @@ class ModifySetViewModel(application: Application) : AndroidViewModel(applicatio
|
||||||
dao.deleteQuestion(selection.value!!)
|
dao.deleteQuestion(selection.value!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun modifAction() {
|
||||||
|
action.value = ActionModifySet.MODIFICATION
|
||||||
|
}
|
||||||
|
fun ajoutAction() {
|
||||||
|
action.value = ActionModifySet.AJOUT
|
||||||
|
}
|
||||||
|
fun supprAction() {
|
||||||
|
action.value = ActionModifySet.SUPPRIMER
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissAction() {
|
||||||
|
action.value = ActionModifySet.AUCUN
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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] ?: 5000 }
|
||||||
|
|
||||||
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,10 +137,20 @@ class PlayViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDelayElapsed() = currentTime.value - timestampQuestion.value >= 3000
|
fun updateTimer(delay: Int) {
|
||||||
|
if (!isDelayElapsed(delay) && currentQuestion.value != null) {
|
||||||
|
updateTime(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isDelayElapsed(delay: Int) = currentTime.value - timestampQuestion.value >= delay
|
||||||
|
|
||||||
fun updateTime(time: Long) {
|
fun updateTime(time: Long) {
|
||||||
currentTime.value = time
|
currentTime.value = time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun giveUp() {
|
||||||
|
showAnswer.value = true
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,37 +6,57 @@ import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.TimePickerState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.datastore.preferences.core.intPreferencesKey
|
import androidx.datastore.preferences.core.intPreferencesKey
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import fr.uparis.diamantkennel.memorisationapplication.HOUR
|
||||||
|
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.STATS_TOTAL_BAD
|
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.TimeConfig
|
||||||
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.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.Calendar
|
||||||
|
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)
|
||||||
private val statsKeyTotalBad = intPreferencesKey(STATS_TOTAL_BAD)
|
private val statsKeyTotalBad = intPreferencesKey(STATS_TOTAL_BAD)
|
||||||
|
|
||||||
val statTotal = stats.data.map { it[statsKeyTotal] ?: 0 }
|
private val notifH = intPreferencesKey(HOUR)
|
||||||
val statTotalDone = stats.data.map { it[statsKeyTotalDone] ?: 0 }
|
private val notifM = intPreferencesKey(MINUTE)
|
||||||
val statTotalGood = stats.data.map { it[statsKeyTotalGood] ?: 0 }
|
|
||||||
val statTotalBad = stats.data.map { it[statsKeyTotalBad] ?: 0 }
|
|
||||||
|
|
||||||
val deletionDB = mutableStateOf(false)
|
private val delay = intPreferencesKey(DELAY)
|
||||||
val deletionStat = mutableStateOf(false)
|
|
||||||
|
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) }
|
||||||
|
|
||||||
|
var deletionDB = mutableStateOf(false)
|
||||||
|
var deletionStat = mutableStateOf(false)
|
||||||
|
var notif = mutableStateOf(false)
|
||||||
|
var delayRequest = mutableStateOf(false)
|
||||||
|
|
||||||
val gavePermissionNow = mutableStateOf(false)
|
val gavePermissionNow = mutableStateOf(false)
|
||||||
|
|
||||||
|
@ -50,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
|
||||||
|
@ -59,6 +79,34 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
fun choiceTimeNotif(state: TimePickerState, context: Context) {
|
||||||
|
notif.value = false
|
||||||
|
val newConfig = TimeConfig(
|
||||||
|
state.hour,
|
||||||
|
state.minute
|
||||||
|
)
|
||||||
|
save(newConfig)
|
||||||
|
schedule(newConfig, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun choiceDelay(value: Int) {
|
||||||
|
delayRequest.value = false
|
||||||
|
viewModelScope.launch {
|
||||||
|
datastore.edit {
|
||||||
|
it[delay] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun resetDelay() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
datastore.edit {
|
||||||
|
it[delay] = 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -77,4 +125,47 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||||
gavePermissionNow.value =
|
gavePermissionNow.value =
|
||||||
context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
|
context.checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun save(config: TimeConfig) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
datastore.edit {
|
||||||
|
it[notifH] = config.hour
|
||||||
|
it[notifM] = config.minute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun schedule(config: TimeConfig, context: Context) {
|
||||||
|
val wm = WorkManager.getInstance(context)
|
||||||
|
wm.cancelAllWork()
|
||||||
|
wm.enqueue(request(config.hour, config.minute))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun request(h: Int, m: Int): PeriodicWorkRequest {
|
||||||
|
val now = Calendar.getInstance()
|
||||||
|
val target = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, h)
|
||||||
|
set(Calendar.MINUTE, m)
|
||||||
|
}
|
||||||
|
if (target.before(now))
|
||||||
|
target.add(Calendar.DAY_OF_YEAR, 1)
|
||||||
|
val delta = target.timeInMillis - now.timeInMillis
|
||||||
|
|
||||||
|
return PeriodicWorkRequest.Builder(RappelWorker::class.java, 1, TimeUnit.DAYS)
|
||||||
|
.setInitialDelay(delta, TimeUnit.MILLISECONDS)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissDeletionDB() {
|
||||||
|
deletionDB.value = false
|
||||||
|
}
|
||||||
|
fun dismissDeletionStat() {
|
||||||
|
deletionStat.value = false
|
||||||
|
}
|
||||||
|
fun dismissNotif() {
|
||||||
|
notif.value = false
|
||||||
|
}
|
||||||
|
fun dismissDelayRequest() {
|
||||||
|
delayRequest.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<string name="error">Erreur</string>
|
<string name="error">Erreur</string>
|
||||||
<string name="delete_set_questions">Supprimer un jeu de question</string>
|
<string name="delete_set_questions">Supprimer un jeu de question</string>
|
||||||
<string name="delete_set_qestions_desc">Voulez-vous supprimer ce jeu de question ?</string>
|
<string name="delete_set_qestions_desc">Voulez-vous supprimer ce jeu de question ?</string>
|
||||||
<string name="ok">Ok</string>
|
<string name="ok">OK</string>
|
||||||
<string name="update_question">Mettre à jour la question</string>
|
<string name="update_question">Mettre à jour la question</string>
|
||||||
<string name="add_question">Ajouter une question</string>
|
<string name="add_question">Ajouter une question</string>
|
||||||
<string name="question">Question</string>
|
<string name="question">Question</string>
|
||||||
|
@ -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>
|
||||||
|
@ -55,4 +56,15 @@
|
||||||
<string name="notif_channel_desc">Notifications de rappel</string>
|
<string name="notif_channel_desc">Notifications de rappel</string>
|
||||||
<string name="notif_reminder_title">Rappel</string>
|
<string name="notif_reminder_title">Rappel</string>
|
||||||
<string name="notif_reminder_content">Il est temps de réviser !</string>
|
<string name="notif_reminder_content">Il est temps de réviser !</string>
|
||||||
|
<string name="time_notif_button">Modifier heure de rappel</string>
|
||||||
|
<string name="time_notif">Choix heure de rappel</string>
|
||||||
|
<string name="gestion">Gestion</string>
|
||||||
|
<string name="notification">Notifications</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_button">Réinitialiser par défaut</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>
|
||||||
|
|
Reference in a new issue