update quiz

This commit is contained in:
DimazzP 2025-02-12 09:13:54 +07:00
parent afb61f8cac
commit 133fa954fd
15 changed files with 901 additions and 32 deletions

View File

@ -71,6 +71,7 @@ dependencies {
implementation("com.google.accompanist:accompanist-navigation-material:0.24.13-rc")
implementation("io.coil-kt:coil-compose:2.1.0")
implementation ("com.google.code.gson:gson:2.10.1")
implementation("com.google.accompanist:accompanist-flowlayout:0.30.1")
implementation(platform("com.google.firebase:firebase-bom:32.0.0"))
implementation("com.google.firebase:firebase-analytics")

View File

@ -5,6 +5,7 @@ import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@ -18,6 +19,7 @@ import com.example.lexilearn.ui.views.pLearAlphabet.LearnAlphabetScreen
import com.example.lexilearn.ui.views.pNavMaterial.NavMaterialScreen
import com.example.lexilearn.ui.views.pLearnNumber.LearnNumberScreen
import com.example.lexilearn.ui.views.pLogin.LoginScreen
import com.example.lexilearn.ui.views.pQuiz.QuizScreen
import com.example.lexilearn.ui.views.pQuiz.pRead.ReadScreen
import com.example.lexilearn.ui.views.pQuiz.pSpell.SpellScreen
import com.example.lexilearn.ui.views.pQuiz.pWrite.WriteScreen
@ -104,7 +106,19 @@ fun MyApp() {
println("masuk kondisi else")
}
}
composable("quizScreen") { backStackEntry ->
val selectedMaterialList = navController.previousBackStackEntry
?.savedStateHandle
?.get<List<MaterialDataModel>>("selectedMaterialList")
val selectedMaterial = selectedMaterialList?.firstOrNull()
if (selectedMaterial != null) {
QuizScreen(navController, selectedMaterial)
} else {
Text("Error: Data tidak tersedia")
}
}
// composable("detailMaterial/{materialJson}") { backStackEntry ->
// val jsonMaterial = backStackEntry.arguments?.getString("materialJson")
//

View File

@ -0,0 +1,5 @@
package com.example.lexilearn.data.model
enum class AnswerType {
FULL_WORD, SHUFFLED_LETTERS
}

View File

@ -10,12 +10,4 @@ data class MaterialDataModel (
@SerializedName("enDescription") val enDescription: String = "",
@SerializedName("image") val image: String = "",
@SerializedName("category") val category: String = ""
// val id: String = "",
// val idName: String = "",
// val enName: String = "",
// val idDescription: String = "",
// val enDescription: String = "",
// val image: String = "",
// val category: String = ""
)

View File

@ -0,0 +1,5 @@
package com.example.lexilearn.data.model
enum class QuestionType {
TEXT, IMAGE, AUDIO
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.data.model
data class QuizAnswer(
val answerType: AnswerType, // FULL_WORD atau SHUFFLED_LETTERS
val correctWord: String // Kata yang benar dalam bahasa Inggris
)

View File

@ -0,0 +1,7 @@
package com.example.lexilearn.data.model
data class QuizQuestion(
val questionType: QuestionType, // TEXT, IMAGE, AUDIO
val question: String, // Bisa teks, URL gambar, atau teks untuk TTS
val correctAnswer: QuizAnswer // Jawaban yang benar
)

View File

@ -0,0 +1,54 @@
package com.example.lexilearn.data.repository
import com.example.lexilearn.data.model.AnswerType
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.model.QuestionType
import com.example.lexilearn.data.model.QuizAnswer
import com.example.lexilearn.data.model.QuizQuestion
import kotlin.random.Random
class QuizRepository {
/**
* Fungsi ini akan membuat soal dengan mode soal & jawaban yang DITENTUKAN oleh parameter.
*/
fun generateQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int): QuizQuestion {
val questionType = when (questionMode) {
1 -> listOf(QuestionType.TEXT, QuestionType.IMAGE).random() // Text atau Image
2 -> QuestionType.AUDIO // Hanya Audio
else -> QuestionType.TEXT // Default ke Text
}
val question = when (questionType) {
QuestionType.TEXT -> listOf(material.idName, material.idDescription).random()
QuestionType.IMAGE -> material.image
QuestionType.AUDIO -> material.enName
} ?: "Soal tidak tersedia"
val answerType = when (answerMode) {
1 -> AnswerType.FULL_WORD
2 -> AnswerType.SHUFFLED_LETTERS
else -> AnswerType.FULL_WORD
}
return QuizQuestion(
questionType = questionType,
question = question,
correctAnswer = QuizAnswer(
answerType = answerType,
correctWord = material.enName
)
)
}
/**
* Fungsi ini akan membuat soal dengan mode soal & jawaban yang DIPILIH SECARA ACAK.
*/
fun generateRandomQuestion(material: MaterialDataModel): QuizQuestion {
val randomQuestionMode = Random.nextInt(1, 3) // Acak antara 1 (Text/Image) atau 2 (Audio)
val randomAnswerMode = Random.nextInt(1, 3) // Acak antara 1 (FULL_WORD) atau 2 (SHUFFLED_LETTERS)
return generateQuestion(material, randomQuestionMode, randomAnswerMode)
}
}

