update competition
This commit is contained in:
parent
98474010d0
commit
b354d2c24b
|
@ -33,7 +33,7 @@ class SharedPrefHelper(context: Context) {
|
||||||
fun saveMaterialList(filterMaterial: String, materialList: List<MaterialDataModel>) {
|
fun saveMaterialList(filterMaterial: String, materialList: List<MaterialDataModel>) {
|
||||||
val editor = sharedPreferences.edit()
|
val editor = sharedPreferences.edit()
|
||||||
val json = Gson().toJson(materialList)
|
val json = Gson().toJson(materialList)
|
||||||
editor.putString("material_list_$filterMaterial", json)
|
editor.putString(" $filterMaterial", json)
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,28 @@ class SharedPrefHelper(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getCombinedMaterialList(): List<MaterialDataModel> {
|
||||||
|
val categories = listOf("animal", "limb", "family", "house")
|
||||||
|
val combinedList = mutableListOf<MaterialDataModel>()
|
||||||
|
|
||||||
|
for (category in categories) {
|
||||||
|
getMaterialList(category)?.let { combinedList.addAll(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return combinedList
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRandomMaterials(nData: Int): List<MaterialDataModel> {
|
||||||
|
val combinedList = getCombinedMaterialList()
|
||||||
|
|
||||||
|
return if (combinedList.size <= nData) {
|
||||||
|
combinedList // Jika jumlah data kurang dari nData, kembalikan semua
|
||||||
|
} else {
|
||||||
|
combinedList.shuffled().take(nData) // Acak dan ambil sejumlah nData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fun clearMaterialList(filterMaterial: String) {
|
fun clearMaterialList(filterMaterial: String) {
|
||||||
sharedPreferences.edit().remove("material_list_$filterMaterial").apply()
|
sharedPreferences.edit().remove("material_list_$filterMaterial").apply()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.example.lexilearn.data.model
|
||||||
|
|
||||||
|
sealed class QuizState {
|
||||||
|
data class QuestionLoaded(val question: MaterialDataModel, val totalScore: Int) : QuizState()
|
||||||
|
data class AnswerCorrect(val score: Int, val totalScore: Int) : QuizState()
|
||||||
|
data class AnswerWrong(val wrongAttempts: Int) : QuizState()
|
||||||
|
data class QuizFinished(val totalScore: Int) : QuizState()
|
||||||
|
data class Error(val message: String) : QuizState()
|
||||||
|
}
|
|
@ -36,6 +36,44 @@ class FirebaseHelper(private val context: Context) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun fetchAllMaterials(callback: (List<MaterialDataModel>) -> Unit) {
|
||||||
|
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("data")
|
||||||
|
|
||||||
|
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
||||||
|
override fun onDataChange(snapshot: DataSnapshot) {
|
||||||
|
val allMaterials = mutableListOf<MaterialDataModel>()
|
||||||
|
|
||||||
|
for (categorySnapshot in snapshot.children) { // Loop setiap kategori (animal, limb, dll.)
|
||||||
|
val categoryKey = categorySnapshot.key ?: continue
|
||||||
|
val materialList = mutableListOf<MaterialDataModel>()
|
||||||
|
|
||||||
|
for (materialSnapshot in categorySnapshot.children) { // Loop setiap item dalam kategori
|
||||||
|
val material = materialSnapshot.getValue(MaterialDataModel::class.java)
|
||||||
|
material?.let {
|
||||||
|
allMaterials.add(it) // Tambahkan ke list utama
|
||||||
|
materialList.add(it) // Tambahkan ke list per kategori
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simpan berdasarkan kategori (misalnya "animal", "limb", dll.)
|
||||||
|
sharedPrefHelper.saveMaterialList(categoryKey, materialList)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simpan semua data ke SharedPreferences dengan key "all"
|
||||||
|
sharedPrefHelper.saveMaterialList("all", allMaterials)
|
||||||
|
|
||||||
|
// Callback dengan seluruh data sebagai satu List<MaterialDataModel>
|
||||||
|
callback(allMaterials)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancelled(error: DatabaseError) {
|
||||||
|
Log.e("FirebaseHelper", "Error fetching all materials: ${error.message}")
|
||||||
|
callback(emptyList()) // Jika terjadi error, kembalikan list kosong
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ✅ 🔥 Fungsi Login - Mencari user berdasarkan email dan password
|
// ✅ 🔥 Fungsi Login - Mencari user berdasarkan email dan password
|
||||||
fun loginUser(email: String, password: String, callback: (Boolean, String?) -> Unit) {
|
fun loginUser(email: String, password: String, callback: (Boolean, String?) -> Unit) {
|
||||||
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users")
|
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users")
|
||||||
|
|
|
@ -27,6 +27,22 @@ class MaterialRepository(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllMaterialData(filterMaterial: String, callback: (List<MaterialDataModel>) -> Unit) {
|
||||||
|
val cachedData =
|
||||||
|
sharedPrefHelper.getMaterialList(filterMaterial) // Ambil data dengan filter
|
||||||
|
if (cachedData != null) {
|
||||||
|
callback(cachedData)
|
||||||
|
} else {
|
||||||
|
firebaseHelper.fetchAllMaterials { materials ->
|
||||||
|
sharedPrefHelper.saveMaterialList(
|
||||||
|
filterMaterial,
|
||||||
|
materials
|
||||||
|
)
|
||||||
|
callback(materials)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun createUser(user: UserModel, callback: (Boolean, String?) -> Unit) {
|
fun createUser(user: UserModel, callback: (Boolean, String?) -> Unit) {
|
||||||
val databaseReference = FirebaseDatabase.getInstance().getReference("users")
|
val databaseReference = FirebaseDatabase.getInstance().getReference("users")
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,11 @@ class QuizRepository {
|
||||||
/**
|
/**
|
||||||
* Fungsi ini akan membuat soal dengan mode soal & jawaban yang DITENTUKAN oleh parameter.
|
* Fungsi ini akan membuat soal dengan mode soal & jawaban yang DITENTUKAN oleh parameter.
|
||||||
*/
|
*/
|
||||||
fun generateQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int): QuizQuestion {
|
fun generateQuestion(
|
||||||
|
material: MaterialDataModel,
|
||||||
|
questionMode: Int,
|
||||||
|
answerMode: Int
|
||||||
|
): QuizQuestion {
|
||||||
val questionType = when (questionMode) {
|
val questionType = when (questionMode) {
|
||||||
1 -> listOf(QuestionType.TEXT, QuestionType.IMAGE).random() // Text atau Image
|
1 -> listOf(QuestionType.TEXT, QuestionType.IMAGE).random() // Text atau Image
|
||||||
2 -> QuestionType.AUDIO // Hanya Audio
|
2 -> QuestionType.AUDIO // Hanya Audio
|
||||||
|
@ -47,8 +51,23 @@ class QuizRepository {
|
||||||
*/
|
*/
|
||||||
fun generateRandomQuestion(material: MaterialDataModel): QuizQuestion {
|
fun generateRandomQuestion(material: MaterialDataModel): QuizQuestion {
|
||||||
val randomQuestionMode = Random.nextInt(1, 3) // Acak antara 1 (Text/Image) atau 2 (Audio)
|
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)
|
val randomAnswerMode =
|
||||||
|
Random.nextInt(1, 3) // Acak antara 1 (FULL_WORD) atau 2 (SHUFFLED_LETTERS)
|
||||||
|
|
||||||
return generateQuestion(material, randomQuestionMode, randomAnswerMode)
|
return generateQuestion(material, randomQuestionMode, randomAnswerMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getRandomMaterials(
|
||||||
|
nData: Int,
|
||||||
|
materials: List<MaterialDataModel>,
|
||||||
|
callback: (List<MaterialDataModel>) -> Unit
|
||||||
|
) {
|
||||||
|
if (materials.isNotEmpty()) {
|
||||||
|
val randomMaterials = materials.shuffled().take(nData)
|
||||||
|
callback(randomMaterials)
|
||||||
|
} else {
|
||||||
|
callback(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -3,15 +3,21 @@ package com.example.lexilearn.ui.views.pHome
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
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.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.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
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.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.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
@ -35,6 +41,7 @@ import androidx.navigation.NavController
|
||||||
import com.example.lexilearn.R
|
import com.example.lexilearn.R
|
||||||
import com.example.lexilearn.ui.components.AutoSizeText
|
import com.example.lexilearn.ui.components.AutoSizeText
|
||||||
import com.example.lexilearn.ui.components.ButtonHome
|
import com.example.lexilearn.ui.components.ButtonHome
|
||||||
|
import com.example.lexilearn.ui.theme.cAccent
|
||||||
import com.example.lexilearn.ui.theme.cGray
|
import com.example.lexilearn.ui.theme.cGray
|
||||||
import com.example.lexilearn.ui.theme.cTextPrimary
|
import com.example.lexilearn.ui.theme.cTextPrimary
|
||||||
import com.example.lexilearn.ui.theme.cprimary
|
import com.example.lexilearn.ui.theme.cprimary
|
||||||
|
@ -48,6 +55,7 @@ import com.example.lexilearn.ui.theme.cwhite
|
||||||
fun HomeScreen(navController: NavController) {
|
fun HomeScreen(navController: NavController) {
|
||||||
val viewModel: HomeViewModel = viewModel()
|
val viewModel: HomeViewModel = viewModel()
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
val leaderBoard = listOf("Tono (987)", "Budi (965)")
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
@ -116,24 +124,80 @@ fun HomeScreen(navController: NavController) {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Column(Modifier.padding(14.dp)) {
|
Row(
|
||||||
Row(
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
modifier = Modifier
|
||||||
verticalAlignment = Alignment.Bottom,
|
.fillMaxWidth()
|
||||||
modifier = Modifier.fillMaxWidth()
|
.padding(14.dp)
|
||||||
) {
|
) {
|
||||||
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = "Materi Terselesaikan",
|
text = "Leaderboard hari ini :",
|
||||||
color = ctextWhite,
|
color = ctextWhite,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
fontSize = 16.sp
|
fontSize = 16.sp
|
||||||
)
|
)
|
||||||
Image(
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
painter = painterResource(id = R.drawable.bg_children),
|
LazyColumn {
|
||||||
contentDescription = "image",
|
itemsIndexed(leaderBoard) { index, player ->
|
||||||
modifier = Modifier.size(80.dp)
|
Text(
|
||||||
)
|
text = "${index + 1}. $player",
|
||||||
|
color = cAccent,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(
|
||||||
|
color = cprimary,
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
.width(160.dp)
|
||||||
|
.clickable {
|
||||||
|
viewModel.prepareCompetitionQuiz {dataMaterial->
|
||||||
|
navController.currentBackStackEntry
|
||||||
|
?.savedStateHandle
|
||||||
|
?.set("selectedMaterialList", dataMaterial)
|
||||||
|
navController.currentBackStackEntry
|
||||||
|
?.savedStateHandle
|
||||||
|
?.set("typeQuiz", "competition")
|
||||||
|
navController.currentBackStackEntry
|
||||||
|
?.savedStateHandle
|
||||||
|
?.set("indexValue", 0)
|
||||||
|
navController.navigate("quizScreen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.padding(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Competition",
|
||||||
|
color = cAccent,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 16.sp
|
||||||
|
)
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.trophy),
|
||||||
|
contentDescription = "image",
|
||||||
|
modifier = Modifier
|
||||||
|
.size(60.dp)
|
||||||
|
.padding(4.dp)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "Tantang dirimu dan raih peringkat tertinggi di leaderboard hari ini!",
|
||||||
|
color = ctextWhite,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
fontSize = 9.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
package com.example.lexilearn.ui.views.pHome
|
package com.example.lexilearn.ui.views.pHome
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.lifecycle.AndroidViewModel
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.example.lexilearn.data.model.MaterialDataModel
|
||||||
|
import com.example.lexilearn.data.model.QuizState
|
||||||
|
import com.example.lexilearn.data.repository.MaterialRepository
|
||||||
|
import com.example.lexilearn.data.repository.QuizRepository
|
||||||
|
|
||||||
class HomeViewModel: ViewModel() {
|
class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
private val _materialRepository = MaterialRepository(application)
|
||||||
|
private val _quizRepository = QuizRepository()
|
||||||
|
|
||||||
|
fun prepareCompetitionQuiz(callback: (List<MaterialDataModel>) -> Unit) {
|
||||||
|
_materialRepository.getAllMaterialData("all") { materials ->
|
||||||
|
_quizRepository.getRandomMaterials(10, materials) { randomData ->
|
||||||
|
randomData.forEach { println(it) }
|
||||||
|
callback(randomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -96,6 +96,7 @@ fun QuizScreen(
|
||||||
val minSize = 70.dp
|
val minSize = 70.dp
|
||||||
val dataQuiz by quizViewModel.questionShuffled.collectAsState()
|
val dataQuiz by quizViewModel.questionShuffled.collectAsState()
|
||||||
val listAnswer by quizViewModel.shuffledAnswerLetters.collectAsState()
|
val listAnswer by quizViewModel.shuffledAnswerLetters.collectAsState()
|
||||||
|
val score by quizViewModel.totalScore.observeAsState(0)
|
||||||
|
|
||||||
val quizXOffset = remember {
|
val quizXOffset = remember {
|
||||||
mutableStateMapOf<Int, Float>()
|
mutableStateMapOf<Int, Float>()
|
||||||
|
@ -138,21 +139,23 @@ fun QuizScreen(
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
quizViewModel.initMaterialData(materials)
|
quizViewModel.initMaterialData(materials)
|
||||||
|
quizViewModel.startQuiz(materials)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(indexQuiz) {
|
LaunchedEffect(indexQuiz) {
|
||||||
println("indexQuizValue = $indexQuiz")
|
|
||||||
if (indexQuiz < materials.size) {
|
if (indexQuiz < materials.size) {
|
||||||
clearOffsets()
|
clearOffsets()
|
||||||
quizViewModel.randomQuestion()
|
quizViewModel.randomQuestion()
|
||||||
} else {
|
} else {
|
||||||
navController.getBackStackEntry("navMaterial/$typeQuiz")
|
if (typeQuiz != "competition") {
|
||||||
.savedStateHandle
|
navController.getBackStackEntry("navMaterial/$typeQuiz")
|
||||||
.set("numberUnlock", indexValue + 1)
|
.savedStateHandle
|
||||||
println("printUpdateUnlock-quiz $indexValue")
|
.set("numberUnlock", indexValue + 1)
|
||||||
// Kemudian kembali ke halaman sebelumnya
|
println("printUpdateUnlock-quiz $indexValue")
|
||||||
navController.popBackStack()
|
// Kemudian kembali ke halaman sebelumnya
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
|
navController.popBackStack()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LaunchedEffect(snackbarMessage) {
|
LaunchedEffect(snackbarMessage) {
|
||||||
|
@ -171,7 +174,7 @@ fun QuizScreen(
|
||||||
) {
|
) {
|
||||||
GradientQuiz(
|
GradientQuiz(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
headerText = stringResource(id = R.string.spelltitle),
|
headerText = "${typeQuiz}-(${score})",
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
ConstraintLayout {
|
ConstraintLayout {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.example.lexilearn.data.model.MaterialDataModel
|
import com.example.lexilearn.data.model.MaterialDataModel
|
||||||
import com.example.lexilearn.data.model.QuizQuestion
|
import com.example.lexilearn.data.model.QuizQuestion
|
||||||
|
import com.example.lexilearn.data.model.QuizState
|
||||||
import com.example.lexilearn.data.repository.QuizRepository
|
import com.example.lexilearn.data.repository.QuizRepository
|
||||||
import com.example.lexilearn.domain.models.ModelAnswerRead
|
import com.example.lexilearn.domain.models.ModelAnswerRead
|
||||||
import com.example.lexilearn.domain.models.ModelSpell
|
import com.example.lexilearn.domain.models.ModelSpell
|
||||||
|
@ -19,12 +20,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
class QuizViewModel(application: Application) : AndroidViewModel(application),
|
class QuizViewModel(application: Application) : AndroidViewModel(application),
|
||||||
TextToSpeech.OnInitListener {
|
TextToSpeech.OnInitListener {
|
||||||
private val quizRepository = QuizRepository()
|
private val quizRepository = QuizRepository()
|
||||||
|
|
||||||
|
|
||||||
private var materialList: List<MaterialDataModel> = emptyList()
|
private var materialList: List<MaterialDataModel> = emptyList()
|
||||||
|
|
||||||
private val _currentQuestion = MutableStateFlow<QuizQuestion?>(null)
|
private val _currentQuestion = MutableStateFlow<QuizQuestion?>(null)
|
||||||
|
@ -78,53 +79,35 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
|
||||||
materialList = material;
|
materialList = material;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fun randomQuestion() {
|
|
||||||
// _currentQuestion.value = quizRepository.generateQuestion(materialList[_indexQuiz.value], 2, 2)
|
|
||||||
// val dataQuest =
|
|
||||||
// _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
|
||||||
// ModelSpell(index + 1, true, data = "coba $char", showCard = false)
|
|
||||||
// } ?: emptyList()
|
|
||||||
// _questionShuffled.value = dataQuest
|
|
||||||
//
|
|
||||||
// val listAnswer =
|
|
||||||
// _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
|
||||||
// ModelAnswerRead(index + 1, char.toString())
|
|
||||||
// } ?: emptyList()
|
|
||||||
// val shuflledAnswer = listAnswer.shuffled()
|
|
||||||
// _shuffledAnswerLetters.value = shuflledAnswer
|
|
||||||
// println("testlistanswer ${_shuffledAnswerLetters.value}")
|
|
||||||
// }
|
|
||||||
// Di dalam fungsi randomQuestion() pada QuizViewModel
|
|
||||||
fun randomQuestion() {
|
|
||||||
_currentQuestion.value = quizRepository.generateQuestion(materialList[_indexQuiz.value], 2, 2)
|
|
||||||
|
|
||||||
// Generate unique IDs berdasarkan indexQuiz dan indeks karakter
|
fun randomQuestion() {
|
||||||
val dataQuest = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
val randomQuestionMode = Random.nextInt(1, 3)
|
||||||
ModelSpell(
|
|
||||||
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
|
|
||||||
type = true,
|
|
||||||
data = "?",
|
|
||||||
showCard = false
|
|
||||||
)
|
|
||||||
} ?: emptyList()
|
|
||||||
_questionShuffled.value = dataQuest
|
|
||||||
|
|
||||||
val listAnswer = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
_currentQuestion.value =
|
||||||
ModelAnswerRead(
|
quizRepository.generateQuestion(materialList[_indexQuiz.value], randomQuestionMode, 2)
|
||||||
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
|
|
||||||
data = char.toString()
|
|
||||||
)
|
|
||||||
} ?: emptyList()
|
|
||||||
_shuffledAnswerLetters.value = listAnswer.shuffled()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
val dataQuest =
|
||||||
|
_currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||||
|
ModelSpell(
|
||||||
|
id = (_indexQuiz.value * 100) + index + 1,
|
||||||
|
type = true,
|
||||||
|
data = "?",
|
||||||
|
showCard = false
|
||||||
|
)
|
||||||
|
} ?: emptyList()
|
||||||
|
_questionShuffled.value = dataQuest
|
||||||
|
|
||||||
fun submitAnswer(answer: String) {
|
val listAnswer =
|
||||||
_userAnswer.value = answer
|
_currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
|
||||||
_isAnswerCorrect.value =
|
ModelAnswerRead(
|
||||||
_currentQuestion.value?.correctAnswer?.correctWord?.equals(answer, ignoreCase = true)
|
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
|
||||||
|
data = char.toString()
|
||||||
|
)
|
||||||
|
} ?: emptyList()
|
||||||
|
_shuffledAnswerLetters.value = listAnswer.shuffled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private var tts: TextToSpeech? = null
|
private var tts: TextToSpeech? = null
|
||||||
private val _isTTSInitialized = MutableLiveData(false)
|
private val _isTTSInitialized = MutableLiveData(false)
|
||||||
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
|
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
|
||||||
|
@ -171,8 +154,10 @@ fun randomQuestion() {
|
||||||
val checkData = checkAnswer(answerString);
|
val checkData = checkAnswer(answerString);
|
||||||
if (checkData) {
|
if (checkData) {
|
||||||
triggerSnackbar("Jawaban Benar")
|
triggerSnackbar("Jawaban Benar")
|
||||||
|
submitAnswer(true)
|
||||||
incrementIndexQuiz()
|
incrementIndexQuiz()
|
||||||
} else {
|
} else {
|
||||||
|
submitAnswer(false)
|
||||||
triggerSnackbar("Jawaban Salah")
|
triggerSnackbar("Jawaban Salah")
|
||||||
}
|
}
|
||||||
_isButtonVisible.value = true
|
_isButtonVisible.value = true
|
||||||
|
@ -197,4 +182,90 @@ fun randomQuestion() {
|
||||||
tts?.shutdown()
|
tts?.shutdown()
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// competition ====================================================================
|
||||||
|
private val _quizState = MutableLiveData<QuizState>()
|
||||||
|
val quizState: LiveData<QuizState> get() = _quizState
|
||||||
|
|
||||||
|
private val _currentScore = MutableLiveData(0) // Skor untuk soal saat ini
|
||||||
|
val currentScore: LiveData<Int> get() = _currentScore
|
||||||
|
|
||||||
|
private val _totalScore = MutableLiveData(0) // Total skor sepanjang sesi
|
||||||
|
val totalScore: LiveData<Int> get() = _totalScore
|
||||||
|
|
||||||
|
private var questionList = listOf<MaterialDataModel>()
|
||||||
|
private var currentQuestionIndex = 0
|
||||||
|
private var wrongAttempts = 0
|
||||||
|
private var startTime: Long = 0
|
||||||
|
|
||||||
|
// Konstanta untuk scoring
|
||||||
|
private val baseScore = 100
|
||||||
|
private val safeTime = 5
|
||||||
|
private val timePenalty = 3
|
||||||
|
private val wrongPenalty = 5
|
||||||
|
private val minScore = 50
|
||||||
|
private val bonusQuick = 10
|
||||||
|
|
||||||
|
/** 1️⃣ Memulai Kuis **/
|
||||||
|
fun startQuiz(materials: List<MaterialDataModel>) {
|
||||||
|
if (materials.isNotEmpty()) {
|
||||||
|
questionList = materials
|
||||||
|
currentQuestionIndex = 0
|
||||||
|
_totalScore.value = 0 // Reset skor saat mulai
|
||||||
|
_currentScore.value = 0
|
||||||
|
wrongAttempts = 0
|
||||||
|
startTime = System.currentTimeMillis()
|
||||||
|
_quizState.value =
|
||||||
|
QuizState.QuestionLoaded(questionList[currentQuestionIndex], _totalScore.value ?: 0)
|
||||||
|
} else {
|
||||||
|
_quizState.value = QuizState.Error("Tidak ada soal tersedia")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 2️⃣ Dipanggil saat jawaban dikirim **/
|
||||||
|
fun submitAnswer(isCorrect: Boolean) {
|
||||||
|
val responseTime =
|
||||||
|
((System.currentTimeMillis() - startTime) / 1000).toInt() // Hitung waktu otomatis
|
||||||
|
|
||||||
|
if (isCorrect) {
|
||||||
|
val score = calculateScore(responseTime, wrongAttempts)
|
||||||
|
_currentScore.value = score
|
||||||
|
_totalScore.value = (_totalScore.value ?: 0) + score // Update total skor
|
||||||
|
moveToNextQuestion()
|
||||||
|
} else {
|
||||||
|
wrongAnswer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 3️⃣ Dipanggil saat jawaban salah **/
|
||||||
|
fun wrongAnswer() {
|
||||||
|
wrongAttempts += 1
|
||||||
|
_quizState.value = QuizState.AnswerWrong(wrongAttempts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 4️⃣ Berganti ke soal berikutnya **/
|
||||||
|
private fun moveToNextQuestion() {
|
||||||
|
if (currentQuestionIndex < questionList.size - 1) {
|
||||||
|
currentQuestionIndex++
|
||||||
|
wrongAttempts = 0
|
||||||
|
startTime = System.currentTimeMillis()
|
||||||
|
_quizState.value =
|
||||||
|
QuizState.QuestionLoaded(questionList[currentQuestionIndex], _totalScore.value ?: 0)
|
||||||
|
} else {
|
||||||
|
_quizState.value = QuizState.QuizFinished(_totalScore.value ?: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 🔢 Perhitungan Skor **/
|
||||||
|
private fun calculateScore(responseTime: Int, wrongAttempts: Int): Int {
|
||||||
|
val penaltyTime = maxOf(0, responseTime - safeTime) * timePenalty
|
||||||
|
val penaltyWrong = wrongAttempts * wrongPenalty
|
||||||
|
var score = baseScore - penaltyTime - penaltyWrong
|
||||||
|
|
||||||
|
if (responseTime <= 3) {
|
||||||
|
score += bonusQuick
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxOf(minScore, score)
|
||||||
|
}
|
||||||
}
|
}
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
Loading…
Reference in New Issue