update quiz
This commit is contained in:
parent
afb61f8cac
commit
133fa954fd
|
@ -71,6 +71,7 @@ dependencies {
|
||||||
implementation("com.google.accompanist:accompanist-navigation-material:0.24.13-rc")
|
implementation("com.google.accompanist:accompanist-navigation-material:0.24.13-rc")
|
||||||
implementation("io.coil-kt:coil-compose:2.1.0")
|
implementation("io.coil-kt:coil-compose:2.1.0")
|
||||||
implementation ("com.google.code.gson:gson:2.10.1")
|
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(platform("com.google.firebase:firebase-bom:32.0.0"))
|
||||||
implementation("com.google.firebase:firebase-analytics")
|
implementation("com.google.firebase:firebase-analytics")
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
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.pNavMaterial.NavMaterialScreen
|
||||||
import com.example.lexilearn.ui.views.pLearnNumber.LearnNumberScreen
|
import com.example.lexilearn.ui.views.pLearnNumber.LearnNumberScreen
|
||||||
import com.example.lexilearn.ui.views.pLogin.LoginScreen
|
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.pRead.ReadScreen
|
||||||
import com.example.lexilearn.ui.views.pQuiz.pSpell.SpellScreen
|
import com.example.lexilearn.ui.views.pQuiz.pSpell.SpellScreen
|
||||||
import com.example.lexilearn.ui.views.pQuiz.pWrite.WriteScreen
|
import com.example.lexilearn.ui.views.pQuiz.pWrite.WriteScreen
|
||||||
|
@ -104,7 +106,19 @@ fun MyApp() {
|
||||||
println("masuk kondisi else")
|
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 ->
|
// composable("detailMaterial/{materialJson}") { backStackEntry ->
|
||||||
// val jsonMaterial = backStackEntry.arguments?.getString("materialJson")
|
// val jsonMaterial = backStackEntry.arguments?.getString("materialJson")
|
||||||
//
|
//
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.example.lexilearn.data.model
|
||||||
|
|
||||||
|
enum class AnswerType {
|
||||||
|
FULL_WORD, SHUFFLED_LETTERS
|
||||||
|
}
|
|
@ -10,12 +10,4 @@ data class MaterialDataModel (
|
||||||
@SerializedName("enDescription") val enDescription: String = "",
|
@SerializedName("enDescription") val enDescription: String = "",
|
||||||
@SerializedName("image") val image: String = "",
|
@SerializedName("image") val image: String = "",
|
||||||
@SerializedName("category") val category: 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 = ""
|
|
||||||
|
|
||||||
)
|
)
|
|
@ -0,0 +1,5 @@
|
||||||
|
package com.example.lexilearn.data.model
|
||||||
|
|
||||||
|
enum class QuestionType {
|
||||||
|
TEXT, IMAGE, AUDIO
|
||||||
|
}
|
|
@ -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
|
||||||
|
)
|
|
@ -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
|
||||||
|
)
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
package com.example.lexilearn.ui.views.pDetailMaterial
|
package com.example.lexilearn.ui.views.pDetailMaterial
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
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.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
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.GradientQuiz
|
||||||
import com.example.lexilearn.ui.components.HorizontalLine
|
import com.example.lexilearn.ui.components.HorizontalLine
|
||||||
import com.example.lexilearn.ui.theme.cAccent
|
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.ctextBlack
|
||||||
import com.example.lexilearn.ui.theme.ctextGray
|
import com.example.lexilearn.ui.theme.ctextGray
|
||||||
import com.example.lexilearn.ui.theme.cwhite
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailMaterialScreen(navController: NavController, materialListData: List<MaterialDataModel>) {
|
fun DetailMaterialScreen(navController: NavController, materialListData: List<MaterialDataModel>) {
|
||||||
|
@ -159,7 +154,17 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List<Ma
|
||||||
}
|
}
|
||||||
ButtonNext(
|
ButtonNext(
|
||||||
onclick = {
|
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",
|
text = "Kerjakan Kuis",
|
||||||
painter = painterResource(id = R.drawable.ic_next),
|
painter = painterResource(id = R.drawable.ic_next),
|
||||||
|
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.example.lexilearn.data.model.MaterialDataModel
|
import com.example.lexilearn.data.model.MaterialDataModel
|
||||||
import com.example.lexilearn.data.repository.MaterialRepository
|
import com.example.lexilearn.data.repository.MaterialRepository
|
||||||
|
import com.example.lexilearn.domain.models.ModelSpell
|
||||||
import com.example.lexilearn.utils.generateNumberList
|
import com.example.lexilearn.utils.generateNumberList
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
@ -22,12 +23,14 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli
|
||||||
|
|
||||||
private val repository = MaterialRepository(application)
|
private val repository = MaterialRepository(application)
|
||||||
|
|
||||||
|
|
||||||
private val _materialList = MutableStateFlow<List<MaterialDataModel>>(emptyList())
|
private val _materialList = MutableStateFlow<List<MaterialDataModel>>(emptyList())
|
||||||
val materialList: StateFlow<List<MaterialDataModel>> = _materialList
|
val materialList: StateFlow<List<MaterialDataModel>> = _materialList
|
||||||
|
|
||||||
private val _textTitle = MutableLiveData("Hello, Jetpack Compose!")
|
private val _textTitle = MutableLiveData("Hello, Jetpack Compose!")
|
||||||
val textTitle: LiveData<String> = _textTitle
|
val textTitle: LiveData<String> = _textTitle
|
||||||
|
|
||||||
|
|
||||||
fun fetchMaterial(materialListData: List<MaterialDataModel>) {
|
fun fetchMaterial(materialListData: List<MaterialDataModel>) {
|
||||||
_materialList.value = materialListData
|
_materialList.value = materialListData
|
||||||
if (materialListData.isNotEmpty()) {
|
if (materialListData.isNotEmpty()) {
|
||||||
|
|
|
@ -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)
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package com.example.lexilearn.ui.views.pQuiz.pComponentsQuiz
|
||||||
|
|
||||||
|
class AnswerShuffled {
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
package com.example.lexilearn.ui.views.pQuiz.pRead
|
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.gestures.detectDragGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
|
@ -1,31 +1,19 @@
|
||||||
package com.example.lexilearn.ui.views.pQuiz.pSpell
|
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.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.gestures.detectDragGestures
|
import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
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.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.offset
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
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.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
Loading…
Reference in New Issue