View File

@ -1,8 +1,6 @@
package com.example.lexilearn.ui.views.pDetailMaterial
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -26,7 +24,6 @@ import androidx.compose.ui.draw.clip
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
import androidx.compose.ui.unit.sp
@ -40,10 +37,8 @@ import com.example.lexilearn.ui.components.FirebaseImage
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.HorizontalLine
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.cGray
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.ctextGray
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun DetailMaterialScreen(navController: NavController, materialListData: List<MaterialDataModel>) {
@ -159,7 +154,17 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List<Ma
}
ButtonNext(
onclick = {
navController.navigate("spell")
val selectedMaterial = materialListData.firstOrNull()
if (selectedMaterial != null) {
// Simpan ke savedStateHandle sebagai List<MaterialDataModel> tapi hanya satu elemen
navController.currentBackStackEntry
?.savedStateHandle
?.set("selectedMaterialList", listOf(selectedMaterial))
// Navigasi ke QuizScreen
navController.navigate("quizScreen")
}
// navController.navigate("spell")
},
text = "Kerjakan Kuis",
painter = painterResource(id = R.drawable.ic_next),

View File

@ -7,6 +7,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.repository.MaterialRepository
import com.example.lexilearn.domain.models.ModelSpell
import com.example.lexilearn.utils.generateNumberList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -22,12 +23,14 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli
private val repository = MaterialRepository(application)
private val _materialList = MutableStateFlow<List<MaterialDataModel>>(emptyList())
val materialList: StateFlow<List<MaterialDataModel>> = _materialList
private val _textTitle = MutableLiveData("Hello, Jetpack Compose!")
val textTitle: LiveData<String> = _textTitle
fun fetchMaterial(materialListData: List<MaterialDataModel>) {
_materialList.value = materialListData
if (materialListData.isNotEmpty()) {
@ -35,9 +38,9 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli
_textTitle.value = "hewan"
} else if (materialListData[0].category == "limb") {
_textTitle.value = "anggota tubuh"
}else if (materialListData[0].category == "house") {
} else if (materialListData[0].category == "house") {
_textTitle.value = "bagian rumah"
}else if (materialListData[0].category == "family") {
} else if (materialListData[0].category == "family") {
_textTitle.value = "anggota keluarga"
}
}

View File

@ -0,0 +1,668 @@
package com.example.lexilearn.ui.views.pQuiz
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
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.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.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
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
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.MyShadowCard
import com.example.lexilearn.ui.theme.ctextGray
import com.example.lexilearn.ui.theme.ctextWhite
import com.google.accompanist.flowlayout.FlowRow
import com.google.accompanist.flowlayout.MainAxisAlignment
import kotlin.math.roundToInt
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun QuizScreen(
navController: NavController,
material: MaterialDataModel,
quizViewModel: QuizViewModel = viewModel()
) {
val question by quizViewModel.currentQuestion.collectAsState()
val isAnswerCorrect by quizViewModel.isAnswerCorrect.collectAsState()
val isTTSInitialized = quizViewModel.isTTSInitialized.observeAsState(false)
LaunchedEffect(Unit) {
quizViewModel.loadRandomQuestion(material)
}
var rectColumnAnswer by remember { mutableStateOf(Rect.Zero) }
val cardSize = remember {
mutableStateMapOf<Int, Dp>()
}
val maxSize = 120.dp
val minSize = 70.dp
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>()
}
val quizYOffset = remember {
mutableStateMapOf<Int, Float>()
}
val boxRectDragable = remember {
mutableStateMapOf<Int, Rect>()
}
val boxRectQuiz = remember {
mutableStateMapOf<Int, Rect>()
}
val answerXOffset = remember {
mutableStateMapOf<Int, Float>()
}
val answerYOffset = remember {
mutableStateMapOf<Int, Float>()
}
val boxRectAnswer = remember {
mutableStateMapOf<Int, Rect>()
}
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = stringResource(id = R.string.spelltitle),
modifier = Modifier.fillMaxSize()
) {
ConstraintLayout {
val buttonRef = createRef()
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
Text(
"Level: 3",
modifier = Modifier.padding(22.dp),
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
color = ctextWhite
)
}
MyShadowCard(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier.fillMaxWidth(),
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)
)
} else if (question?.questionType == QuestionType.TEXT) {
Text(
text = question!!.question,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(12.dp)
)
} else if (question?.questionType == QuestionType.AUDIO) {
IconButton(
onClick = {
quizViewModel.speakLetter(question!!.question)
},
modifier = Modifier
.size(160.dp)
) {
Icon(
imageVector = Icons.Filled.VolumeUp, // 🔥 Ikon Speaker dari Material Icons
contentDescription = "Speaker Icon",
tint = Color.Black // 🔥 Warna ikon
)
}
}
}
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
) {
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,
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)
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) {
Text(
text = dt.data,
color = ctextWhite,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
)
}
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
}
}
Spacer(modifier = Modifier.height(12.dp))
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(ctextGray)
)
Spacer(modifier = Modifier.height(12.dp))
Column(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned {
rectColumnAnswer = it.boundsInWindow()
},
) {
listAnswer.chunked(2).forEach { rowItems ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
rowItems.forEach { item ->
val id = item.id
if (!cardSize.containsKey(id))
cardSize[id] = maxSize
if (!boxRectAnswer.containsKey(id))
boxRectAnswer[id] = Rect.Zero
if (!answerXOffset.containsKey(id))
answerXOffset[id] = 0f
if (!answerYOffset.containsKey(id))
answerYOffset[id] = 0f
DraggableAnswerCard(
item = item.data,
modifier = Modifier
.padding(10.dp)
.size(cardSize[id]!!)
.offset {
IntOffset(
answerXOffset[id]!!.roundToInt(),
answerYOffset[id]!!.roundToInt()
)
}
.onGloballyPositioned { coordinates ->
boxRectAnswer[id] = coordinates.boundsInWindow()
}
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, dragAmount ->
change.consume()
answerXOffset[id] =
answerXOffset[id]!! + dragAmount.x
answerYOffset[id] =
answerYOffset[id]!! + dragAmount.y
cardSize[id] = minSize
},
onDragEnd = {
var checkNull = false
for ((ind, entry) in boxRectQuiz.entries.withIndex()) {
val (_, rect) = entry
if (dataQuiz[ind].hasContent)
continue
if (boxRectAnswer[id]!!.overlaps(
rect
)
) {
cardSize[id] = minSize
quizViewModel.updateQuestionShuffled(
ind,
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
answerYOffset[id] = 0f
break
}
}
if (!checkNull) {
cardSize[id] = maxSize
answerXOffset[id] = 0f
answerYOffset[id] = 0f
}
}
)
}
)
}
}
}
}
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

