update unlock material

This commit is contained in:
DimazzP 2025-02-21 09:32:07 +07:00
parent 522e568a9e
commit 98474010d0
9 changed files with 489 additions and 251 deletions

View File

@ -7,9 +7,11 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.example.lexilearn.data.model.MaterialDataModel import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.model.UserModel import com.example.lexilearn.data.model.UserModel
import com.example.lexilearn.data.repository.MaterialRepository import com.example.lexilearn.data.repository.MaterialRepository
@ -91,6 +93,15 @@ fun MyApp() {
composable("resultscreening") { ResultScreeningScreen(navController) } composable("resultscreening") { ResultScreeningScreen(navController) }
composable("learnAlphabet") { LearnAlphabetScreen(navController) } composable("learnAlphabet") { LearnAlphabetScreen(navController) }
composable("learnNumber") { LearnNumberScreen(navController) } composable("learnNumber") { LearnNumberScreen(navController) }
// composable(
// "navMaterial/{materialId}",
// arguments = listOf(
// navArgument("materialId") { type = NavType.StringType }
// )
// ) { backStackEntry ->
// val materialId = backStackEntry.arguments?.getString("materialId") ?: ""
// NavMaterialScreen(navController, materialId)
// }
composable("navMaterial/{materialId}") { backStackEntry -> composable("navMaterial/{materialId}") { backStackEntry ->
val materialId = backStackEntry.arguments?.getString("materialId") val materialId = backStackEntry.arguments?.getString("materialId")
NavMaterialScreen(navController, materialId ?: "") NavMaterialScreen(navController, materialId ?: "")
@ -99,9 +110,11 @@ fun MyApp() {
val materialList = navController.previousBackStackEntry val materialList = navController.previousBackStackEntry
?.savedStateHandle ?.savedStateHandle
?.get<List<MaterialDataModel>>("materialList") ?.get<List<MaterialDataModel>>("materialList")
val indexValue = navController.previousBackStackEntry
?.savedStateHandle
?.get<Int>("indexValue") ?: -1
if (materialList != null) { if (materialList != null) {
DetailMaterialScreen(navController, materialList) DetailMaterialScreen(navController, materialList, indexValue)
}else{ }else{
println("masuk kondisi else") println("masuk kondisi else")
} }
@ -111,14 +124,21 @@ fun MyApp() {
?.savedStateHandle ?.savedStateHandle
?.get<List<MaterialDataModel>>("selectedMaterialList") ?.get<List<MaterialDataModel>>("selectedMaterialList")
val selectedMaterial = selectedMaterialList?.firstOrNull() val typeQuiz = navController.previousBackStackEntry
?.savedStateHandle
?.get<String>("typeQuiz") ?: "default"
if (selectedMaterial != null) { val indexValue = navController.previousBackStackEntry
QuizScreen(navController, selectedMaterial) ?.savedStateHandle
?.get<Int>("indexValue") ?: 0
if (!selectedMaterialList.isNullOrEmpty()) {
QuizScreen(navController, selectedMaterialList, typeQuiz, indexValue)
} else { } else {
Text("Error: Data tidak tersedia") 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")
// //

View File

@ -65,47 +65,140 @@ class MaterialRepository(private val context: Context) {
} }
fun updateUnlockData(idUnlock: String, value: Int, callback: (Boolean) -> Unit) { fun updateUnlockData(idUnlock: String, value: Int, callback: (Boolean) -> Unit) {
val userId = sharedPrefHelper.getUserId() try {
val userId = sharedPrefHelper.getUserId()
if (userId == null) {
println("printUpdateUnlock: userId is null")
callback(false)
return
}
if (userId == null) { val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock")
callback(false)
return
}
val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock") // Ambil data user dari Firebase dengan try-catch di dalam callback
userRef.get().addOnSuccessListener { snapshot ->
try {
val currentValue = snapshot.getValue(Int::class.java) ?: 0
// 🔥 Ambil data user dari Firebase // Hanya update jika nilai baru lebih besar
userRef.get().addOnSuccessListener { snapshot -> if (value > currentValue) {
val currentValue = snapshot.getValue(Int::class.java) ?: 0 userRef.setValue(value).addOnCompleteListener { task ->
try {
// 🔥 Hanya update jika nilai baru lebih besar if (task.isSuccessful) {
if (value > currentValue) { // Update juga di SharedPreferences
userRef.setValue(value).addOnCompleteListener { task -> val updatedUser = sharedPrefHelper.getUserData()
if (task.isSuccessful) { updatedUser?.unlock_data?.let { unlockData ->
// 🔥 Update juga di SharedPreferences when (idUnlock) {
val updatedUser = sharedPrefHelper.getUserData() "animal" -> unlockData.animal = value
updatedUser?.unlock_data?.let { unlockData -> "family" -> unlockData.family = value
when (idUnlock) { "house" -> unlockData.house = value
"animal" -> unlockData.animal = value "limb" -> unlockData.limb = value
"family" -> unlockData.family = value }
"house" -> unlockData.house = value }
"limb" -> unlockData.limb = value // Pastikan updatedUser tidak null sebelum menyimpan
if (updatedUser != null) {
println("printUpdateUnlock: successfully updated Firebase and SharedPreferences")
sharedPrefHelper.saveUserData(updatedUser)
callback(true)
} else {
println("printUpdateUnlock: updatedUser is null after updating Firebase")
callback(false)
}
} else {
println("printUpdateUnlock: task is not successful")
callback(false)
}
} catch (e: Exception) {
e.printStackTrace()
println("printUpdateUnlock: exception in onCompleteListener: $e")
callback(false)
} }
} }
sharedPrefHelper.saveUserData(updatedUser!!)
callback(true)
} else { } else {
callback(false) println("printUpdateUnlock: new value $value is not greater than current value $currentValue")
callback(false) // Jika nilai baru <= nilai lama, tidak update
} }
} catch (e: Exception) {
e.printStackTrace()
println("printUpdateUnlock: exception in onSuccessListener: $e")
callback(false)
} }
} else { }.addOnFailureListener {
callback(false) // 🔥 Jika nilai baru <= nilai lama, tidak update it.printStackTrace()
println("printUpdateUnlock: failed to get data: ${it.message}")
callback(false)
} }
}.addOnFailureListener { } catch (e: Exception) {
println("printUpdateUnlock: error_update_unlock_data: $e")
e.printStackTrace()
callback(false) callback(false)
} }
} }
// fun updateUnlockData(idUnlock: String, value: Int, callback: (Boolean) -> Unit) {
// try {
// val userId = sharedPrefHelper.getUserId()
// if (userId == null) {
// callback(false)
// return
// }
//
// val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock")
//
// // Ambil data user dari Firebase dengan try-catch di dalam callback
// userRef.get().addOnSuccessListener { snapshot ->
// try {
// val currentValue = snapshot.getValue(Int::class.java) ?: 0
//
// // Hanya update jika nilai baru lebih besar
// if (value > currentValue) {
// userRef.setValue(value).addOnCompleteListener { task ->
// try {
// if (task.isSuccessful) {
// // Update juga di SharedPreferences
// val updatedUser = sharedPrefHelper.getUserData()
// updatedUser?.unlock_data?.let { unlockData ->
// when (idUnlock) {
// "animal" -> unlockData.animal = value
// "family" -> unlockData.family = value
// "house" -> unlockData.house = value
// "limb" -> unlockData.limb = value
// }
// }
// // Pastikan updatedUser tidak null sebelum menyimpan
// if (updatedUser != null) {
// sharedPrefHelper.saveUserData(updatedUser)
// callback(true)
// } else {
// callback(false)
// }
// } else {
// callback(false)
// }
// } catch (e: Exception) {
// e.printStackTrace()
// callback(false)
// }
// }
// } else {
// callback(false) // Jika nilai baru <= nilai lama, tidak update
// }
// } catch (e: Exception) {
// e.printStackTrace()
// callback(false)
// }
// }.addOnFailureListener {
// it.printStackTrace()
// callback(false)
// }
// } catch (e: Exception) {
// println("error_update_unlock_data: $e")
// e.printStackTrace()
// callback(false)
// }
// }
fun getUnlockValue(idUnlock: String, callback: (Int?) -> Unit) { fun getUnlockValue(idUnlock: String, callback: (Int?) -> Unit) {
val userId = sharedPrefHelper.getUserId() val userId = sharedPrefHelper.getUserId()

View File

@ -41,7 +41,7 @@ import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.ctextGray import com.example.lexilearn.ui.theme.ctextGray
@Composable @Composable
fun DetailMaterialScreen(navController: NavController, materialListData: List<MaterialDataModel>) { fun DetailMaterialScreen(navController: NavController, materialListData: List<MaterialDataModel>, indexValue: Int) {
val viewModel: DetailMaterialViewModel = viewModel() val viewModel: DetailMaterialViewModel = viewModel()
LaunchedEffect(materialListData) { LaunchedEffect(materialListData) {
viewModel.fetchMaterial(materialListData) viewModel.fetchMaterial(materialListData)
@ -154,17 +154,19 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List<Ma
} }
ButtonNext( ButtonNext(
onclick = { onclick = {
val selectedMaterial = materialListData.firstOrNull() if (materialListData.isNotEmpty()) {
if (selectedMaterial != null) {
// Simpan ke savedStateHandle sebagai List<MaterialDataModel> tapi hanya satu elemen
navController.currentBackStackEntry navController.currentBackStackEntry
?.savedStateHandle ?.savedStateHandle
?.set("selectedMaterialList", listOf(selectedMaterial)) ?.set("selectedMaterialList", viewModel.randomMaterialModel())
navController.currentBackStackEntry
// Navigasi ke QuizScreen ?.savedStateHandle
?.set("typeQuiz", materialListData[0].category)
navController.currentBackStackEntry
?.savedStateHandle
?.set("indexValue", indexValue)
navController.navigate("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),

View File

@ -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.data.repository.QuizRepository
import com.example.lexilearn.domain.models.ModelSpell 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
@ -20,6 +21,7 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli
private val _isTTSInitialized = MutableLiveData(false) private val _isTTSInitialized = MutableLiveData(false)
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
val alphabetList = generateNumberList() val alphabetList = generateNumberList()
private val quizRepository = QuizRepository()
private val repository = MaterialRepository(application) private val repository = MaterialRepository(application)
@ -46,6 +48,10 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli
} }
} }
fun randomMaterialModel(): List<MaterialDataModel> {
return _materialList.value.shuffled()
}
init { init {
tts = TextToSpeech(application, this) tts = TextToSpeech(application, this)
} }

View File

@ -249,7 +249,7 @@ fun HomeScreen(navController: NavController) {
) )
} }
} }
ButtonHome(onClick = { }, ButtonHome(onClick = { navController.navigate("navMaterial/house") },
color = Color(0xffea2b72), color = Color(0xffea2b72),
modifier = Modifier modifier = Modifier
@ -304,7 +304,7 @@ fun HomeScreen(navController: NavController) {
) )
} }
} }
ButtonHome(onClick = { navController.navigate("read") }, ButtonHome(onClick = { navController.navigate("navMaterial/family") },
color = Color(0xffedb92f), color = Color(0xffedb92f),
modifier = Modifier modifier = Modifier
.constrainAs(newsRef) { .constrainAs(newsRef) {

View File

@ -17,6 +17,9 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur import androidx.compose.ui.draw.blur
@ -44,6 +47,16 @@ fun NavMaterialScreen(navController: NavController, materialId: String) {
viewModel.fetchMaterial(materialId) viewModel.fetchMaterial(materialId)
} }
val numberUnlock by navController.currentBackStackEntry
?.savedStateHandle
?.getLiveData<Int>("numberUnlock")
?.observeAsState(initial = 0) ?: remember { mutableIntStateOf(0) }
LaunchedEffect(numberUnlock) {
println("printUpdateUnlock-nav $numberUnlock")
viewModel.updateUnlockValue(materialId, numberUnlock)
}
val materialList by viewModel.materialList.collectAsState() val materialList by viewModel.materialList.collectAsState()
val textTitle by viewModel.textTitle.observeAsState("") val textTitle by viewModel.textTitle.observeAsState("")
val sizeUnlock by viewModel.sizeUnlock.observeAsState() val sizeUnlock by viewModel.sizeUnlock.observeAsState()
@ -89,6 +102,9 @@ fun NavMaterialScreen(navController: NavController, materialId: String) {
"materialList", "materialList",
chunk chunk
) )
navController.currentBackStackEntry?.savedStateHandle?.set(
"indexValue", index
)
navController.navigate("detailMaterial") navController.navigate("detailMaterial")
} }
}, },

View File

@ -73,6 +73,19 @@ class NavMaterialViewModel(application: Application) : AndroidViewModel(applicat
} }
} }
fun updateUnlockValue(materialId: String, value: Int) {
if(value >= (_sizeUnlock.value ?: 0) && _materialList.value.size > value){
viewModelScope.launch {
repository.updateUnlockData(materialId, value){
println("numberUnlock update unlock value success $it")
fetchMaterial(materialId)
}
}
}else{
println("numberUnlock-else $value, ${_sizeUnlock.value}, ${_materialList.value.size}")
}
}
override fun onCleared() { override fun onCleared() {
tts?.stop() tts?.stop()
tts?.shutdown() tts?.shutdown()

View File

@ -42,6 +42,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.key
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@ -50,6 +51,7 @@ 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.IntOffset import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.zIndex
import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayout
import com.example.lexilearn.R import com.example.lexilearn.R
import com.example.lexilearn.data.model.QuestionType import com.example.lexilearn.data.model.QuestionType
@ -67,9 +69,12 @@ import kotlin.math.roundToInt
@Composable @Composable
fun QuizScreen( fun QuizScreen(
navController: NavController, navController: NavController,
material: MaterialDataModel, materials: List<MaterialDataModel>,
typeQuiz: String,
indexValue: Int,
quizViewModel: QuizViewModel = viewModel() quizViewModel: QuizViewModel = viewModel()
) { ) {
println("testco tipe: ${typeQuiz}, index = ${indexValue}")
val question by quizViewModel.currentQuestion.collectAsState() val question by quizViewModel.currentQuestion.collectAsState()
val isButtonVisible by quizViewModel.isButtonVisible val isButtonVisible by quizViewModel.isButtonVisible
val isAnswerCorrect by quizViewModel.isAnswerCorrect.collectAsState() val isAnswerCorrect by quizViewModel.isAnswerCorrect.collectAsState()
@ -77,16 +82,8 @@ fun QuizScreen(
val snackbarMessage by quizViewModel.snackbarMessage.collectAsState() val snackbarMessage by quizViewModel.snackbarMessage.collectAsState()
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val indexQuiz by quizViewModel.indexQuiz.collectAsState()
LaunchedEffect(Unit) {
quizViewModel.loadRandomQuestion(material)
}
LaunchedEffect(snackbarMessage) {
snackbarMessage?.let { message ->
snackbarHostState.showSnackbar(message)
quizViewModel.onSnackbarShown()
}
}
var rectColumnAnswer by remember { mutableStateOf(Rect.Zero) } var rectColumnAnswer by remember { mutableStateOf(Rect.Zero) }
@ -128,10 +125,50 @@ fun QuizScreen(
mutableStateMapOf<Int, Rect>() mutableStateMapOf<Int, Rect>()
} }
fun clearOffsets() {
boxRectAnswer.clear()
boxRectDragable.clear()
cardSize.clear()
quizXOffset.clear()
quizYOffset.clear()
boxRectQuiz.clear()
answerXOffset.clear()
answerYOffset.clear()
}
LaunchedEffect(Unit) {
quizViewModel.initMaterialData(materials)
}
LaunchedEffect(indexQuiz) {
println("indexQuizValue = $indexQuiz")
if (indexQuiz < materials.size) {
clearOffsets()
quizViewModel.randomQuestion()
} else {
navController.getBackStackEntry("navMaterial/$typeQuiz")
.savedStateHandle
.set("numberUnlock", indexValue + 1)
println("printUpdateUnlock-quiz $indexValue")
// Kemudian kembali ke halaman sebelumnya
navController.popBackStack()
navController.popBackStack()
}
}
LaunchedEffect(snackbarMessage) {
snackbarMessage?.let { message ->
snackbarHostState.showSnackbar(message)
quizViewModel.onSnackbarShown()
}
}
Scaffold( Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) } snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
){innerPadding-> ) { innerPadding ->
Surface(modifier = Modifier.fillMaxSize().padding(innerPadding)) { Surface(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
GradientQuiz( GradientQuiz(
navController = navController, navController = navController,
headerText = stringResource(id = R.string.spelltitle), headerText = stringResource(id = R.string.spelltitle),
@ -205,124 +242,139 @@ fun QuizScreen(
) { ) {
dataQuiz.forEach { dt -> dataQuiz.forEach { dt ->
val id = dt.id key(dt.id) {
if (!boxRectDragable.containsKey(id)) val id = dt.id
boxRectDragable[id] = Rect.Zero if (!boxRectDragable.containsKey(id))
if (!boxRectQuiz.containsKey(id)) boxRectDragable[id] = Rect.Zero
boxRectQuiz[id] = Rect.Zero if (!boxRectQuiz.containsKey(id))
if (!quizXOffset.containsKey(id)) boxRectQuiz[id] = Rect.Zero
quizXOffset[id] = 0f if (!quizXOffset.containsKey(id))
if (!quizYOffset.containsKey(id)) quizXOffset[id] = 0f
quizYOffset[id] = 0f if (!quizYOffset.containsKey(id))
quizYOffset[id] = 0f
CardQuiz( CardQuiz(
modifier = Modifier modifier = Modifier
.padding(vertical = 10.dp) .zIndex(zIndex = 1f)
.size(minSize) .padding(vertical = 10.dp)
.onGloballyPositioned { coordinates -> .size(minSize)
if (dt.type) .onGloballyPositioned { coordinates ->
boxRectQuiz[id] = coordinates.boundsInWindow() if (dt.type)
} boxRectQuiz[id] =
) { coordinates.boundsInWindow()
if (dt.type) { }
Text( ) {
text = dt.data, if (dt.type) {
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(
text = dt.data, text = dt.data,
color = ctextWhite, color = ctextWhite,
fontSize = 20.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center, 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,
)
}
} }
} }
} }
@ -353,76 +405,79 @@ fun QuizScreen(
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
rowItems.forEach { item -> rowItems.forEach { item ->
val id = item.id key(item.id) {
if (!cardSize.containsKey(id)) val id = item.id
cardSize[id] = maxSize if (!cardSize.containsKey(id))
if (!boxRectAnswer.containsKey(id)) cardSize[id] = maxSize
boxRectAnswer[id] = Rect.Zero if (!boxRectAnswer.containsKey(id))
if (!answerXOffset.containsKey(id)) boxRectAnswer[id] = Rect.Zero
answerXOffset[id] = 0f if (!answerXOffset.containsKey(id))
if (!answerYOffset.containsKey(id)) answerXOffset[id] = 0f
answerYOffset[id] = 0f if (!answerYOffset.containsKey(id))
DraggableAnswerCard( answerYOffset[id] = 0f
item = item.data, DraggableAnswerCard(
modifier = Modifier item = item.data,
.padding(4.dp) modifier = Modifier
.size(cardSize[id]!!) .padding(4.dp)
.offset { .size(cardSize[id]!!)
IntOffset( .offset {
answerXOffset[id]!!.roundToInt(), IntOffset(
answerYOffset[id]!!.roundToInt() answerXOffset[id]!!.roundToInt(),
) answerYOffset[id]!!.roundToInt()
} )
.onGloballyPositioned { coordinates -> }
boxRectAnswer[id] = coordinates.boundsInWindow() .onGloballyPositioned { coordinates ->
} boxRectAnswer[id] =
.pointerInput(Unit) { coordinates.boundsInWindow()
detectDragGestures( }
onDrag = { change, dragAmount -> .pointerInput(Unit) {
change.consume() detectDragGestures(
answerXOffset[id] = onDrag = { change, dragAmount ->
answerXOffset[id]!! + dragAmount.x change.consume()
answerYOffset[id] = answerXOffset[id] =
answerYOffset[id]!! + dragAmount.y answerXOffset[id]!! + dragAmount.x
cardSize[id] = minSize answerYOffset[id] =
}, answerYOffset[id]!! + dragAmount.y
onDragEnd = { cardSize[id] = minSize
var checkNull = false },
for ((ind, entry) in boxRectQuiz.entries.withIndex()) { onDragEnd = {
val (_, rect) = entry var checkNull = false
if (dataQuiz[ind].hasContent) for ((ind, entry) in boxRectQuiz.entries.withIndex()) {
continue val (_, rect) = entry
if (dataQuiz[ind].hasContent)
continue
if (boxRectAnswer[id]!!.overlaps( if (boxRectAnswer[id]!!.overlaps(
rect rect
)
) {
cardSize[id] = minSize
quizViewModel.updateQuestionShuffled(
ind,
dataQuiz[ind].copy(
data = item.data,
showCard = true,
emp = id,
hasContent = true
) )
) ) {
checkNull = true cardSize[id] = minSize
cardSize[id] = 0.dp quizViewModel.updateQuestionShuffled(
ind,
dataQuiz[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 answerXOffset[id] = 0f
answerYOffset[id] = 0f answerYOffset[id] = 0f
break
} }
} }
if (!checkNull) { )
cardSize[id] = maxSize }
answerXOffset[id] = 0f )
answerYOffset[id] = 0f }
}
}
)
}
)
} }
} }
} }

View File

@ -24,6 +24,9 @@ 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 val _currentQuestion = MutableStateFlow<QuizQuestion?>(null) private val _currentQuestion = MutableStateFlow<QuizQuestion?>(null)
val currentQuestion: StateFlow<QuizQuestion?> = _currentQuestion val currentQuestion: StateFlow<QuizQuestion?> = _currentQuestion
@ -33,6 +36,14 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
private val _isAnswerCorrect = MutableStateFlow<Boolean?>(null) private val _isAnswerCorrect = MutableStateFlow<Boolean?>(null)
val isAnswerCorrect: StateFlow<Boolean?> = _isAnswerCorrect val isAnswerCorrect: StateFlow<Boolean?> = _isAnswerCorrect
private val _indexQuiz = MutableStateFlow(0)
val indexQuiz: StateFlow<Int> = _indexQuiz
// Update indexQuiz saat jawaban benar
fun incrementIndexQuiz() {
_indexQuiz.value += 1
}
fun loadNewQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int) { fun loadNewQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int) {
_currentQuestion.value = quizRepository.generateQuestion(material, questionMode, answerMode) _currentQuestion.value = quizRepository.generateQuestion(material, questionMode, answerMode)
} }
@ -63,29 +74,50 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
} }
fun loadRandomQuestion(material: MaterialDataModel) { fun initMaterialData(material: List<MaterialDataModel>) {
_currentQuestion.value = quizRepository.generateQuestion(material, 2, 2) materialList = material;
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()
val shuflledAnswer = listAnswer.shuffled()
_shuffledAnswerLetters.value = shuflledAnswer
// Menambahkan delay 1 detik sebelum memanggil observeData()
// viewModelScope.launch {
// delay(1000L) // delay 1 detik
//
// }
} }
// 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
val dataQuest = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char ->
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 ->
ModelAnswerRead(
id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan
data = char.toString()
)
} ?: emptyList()
_shuffledAnswerLetters.value = listAnswer.shuffled()
}
fun submitAnswer(answer: String) { fun submitAnswer(answer: String) {
_userAnswer.value = answer _userAnswer.value = answer
@ -139,6 +171,7 @@ class QuizViewModel(application: Application) : AndroidViewModel(application),
val checkData = checkAnswer(answerString); val checkData = checkAnswer(answerString);
if (checkData) { if (checkData) {
triggerSnackbar("Jawaban Benar") triggerSnackbar("Jawaban Benar")
incrementIndexQuiz()
} else { } else {
triggerSnackbar("Jawaban Salah") triggerSnackbar("Jawaban Salah")
} }