update quiz type 2

This commit is contained in:
DimazzP 2025-02-16 14:01:59 +07:00
parent 133fa954fd
commit 522e568a9e
2 changed files with 362 additions and 545 deletions

View File

@ -31,20 +31,21 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.data.model.MaterialDataModel
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
@ -52,8 +53,6 @@ import androidx.compose.ui.unit.IntOffset
import androidx.constraintlayout.compose.ConstraintLayout
import com.example.lexilearn.R
import com.example.lexilearn.data.model.QuestionType
import com.example.lexilearn.domain.models.ModelAnswerRead
import com.example.lexilearn.ui.components.ButtonNext
import com.example.lexilearn.ui.components.CardQuiz
import com.example.lexilearn.ui.components.DraggableAnswerCard
import com.example.lexilearn.ui.components.FirebaseImage
@ -65,7 +64,6 @@ import com.google.accompanist.flowlayout.FlowRow
import com.google.accompanist.flowlayout.MainAxisAlignment
import kotlin.math.roundToInt
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun QuizScreen(
navController: NavController,
@ -73,12 +71,22 @@ fun QuizScreen(
quizViewModel: QuizViewModel = viewModel()
) {
val question by quizViewModel.currentQuestion.collectAsState()
val isButtonVisible by quizViewModel.isButtonVisible
val isAnswerCorrect by quizViewModel.isAnswerCorrect.collectAsState()
val isTTSInitialized = quizViewModel.isTTSInitialized.observeAsState(false)
val snackbarMessage by quizViewModel.snackbarMessage.collectAsState()
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(Unit) {
quizViewModel.loadRandomQuestion(material)
}
LaunchedEffect(snackbarMessage) {
snackbarMessage?.let { message ->
snackbarHostState.showSnackbar(message)
quizViewModel.onSnackbarShown()
}
}
var rectColumnAnswer by remember { mutableStateOf(Rect.Zero) }
@ -92,25 +100,6 @@ fun QuizScreen(
val dataQuiz by quizViewModel.questionShuffled.collectAsState()
val listAnswer by quizViewModel.shuffledAnswerLetters.collectAsState()
// var dataQuiz = remember {
// mutableStateListOf(
// ModelSpell(1, true, "? ", showCard = false),
// ModelSpell(2, true, "?", showCard = false),
// ModelSpell(3, true, "?", showCard = false),
// ModelSpell(4, true, "?", showCard = false),
// )
// }
// val listAnswer =
// remember {
// mutableStateListOf(
// ModelAnswerRead(1, "a"),
// ModelAnswerRead(2, "c"),
// ModelAnswerRead(3, "d"),
// ModelAnswerRead(4, "k")
// )
// }
val quizXOffset = remember {
mutableStateMapOf<Int, Float>()
}
@ -139,7 +128,10 @@ fun QuizScreen(
mutableStateMapOf<Int, Rect>()
}
Surface(modifier = Modifier.fillMaxSize()) {
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
){innerPadding->
Surface(modifier = Modifier.fillMaxSize().padding(innerPadding)) {
GradientQuiz(
navController = navController,
headerText = stringResource(id = R.string.spelltitle),
@ -156,7 +148,7 @@ fun QuizScreen(
horizontalArrangement = Arrangement.End,
) {
Text(
"Level: 3",
"",
modifier = Modifier.padding(22.dp),
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
@ -173,25 +165,13 @@ fun QuizScreen(
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(12.dp))
// Text(
// text = "Susun Kata",
// fontSize = 20.sp,
// fontWeight = FontWeight.Bold
// )
// Spacer(modifier = Modifier.height(12.dp))
// Image(
// painter = painterResource(id = R.drawable.ic_news),
// contentDescription = "image",
// modifier = Modifier.size(120.dp)
// )
if (question != null) {
if (question?.questionType == QuestionType.IMAGE) {
FirebaseImage(
path = question!!.question,
contentScale = ContentScale.Crop,
modifier = Modifier.size(200.dp)
modifier = Modifier.size(160.dp)
)
} else if (question?.questionType == QuestionType.TEXT) {
Text(
text = question!!.question,
@ -204,164 +184,25 @@ fun QuizScreen(
onClick = {
quizViewModel.speakLetter(question!!.question)
},
modifier = Modifier
.size(160.dp)
) {
Icon(
imageVector = Icons.Filled.VolumeUp, // 🔥 Ikon Speaker dari Material Icons
imageVector = Icons.Filled.VolumeUp,
contentDescription = "Speaker Icon",
tint = Color.Black // 🔥 Warna ikon
tint = Color.Black,
modifier = Modifier.size(200.dp)
)
}
}
}
Spacer(modifier = Modifier.height(12.dp))
// Row(
// modifier = Modifier.fillMaxWidth(),
// horizontalArrangement = Arrangement.SpaceAround
// ) {
// dataQuiz.forEach { dt ->
// val id = dt.id
// if (!boxRectDragable.containsKey(id))
// boxRectDragable[id] = Rect.Zero
// if (!boxRectQuiz.containsKey(id))
// boxRectQuiz[id] = Rect.Zero
// if (!quizXOffset.containsKey(id))
// quizXOffset[id] = 0f
// if (!quizYOffset.containsKey(id))
// quizYOffset[id] = 0f
// CardQuiz(
// modifier = Modifier
// .padding(vertical = 10.dp)
// .size(minSize)
// .onGloballyPositioned { coordinates ->
// if (dt.type)
// boxRectQuiz[id] = coordinates.boundsInWindow()
// }
// ) {
// if (dt.type) {
// Text(
// text = dt.data, // Use the state to display text
// color = ctextWhite,
// fontWeight = FontWeight.Bold,
// textAlign = TextAlign.Center,
// modifier = Modifier.fillMaxWidth()
// )
// if (dt.showCard) {
// DraggableAnswerCard(
// item = dt.data,
// modifier = Modifier
// .offset {
// val xOffset = quizXOffset[id] ?: 0f
// val yOffset = quizYOffset[id] ?: 0f
// IntOffset(
// xOffset.roundToInt(),
// yOffset.roundToInt()
// )
// }
// .onGloballyPositioned {
// boxRectDragable[id] =
// it.boundsInWindow()
// }
// .fillMaxSize()
// .pointerInput(Unit) {
// detectDragGestures(
// onDrag = { change, dragAmount ->
// change.consume()
// quizXOffset[id] =
// quizXOffset[id]!! + dragAmount.x
// quizYOffset[id] =
// quizYOffset[id]!! + dragAmount.y
// },
// onDragEnd = {
// var checkNull = false
// for ((ind, entry) in boxRectQuiz.entries.withIndex()) {
// val (key, rect) = entry
// if (key == id)
// continue
//
// if (dataQuiz[ind].hasContent)
// continue
//
// if (boxRectDragable[id]!!.overlaps(
// rect
// )
// ) {
// quizViewModel.updateQuestionShuffled(
// ind,
// dt
// )
//// dataQuiz =
//// dataQuiz.apply {
//// this[ind] =
//// this[ind].copy(
//// data = dt.data,
//// showCard = true,
//// emp = dt.emp,
//// hasContent = true
//// )
//// }
// dt.apply {
// hasContent = false
// showCard = false
// data = "?"
// }
// checkNull = true
// quizXOffset[id] = 0f
// quizYOffset[id] = 0f
// break
// }
//
// }
// if (boxRectDragable[id]!!.overlaps(
// rectColumnAnswer
// )
// ) {
// val emDt = dt.emp
// if (emDt != null) {
// dt.apply {
// hasContent = false
// showCard = false
// data = "?"
// }
// checkNull = true
// cardSize[emDt] = maxSize
// quizXOffset[id] = 0f
// quizYOffset[id] = 0f
// dt.data = "?"
// }
// }
// if (!checkNull) {
// quizXOffset[id] = 0f
// quizYOffset[id] = 0f
// }
// }
// )
// }
// )
// }
// } else {
// Box(modifier = Modifier.align(Alignment.CenterVertically)) {
// Text(
// text = dt.data,
// color = ctextWhite,
// fontSize = 20.sp,
// fontWeight = FontWeight.Bold,
// textAlign = TextAlign.Center,
// )
// }
//
// }
// }
// }
// }
FlowRow(
modifier = Modifier.fillMaxWidth().padding(12.dp),
mainAxisSpacing = 8.dp, // Jarak horizontal antar item
crossAxisSpacing = 8.dp, // Jarak vertikal antar item
mainAxisAlignment = MainAxisAlignment.Center
mainAxisSpacing = 8.dp,
crossAxisSpacing = 8.dp,
mainAxisAlignment = MainAxisAlignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
) {
dataQuiz.forEach { dt ->
val id = dt.id
@ -404,7 +245,8 @@ fun QuizScreen(
)
}
.onGloballyPositioned {
boxRectDragable[id] = it.boundsInWindow()
boxRectDragable[id] =
it.boundsInWindow()
}
.fillMaxSize()
.pointerInput(Unit) {
@ -426,8 +268,14 @@ fun QuizScreen(
if (dataQuiz[ind].hasContent)
continue
if (boxRectDragable[id]!!.overlaps(rect)) {
quizViewModel.updateQuestionShuffled(ind, dt)
if (boxRectDragable[id]!!.overlaps(
rect
)
) {
quizViewModel.updateQuestionShuffled(
ind,
dt
)
dt.apply {
hasContent = false
showCard = false
@ -439,7 +287,10 @@ fun QuizScreen(
break
}
}
if (boxRectDragable[id]!!.overlaps(rectColumnAnswer)) {
if (boxRectDragable[id]!!.overlaps(
rectColumnAnswer
)
) {
val emDt = dt.emp
if (emDt != null) {
dt.apply {
@ -491,11 +342,12 @@ fun QuizScreen(
Column(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 300.dp)
.onGloballyPositioned {
rectColumnAnswer = it.boundsInWindow()
},
) {
listAnswer.chunked(2).forEach { rowItems ->
listAnswer.chunked(3).forEach { rowItems ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
@ -513,7 +365,7 @@ fun QuizScreen(
DraggableAnswerCard(
item = item.data,
modifier = Modifier
.padding(10.dp)
.padding(4.dp)
.size(cardSize[id]!!)
.offset {
IntOffset(
@ -548,18 +400,13 @@ fun QuizScreen(
cardSize[id] = minSize
quizViewModel.updateQuestionShuffled(
ind,
dataQuiz[ind].copy(data = item.data, showCard = true, emp = id, hasContent = true)
dataQuiz[ind].copy(
data = item.data,
showCard = true,
emp = id,
hasContent = true
)
)
// dataQuiz = dataQuiz
// .apply {
// this[ind] =
// this[ind].copy(
// data = item.data,
// showCard = true,
// emp = id,
// hasContent = true
// )
// }
checkNull = true
cardSize[id] = 0.dp
answerXOffset[id] = 0f
@ -583,86 +430,9 @@ fun QuizScreen(
Spacer(modifier = Modifier.height(12.dp))
}
ButtonNext(
onclick = {
navController.navigate("write")
},
text = stringResource(id = R.string.next),
painter = painterResource(id = R.drawable.ic_next),
modifier = Modifier
.padding(vertical = 30.dp, horizontal = 50.dp)
.fillMaxWidth()
.constrainAs(buttonRef) {
bottom.linkTo(parent.bottom)
}
)
}
}
}
}
}
//
//@Composable
//fun DragAndDropAnswer(correctAnswer: String, onAnswerSubmitted: (String) -> Unit) {
// val shuffledLetters = correctAnswer.toList().shuffled()
//
// var selectedLetters by remember { mutableStateOf("") }
// val answerBoxRect = remember { mutableStateOf(Rect.Zero) }
//
// Column(
// modifier = Modifier.fillMaxWidth(),
// horizontalAlignment = Alignment.CenterHorizontally
// ) {
// Box(
// modifier = Modifier
// .size(150.dp, 50.dp)
// .background(Color.LightGray)
// .onGloballyPositioned { answerBoxRect.value = it.boundsInWindow() },
// contentAlignment = Alignment.Center
// ) {
// Text(text = selectedLetters, fontSize = 20.sp)
// }
//
// Spacer(modifier = Modifier.height(20.dp))
//
// Row {
// shuffledLetters.forEach { letter ->
// DraggableLetter(
// letter = letter.toString(),
// onDrop = {
// selectedLetters += letter
// if (selectedLetters.length == correctAnswer.length) {
// onAnswerSubmitted(selectedLetters)
// }
// }
// )
// }
// }
// }
//}
//
//@Composable
//fun DraggableLetter(letter: String, onDrop: () -> Unit) {
// var offsetX by remember { mutableStateOf(0f) }
// var offsetY by remember { mutableStateOf(0f) }
//
// Box(
// modifier = Modifier
// .size(50.dp)
// .background(Color.Blue)
// .pointerInput(Unit) {
// detectDragGestures(
// onDrag = { change, dragAmount ->
// change.consume()
// offsetX += dragAmount.x
// offsetY += dragAmount.y
// },
// onDragEnd = { onDrop() }
// )
// },
// contentAlignment = Alignment.Center
// ) {
// Text(text = letter, fontSize = 20.sp, color = Color.White)
// }
//}

View File

@ -2,16 +2,22 @@ package com.example.lexilearn.ui.views.pQuiz
import android.app.Application
import android.speech.tts.TextToSpeech
import android.util.Log
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.model.QuizQuestion
import com.example.lexilearn.data.repository.QuizRepository
import com.example.lexilearn.domain.models.ModelAnswerRead
import com.example.lexilearn.domain.models.ModelSpell
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.util.Locale
class QuizViewModel(application: Application) : AndroidViewModel(application),
@ -33,25 +39,14 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
private val _shuffledAnswerLetters = MutableStateFlow<List<ModelAnswerRead>>(emptyList())
val shuffledAnswerLetters: StateFlow<List<ModelAnswerRead>> = _shuffledAnswerLetters
// fun updateShuffledLetter(index: Int, newData: ModelAnswerRead) {
// _shuffledAnswerLetters.value = _shuffledAnswerLetters.value.mapIndexed { i, item ->
// if (i == index) {
// item.copy(
// data = newData.data,
// showCard = true,
// emp = newData.emp,
// hasContent = true
// )
// } else {
// item
// }
// }
// }
private val _questionShuffled = MutableStateFlow<List<ModelSpell>>(emptyList())
val questionShuffled: StateFlow<List<ModelSpell>> = _questionShuffled
private val _isButtonVisible = mutableStateOf(false)
val isButtonVisible: State<Boolean> get() = _isButtonVisible
fun updateQuestionShuffled(index: Int, newData: ModelSpell) {
_questionShuffled.value = _questionShuffled.value.mapIndexed { i, item ->
if (i == index) {
@ -69,20 +64,29 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
fun loadRandomQuestion(material: MaterialDataModel) {
// _currentQuestion.value = quizRepository.generateRandomQuestion(material)
_currentQuestion.value = quizRepository.generateQuestion(material, 1, 2)
val dataQuest = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
_currentQuestion.value = quizRepository.generateQuestion(material, 2, 2)
val dataQuest =
_currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
ModelSpell(index + 1, true, "?", showCard = false)
} ?: emptyList()
_questionShuffled.value = dataQuest
val listAnswer = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
val listAnswer =
_currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
ModelAnswerRead(index + 1, char.toString())
} ?: emptyList()
_shuffledAnswerLetters.value = listAnswer
println("testing_question ${_currentQuestion.value}")
println("testing_question ${material}")
val shuflledAnswer = listAnswer.shuffled()
_shuffledAnswerLetters.value = shuflledAnswer
// Menambahkan delay 1 detik sebelum memanggil observeData()
// viewModelScope.launch {
// delay(1000L) // delay 1 detik
//
// }
}
fun submitAnswer(answer: String) {
_userAnswer.value = answer
_isAnswerCorrect.value =
@ -95,6 +99,7 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
init {
tts = TextToSpeech(application, this)
observeData()
}
override fun onInit(status: Int) {
@ -112,6 +117,48 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
}
}
fun checkAnswer(answerString: String): Boolean {
if (answerString == currentQuestion.value?.correctAnswer?.correctWord) {
return true
}
return false
}
fun observeData() {
viewModelScope.launch {
_questionShuffled.collect { newList ->
// Lakukan aksi setiap kali data berubah
var answerString = ""
newList.forEach { text ->
answerString = answerString + text.data
}
if (answerString.contains("?") || answerString.isEmpty() || answerString.contains("null")) {
_isButtonVisible.value = false
} else {
Log.d("cobaaja", answerString)
val checkData = checkAnswer(answerString);
if (checkData) {
triggerSnackbar("Jawaban Benar")
} else {
triggerSnackbar("Jawaban Salah")
}
_isButtonVisible.value = true
}
}
}
}
private val _snackbarMessage = MutableStateFlow<String?>(null)
val snackbarMessage: StateFlow<String?> = _snackbarMessage
fun triggerSnackbar(message: String) {
_snackbarMessage.value = message
}
fun onSnackbarShown() {
_snackbarMessage.value = null
}
override fun onCleared() {
tts?.stop()
tts?.shutdown()