@ -0,0 +1,120 @@
package com.example.lexilearn.ui.views.pQuiz
import android.app.Application
import android.speech.tts.TextToSpeech
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
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.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import java.util.Locale
class QuizViewModel(application: Application) : AndroidViewModel(application),
TextToSpeech.OnInitListener {
private val quizRepository = QuizRepository()
private val _currentQuestion = MutableStateFlow<QuizQuestion?>(null)
val currentQuestion: StateFlow<QuizQuestion?> = _currentQuestion
private val _userAnswer = MutableStateFlow<String?>(null)
val userAnswer: StateFlow<String?> = _userAnswer
private val _isAnswerCorrect = MutableStateFlow<Boolean?>(null)
val isAnswerCorrect: StateFlow<Boolean?> = _isAnswerCorrect
fun loadNewQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int) {
_currentQuestion.value = quizRepository.generateQuestion(material, questionMode, answerMode)
}
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
fun updateQuestionShuffled(index: Int, newData: ModelSpell) {
_questionShuffled.value = _questionShuffled.value.mapIndexed { i, item ->
if (i == index) {
item.copy(
data = newData.data,
showCard = true,
emp = newData.emp,
hasContent = true
)
} else {
item
}
}
}
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 ->
ModelSpell(index + 1, true, "?", showCard = false)
} ?: emptyList()
_questionShuffled.value = dataQuest
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}")
}
fun submitAnswer(answer: String) {
_userAnswer.value = answer
_isAnswerCorrect.value =
_currentQuestion.value?.correctAnswer?.correctWord?.equals(answer, ignoreCase = true)
}
private var tts: TextToSpeech? = null
private val _isTTSInitialized = MutableLiveData(false)
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
init {
tts = TextToSpeech(application, this)
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
tts?.language = Locale.ENGLISH
_isTTSInitialized.value = true
} else {
_isTTSInitialized.value = false
}
}
fun speakLetter(letter: String) {
if (_isTTSInitialized.value == true) {
tts?.speak(letter, TextToSpeech.QUEUE_FLUSH, null, null)
}
}
override fun onCleared() {
tts?.stop()
tts?.shutdown()
super.onCleared()
}
}

View File

@ -0,0 +1,4 @@
package com.example.lexilearn.ui.views.pQuiz.pComponentsQuiz
class AnswerShuffled {
}

View File

@ -1,9 +1,6 @@
package com.example.lexilearn.ui.views.pQuiz.pRead
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box

View File

@ -1,31 +1,19 @@
package com.example.lexilearn.ui.views.pQuiz.pSpell
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable