From 98474010d0dd980ec05fe4e348bfdad8fd389d5a Mon Sep 17 00:00:00 2001 From: DimazzP Date: Fri, 21 Feb 2025 09:32:07 +0700 Subject: [PATCH] update unlock material --- .../com/example/lexilearn/MainActivity.kt | 30 +- .../data/repository/MaterialRepository.kt | 149 ++++-- .../pDetailMaterial/DetailMaterialScreen.kt | 18 +- .../DetailMaterialViewModel.kt | 6 + .../lexilearn/ui/views/pHome/HomeScreen.kt | 4 +- .../views/pNavMaterial/NavMaterialScreen.kt | 16 + .../pNavMaterial/NavMaterialViewModel.kt | 13 + .../lexilearn/ui/views/pQuiz/QuizScreen.kt | 429 ++++++++++-------- .../lexilearn/ui/views/pQuiz/QuizViewModel.kt | 75 ++- 9 files changed, 489 insertions(+), 251 deletions(-) diff --git a/app/src/main/java/com/example/lexilearn/MainActivity.kt b/app/src/main/java/com/example/lexilearn/MainActivity.kt index 504c66c..b0c366f 100644 --- a/app/src/main/java/com/example/lexilearn/MainActivity.kt +++ b/app/src/main/java/com/example/lexilearn/MainActivity.kt @@ -7,9 +7,11 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument import com.example.lexilearn.data.model.MaterialDataModel import com.example.lexilearn.data.model.UserModel import com.example.lexilearn.data.repository.MaterialRepository @@ -91,6 +93,15 @@ fun MyApp() { composable("resultscreening") { ResultScreeningScreen(navController) } composable("learnAlphabet") { LearnAlphabetScreen(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 -> val materialId = backStackEntry.arguments?.getString("materialId") NavMaterialScreen(navController, materialId ?: "") @@ -99,9 +110,11 @@ fun MyApp() { val materialList = navController.previousBackStackEntry ?.savedStateHandle ?.get>("materialList") - + val indexValue = navController.previousBackStackEntry + ?.savedStateHandle + ?.get("indexValue") ?: -1 if (materialList != null) { - DetailMaterialScreen(navController, materialList) + DetailMaterialScreen(navController, materialList, indexValue) }else{ println("masuk kondisi else") } @@ -111,14 +124,21 @@ fun MyApp() { ?.savedStateHandle ?.get>("selectedMaterialList") - val selectedMaterial = selectedMaterialList?.firstOrNull() + val typeQuiz = navController.previousBackStackEntry + ?.savedStateHandle + ?.get("typeQuiz") ?: "default" - if (selectedMaterial != null) { - QuizScreen(navController, selectedMaterial) + val indexValue = navController.previousBackStackEntry + ?.savedStateHandle + ?.get("indexValue") ?: 0 + + if (!selectedMaterialList.isNullOrEmpty()) { + QuizScreen(navController, selectedMaterialList, typeQuiz, indexValue) } else { Text("Error: Data tidak tersedia") } } + // composable("detailMaterial/{materialJson}") { backStackEntry -> // val jsonMaterial = backStackEntry.arguments?.getString("materialJson") // diff --git a/app/src/main/java/com/example/lexilearn/data/repository/MaterialRepository.kt b/app/src/main/java/com/example/lexilearn/data/repository/MaterialRepository.kt index ab1db58..bc89911 100644 --- a/app/src/main/java/com/example/lexilearn/data/repository/MaterialRepository.kt +++ b/app/src/main/java/com/example/lexilearn/data/repository/MaterialRepository.kt @@ -65,47 +65,140 @@ class MaterialRepository(private val context: Context) { } 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) { - callback(false) - return - } + val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock") - 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 - userRef.get().addOnSuccessListener { snapshot -> - val currentValue = snapshot.getValue(Int::class.java) ?: 0 - - // 🔥 Hanya update jika nilai baru lebih besar - if (value > currentValue) { - userRef.setValue(value).addOnCompleteListener { task -> - 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 + // 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) { + 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 { - 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 { - callback(false) // 🔥 Jika nilai baru <= nilai lama, tidak update + }.addOnFailureListener { + 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) } } +// 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) { val userId = sharedPrefHelper.getUserId() diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialScreen.kt b/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialScreen.kt index 5d07318..4e41e81 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialScreen.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialScreen.kt @@ -41,7 +41,7 @@ import com.example.lexilearn.ui.theme.ctextBlack import com.example.lexilearn.ui.theme.ctextGray @Composable -fun DetailMaterialScreen(navController: NavController, materialListData: List) { +fun DetailMaterialScreen(navController: NavController, materialListData: List, indexValue: Int) { val viewModel: DetailMaterialViewModel = viewModel() LaunchedEffect(materialListData) { viewModel.fetchMaterial(materialListData) @@ -154,17 +154,19 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List tapi hanya satu elemen + if (materialListData.isNotEmpty()) { navController.currentBackStackEntry ?.savedStateHandle - ?.set("selectedMaterialList", listOf(selectedMaterial)) - - // Navigasi ke QuizScreen + ?.set("selectedMaterialList", viewModel.randomMaterialModel()) + navController.currentBackStackEntry + ?.savedStateHandle + ?.set("typeQuiz", materialListData[0].category) + navController.currentBackStackEntry + ?.savedStateHandle + ?.set("indexValue", indexValue) navController.navigate("quizScreen") } -// navController.navigate("spell") + }, text = "Kerjakan Kuis", painter = painterResource(id = R.drawable.ic_next), diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialViewModel.kt b/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialViewModel.kt index aca5322..d084d89 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialViewModel.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pDetailMaterial/DetailMaterialViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.example.lexilearn.data.model.MaterialDataModel import com.example.lexilearn.data.repository.MaterialRepository +import com.example.lexilearn.data.repository.QuizRepository import com.example.lexilearn.domain.models.ModelSpell import com.example.lexilearn.utils.generateNumberList import kotlinx.coroutines.flow.MutableStateFlow @@ -20,6 +21,7 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli private val _isTTSInitialized = MutableLiveData(false) val isTTSInitialized: LiveData = _isTTSInitialized val alphabetList = generateNumberList() + private val quizRepository = QuizRepository() private val repository = MaterialRepository(application) @@ -46,6 +48,10 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli } } + fun randomMaterialModel(): List { + return _materialList.value.shuffled() + } + init { tts = TextToSpeech(application, this) } diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pHome/HomeScreen.kt b/app/src/main/java/com/example/lexilearn/ui/views/pHome/HomeScreen.kt index e653b6b..d25befb 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pHome/HomeScreen.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pHome/HomeScreen.kt @@ -249,7 +249,7 @@ fun HomeScreen(navController: NavController) { ) } } - ButtonHome(onClick = { }, + ButtonHome(onClick = { navController.navigate("navMaterial/house") }, color = Color(0xffea2b72), modifier = Modifier @@ -304,7 +304,7 @@ fun HomeScreen(navController: NavController) { ) } } - ButtonHome(onClick = { navController.navigate("read") }, + ButtonHome(onClick = { navController.navigate("navMaterial/family") }, color = Color(0xffedb92f), modifier = Modifier .constrainAs(newsRef) { diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialScreen.kt b/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialScreen.kt index 9de01ea..012c1d9 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialScreen.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialScreen.kt @@ -17,6 +17,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue 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.Modifier import androidx.compose.ui.draw.blur @@ -44,6 +47,16 @@ fun NavMaterialScreen(navController: NavController, materialId: String) { viewModel.fetchMaterial(materialId) } + val numberUnlock by navController.currentBackStackEntry + ?.savedStateHandle + ?.getLiveData("numberUnlock") + ?.observeAsState(initial = 0) ?: remember { mutableIntStateOf(0) } + + LaunchedEffect(numberUnlock) { + println("printUpdateUnlock-nav $numberUnlock") + viewModel.updateUnlockValue(materialId, numberUnlock) + } + val materialList by viewModel.materialList.collectAsState() val textTitle by viewModel.textTitle.observeAsState("") val sizeUnlock by viewModel.sizeUnlock.observeAsState() @@ -89,6 +102,9 @@ fun NavMaterialScreen(navController: NavController, materialId: String) { "materialList", chunk ) + navController.currentBackStackEntry?.savedStateHandle?.set( + "indexValue", index + ) navController.navigate("detailMaterial") } }, diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialViewModel.kt b/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialViewModel.kt index 3727dcf..9cedba4 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialViewModel.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pNavMaterial/NavMaterialViewModel.kt @@ -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() { tts?.stop() tts?.shutdown() diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizScreen.kt b/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizScreen.kt index 6e9dec5..21fdb8a 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizScreen.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Surface +import androidx.compose.runtime.key import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableStateMapOf 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.unit.Dp import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.zIndex import androidx.constraintlayout.compose.ConstraintLayout import com.example.lexilearn.R import com.example.lexilearn.data.model.QuestionType @@ -67,9 +69,12 @@ import kotlin.math.roundToInt @Composable fun QuizScreen( navController: NavController, - material: MaterialDataModel, + materials: List, + typeQuiz: String, + indexValue: Int, quizViewModel: QuizViewModel = viewModel() ) { + println("testco tipe: ${typeQuiz}, index = ${indexValue}") val question by quizViewModel.currentQuestion.collectAsState() val isButtonVisible by quizViewModel.isButtonVisible val isAnswerCorrect by quizViewModel.isAnswerCorrect.collectAsState() @@ -77,16 +82,8 @@ fun QuizScreen( val snackbarMessage by quizViewModel.snackbarMessage.collectAsState() 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) } @@ -128,10 +125,50 @@ fun QuizScreen( mutableStateMapOf() } + 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( snackbarHost = { SnackbarHost(hostState = snackbarHostState) } - ){innerPadding-> - Surface(modifier = Modifier.fillMaxSize().padding(innerPadding)) { + ) { innerPadding -> + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { GradientQuiz( navController = navController, headerText = stringResource(id = R.string.spelltitle), @@ -205,124 +242,139 @@ fun QuizScreen( ) { 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 + key(dt.id) { + 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) { + CardQuiz( + modifier = Modifier + .zIndex(zIndex = 1f) + .padding(vertical = 10.dp) + .size(minSize) + .onGloballyPositioned { coordinates -> + if (dt.type) + boxRectQuiz[id] = + coordinates.boundsInWindow() + } + ) { + if (dt.type) { Text( text = dt.data, color = ctextWhite, - fontSize = 20.sp, 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, + ) + } } } } @@ -353,76 +405,79 @@ fun QuizScreen( 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(4.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 + key(item.id) { + 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(4.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 + if (boxRectAnswer[id]!!.overlaps( + rect ) - ) - checkNull = true - cardSize[id] = 0.dp + ) { + cardSize[id] = minSize + 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 answerYOffset[id] = 0f - break } } - if (!checkNull) { - cardSize[id] = maxSize - answerXOffset[id] = 0f - answerYOffset[id] = 0f - } - } - ) - } - ) + ) + } + ) + } } } } diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizViewModel.kt b/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizViewModel.kt index af64482..9f294fc 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizViewModel.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pQuiz/QuizViewModel.kt @@ -24,6 +24,9 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), TextToSpeech.OnInitListener { private val quizRepository = QuizRepository() + + private var materialList: List = emptyList() + private val _currentQuestion = MutableStateFlow(null) val currentQuestion: StateFlow = _currentQuestion @@ -33,6 +36,14 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), private val _isAnswerCorrect = MutableStateFlow(null) val isAnswerCorrect: StateFlow = _isAnswerCorrect + private val _indexQuiz = MutableStateFlow(0) + val indexQuiz: StateFlow = _indexQuiz + + // Update indexQuiz saat jawaban benar + fun incrementIndexQuiz() { + _indexQuiz.value += 1 + } + fun loadNewQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int) { _currentQuestion.value = quizRepository.generateQuestion(material, questionMode, answerMode) } @@ -63,29 +74,50 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), } - fun loadRandomQuestion(material: MaterialDataModel) { - _currentQuestion.value = quizRepository.generateQuestion(material, 2, 2) - - val dataQuest = - _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char -> - ModelSpell(index + 1, true, "?", showCard = false) - } ?: emptyList() - _questionShuffled.value = dataQuest - - val listAnswer = - _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char -> - 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 initMaterialData(material: List) { + 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 + 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) { _userAnswer.value = answer @@ -139,6 +171,7 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), val checkData = checkAnswer(answerString); if (checkData) { triggerSnackbar("Jawaban Benar") + incrementIndexQuiz() } else { triggerSnackbar("Jawaban Salah") }