diff --git a/app/src/main/java/com/example/lexilearn/MainActivity.kt b/app/src/main/java/com/example/lexilearn/MainActivity.kt index fc66079..79f51c1 100644 --- a/app/src/main/java/com/example/lexilearn/MainActivity.kt +++ b/app/src/main/java/com/example/lexilearn/MainActivity.kt @@ -15,6 +15,7 @@ import androidx.navigation.navArgument import com.example.lexilearn.data.model.MaterialDataModel import com.example.lexilearn.data.model.UserDataModel import com.example.lexilearn.data.repository.UserRepository +import com.example.lexilearn.ui.theme.LexiLearnTheme import com.example.lexilearn.ui.views.pDetailMaterial.DetailMaterialScreen import com.example.lexilearn.ui.views.pHome.HomeScreen import com.example.lexilearn.ui.views.pLearAlphabet.LearnAlphabetScreen @@ -75,7 +76,9 @@ class MainActivity : ComponentActivity() { } } setContent { - MyApp() + LexiLearnTheme { + MyApp() // Menggunakan MyApp dengan tema LexiLearn + } } } } diff --git a/app/src/main/java/com/example/lexilearn/data/remote/FirebaseHelper.kt b/app/src/main/java/com/example/lexilearn/data/remote/FirebaseHelper.kt index d21382a..085ca12 100644 --- a/app/src/main/java/com/example/lexilearn/data/remote/FirebaseHelper.kt +++ b/app/src/main/java/com/example/lexilearn/data/remote/FirebaseHelper.kt @@ -18,29 +18,30 @@ class FirebaseHelper(private val context: Context) { // 🔥 Fungsi untuk mengambil data berdasarkan filterMaterial (kode lama, tetap dipertahankan) fun fetchMaterials(filterMaterial: String, callback: (List) -> Unit) { - val databaseReference: DatabaseReference = - FirebaseDatabase.getInstance().getReference("data/$filterMaterial") +val databaseReference: DatabaseReference = +FirebaseDatabase.getInstance().getReference("data/$filterMaterial") - databaseReference.addListenerForSingleValueEvent(object : ValueEventListener { - override fun onDataChange(snapshot: DataSnapshot) { - val materialList = mutableListOf() - for (materialSnapshot in snapshot.children) { - val material = materialSnapshot.getValue(MaterialDataModel::class.java) - material?.let { materialList.add(it) } - } +databaseReference.addListenerForSingleValueEvent(object : ValueEventListener { +override fun onDataChange(snapshot: DataSnapshot) { +val materialList = mutableListOf() +for (materialSnapshot in snapshot.children) { + val material = materialSnapshot.getValue(MaterialDataModel::class.java) + material?.let { materialList.add(it) } +} - sharedPrefHelper.saveMaterialList(filterMaterial, materialList) - callback(materialList) - } +sharedPrefHelper.saveMaterialList(filterMaterial, materialList) +callback(materialList) +} - override fun onCancelled(error: DatabaseError) { - Log.e("FirebaseHelper", "Error: ${error.message}") - } - }) +override fun onCancelled(error: DatabaseError) { +Log.e("FirebaseHelper", "Error: ${error.message}") +} +}) } fun fetchAllMaterials(callback: (List) -> Unit) { - val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("data") + val databaseReference: DatabaseReference = + FirebaseDatabase.getInstance().getReference("data") databaseReference.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { @@ -102,8 +103,10 @@ class FirebaseHelper(private val context: Context) { // }) // } fun loginUser(callback: (Boolean, String?) -> Unit) { - val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users") - val userId = sharedPrefHelper.getUserId() // Ambil ID pengguna yang disimpan di SharedPreferences + val databaseReference: DatabaseReference = + FirebaseDatabase.getInstance().getReference("users") + val userId = + sharedPrefHelper.getUserId() // Ambil ID pengguna yang disimpan di SharedPreferences if (userId.isNullOrEmpty()) { callback(false, null) // Jika tidak ada ID tersimpan @@ -145,20 +148,19 @@ class FirebaseHelper(private val context: Context) { } } - fun updateUser(updatedUser: UserDataModel, callback: (Boolean) -> Unit) { - val getUserData = sharedPrefHelper.getUserData() - val userId = sharedPrefHelper.getUserId() // Ambil ID pengguna yang disimpan di SharedPreferences - val databaseReference = FirebaseDatabase.getInstance().getReference("users") - val newData = mapOf( - "name" to updatedUser.name, - "age" to updatedUser.age, - ) - if(userId!=null){ - databaseReference.child(userId).updateChildren(newData).addOnCompleteListener { task -> - callback(task.isSuccessful) - } - } - } +fun updateUser(updatedUser: UserDataModel, callback: (Boolean) -> Unit) { +val userId = sharedPrefHelper.getUserId() +val databaseReference = FirebaseDatabase.getInstance().getReference("users") +val newData = mapOf( +"name" to updatedUser.name, +"age" to updatedUser.age, +) +if (userId != null) { +databaseReference.child(userId).updateChildren(newData).addOnCompleteListener { task -> +callback(task.isSuccessful) +} +} +} // ✅ 🔥 Fungsi Fetch User Data Berdasarkan ID (Dapatkan data user setelah login) fun fetchUserData(callback: (UserDataModel?) -> Unit) { @@ -169,7 +171,8 @@ class FirebaseHelper(private val context: Context) { return } - val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users/$userId") + val databaseReference: DatabaseReference = + FirebaseDatabase.getInstance().getReference("users/$userId") databaseReference.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { @@ -183,6 +186,7 @@ class FirebaseHelper(private val context: Context) { } }) } + fun fetchTop10Scores(callback: (List>) -> Unit) { val todayDate = getTodayDate() val databaseReference: DatabaseReference = diff --git a/app/src/main/java/com/example/lexilearn/data/repository/QuizRepository.kt b/app/src/main/java/com/example/lexilearn/data/repository/QuizRepository.kt index d09a77f..f5bb030 100644 --- a/app/src/main/java/com/example/lexilearn/data/repository/QuizRepository.kt +++ b/app/src/main/java/com/example/lexilearn/data/repository/QuizRepository.kt @@ -10,52 +10,38 @@ 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 - } +fun generateQuestion( + material: MaterialDataModel, + questionMode: Int, + answerMode: Int +): QuizQuestion { + val questionType = when (questionMode) { + 1 -> listOf(QuestionType.TEXT, QuestionType.IMAGE).random() + 2 -> QuestionType.AUDIO + else -> QuestionType.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 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 - } + 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 - ) + 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) - } + ) +} fun getRandomMaterials( nData: Int, diff --git a/app/src/main/java/com/example/lexilearn/data/repository/UserRepository.kt b/app/src/main/java/com/example/lexilearn/data/repository/UserRepository.kt index 2bb0b4a..c1b674e 100644 --- a/app/src/main/java/com/example/lexilearn/data/repository/UserRepository.kt +++ b/app/src/main/java/com/example/lexilearn/data/repository/UserRepository.kt @@ -40,7 +40,8 @@ class UserRepository(private val context: Context) { return } - val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock") + val userRef = + FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock") userRef.get().addOnSuccessListener { snapshot -> val currentValue = snapshot.getValue(Int::class.java) ?: 0 @@ -85,7 +86,8 @@ class UserRepository(private val context: Context) { return } - val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock") + val userRef = + FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock") userRef.get().addOnSuccessListener { snapshot -> callback(snapshot.getValue(Int::class.java) ?: 0) }.addOnFailureListener { diff --git a/app/src/main/java/com/example/lexilearn/ui/theme/Type.kt b/app/src/main/java/com/example/lexilearn/ui/theme/Type.kt index 3c291ae..5949201 100644 --- a/app/src/main/java/com/example/lexilearn/ui/theme/Type.kt +++ b/app/src/main/java/com/example/lexilearn/ui/theme/Type.kt @@ -7,31 +7,31 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp import com.example.lexilearn.R +val _fredokaOne = FontFamily( + Font(R.font.fredoka) // Pastikan nama file sesuai +) // Set of Material typography styles to start with val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily(Font(R.font.inter)), - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ + + displayLarge = TextStyle(fontFamily = _fredokaOne, fontSize = 57.sp), // Gaya displayLarge + displayMedium = TextStyle(fontFamily = _fredokaOne, fontSize = 45.sp), // Gaya displayMedium + displaySmall = TextStyle(fontFamily = _fredokaOne, fontSize = 36.sp), // Gaya displaySmall + + headlineLarge = TextStyle(fontFamily = _fredokaOne, fontSize = 32.sp), // Gaya headlineLarge + headlineMedium = TextStyle(fontFamily = _fredokaOne, fontSize = 28.sp), // Gaya headlineMedium + headlineSmall = TextStyle(fontFamily = _fredokaOne, fontSize = 24.sp), // Gaya headlineSmall + + titleLarge = TextStyle(fontFamily = _fredokaOne, fontSize = 22.sp), // Gaya titleLarge + titleMedium = TextStyle(fontFamily = _fredokaOne, fontSize = 20.sp), // Gaya titleMedium + titleSmall = TextStyle(fontFamily = _fredokaOne, fontSize = 18.sp), // Gaya titleSmall + + bodyLarge = TextStyle(fontFamily = _fredokaOne, fontSize = 16.sp), // Gaya bodyLarge + bodyMedium = TextStyle(fontFamily = _fredokaOne, fontSize = 14.sp), // Gaya bodyMedium + bodySmall = TextStyle(fontFamily = _fredokaOne, fontSize = 12.sp), // Gaya bodySmall + + labelLarge = TextStyle(fontFamily = _fredokaOne, fontSize = 14.sp), // Gaya labelLarge + labelMedium = TextStyle(fontFamily = _fredokaOne, fontSize = 12.sp), // Gaya labelMedium + labelSmall = TextStyle(fontFamily = _fredokaOne, fontSize = 10.sp) // Gaya labelSm ) 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 4e41e81..1c6d484 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,11 @@ import com.example.lexilearn.ui.theme.ctextBlack import com.example.lexilearn.ui.theme.ctextGray @Composable -fun DetailMaterialScreen(navController: NavController, materialListData: List, indexValue: Int) { +fun DetailMaterialScreen( + navController: NavController, + materialListData: List, + indexValue: Int +) { val viewModel: DetailMaterialViewModel = viewModel() LaunchedEffect(materialListData) { viewModel.fetchMaterial(materialListData) @@ -98,8 +102,8 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List() - for (scoreSnapshot in snapshot.children) { val scoreData = scoreSnapshot.getValue(CompetitionScore::class.java) scoreData?.let { scoreList.add(it) } } - - // Urutkan dari yang tertinggi ke terendah _topScores.value = scoreList.sortedByDescending { it.score } } diff --git a/app/src/main/java/com/example/lexilearn/ui/views/pLearnNumber/LearnNumberScreen.kt b/app/src/main/java/com/example/lexilearn/ui/views/pLearnNumber/LearnNumberScreen.kt index c82c038..2c27782 100644 --- a/app/src/main/java/com/example/lexilearn/ui/views/pLearnNumber/LearnNumberScreen.kt +++ b/app/src/main/java/com/example/lexilearn/ui/views/pLearnNumber/LearnNumberScreen.kt @@ -30,7 +30,7 @@ fun LearnNumberScreen(navController: NavController) { Surface(modifier = Modifier.fillMaxSize()) { GradientQuiz( navController = navController, - headerText = "Belajar Alfabet Inggris", + headerText = "Belajar Angka Inggris", modifier = Modifier.fillMaxSize() ) { Column( 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 969b852..d22ca56 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 @@ -45,7 +45,6 @@ class NavMaterialViewModel(application: Application) : AndroidViewModel(applicat _sizeUnlock.value = value } repository.getMaterialData(materialId) { materials -> -// val filteredMaterials = materials.filter { it.category == materialId } _materialList.value = materials.chunked(3) if (materials[0].category == "animal") { _textTitle.value = "hewan" 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 bb7b695..86edab7 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 @@ -53,12 +53,14 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.zIndex import androidx.constraintlayout.compose.ConstraintLayout import com.example.lexilearn.data.model.QuestionType +import com.example.lexilearn.domain.models.ModelQuestion 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.cAccent +import com.example.lexilearn.ui.theme.ctextBlack import com.example.lexilearn.ui.theme.ctextGray import com.example.lexilearn.ui.theme.ctextWhite import com.google.accompanist.flowlayout.FlowRow @@ -73,7 +75,6 @@ fun QuizScreen( 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() @@ -222,7 +223,7 @@ fun QuizScreen( horizontalArrangement = Arrangement.SpaceBetween ) { Text( - text = "Score: $liveCurrentScore", + text = "Point: $liveCurrentScore", color = cAccent, fontSize = 18.sp, fontWeight = FontWeight.Bold @@ -256,6 +257,7 @@ fun QuizScreen( Text( text = question!!.question, fontSize = 20.sp, + color = ctextBlack, fontWeight = FontWeight.Bold, modifier = Modifier.padding(12.dp) ) @@ -366,10 +368,7 @@ fun QuizScreen( rect ) ) { - quizViewModel.updateQuestionShuffled( - ind, - dt - ) + val newDt = dt.copy() dt.apply { hasContent = false @@ -377,6 +376,10 @@ fun QuizScreen( false data = "?" } + quizViewModel.updateQuestionShuffled( + ind, + newDt + ) checkNull = true quizXOffset[id] = 0f @@ -431,6 +434,7 @@ fun QuizScreen( } } } + println("--------") } Spacer(modifier = Modifier.height(12.dp)) } 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 4557ef5..d596f86 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 @@ -2,7 +2,6 @@ package com.example.lexilearn.ui.views.pQuiz import android.app.Application import android.speech.tts.TextToSpeech -import android.util.Log import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.AndroidViewModel @@ -46,10 +45,6 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), _indexQuiz.value += 1 } - fun loadNewQuestion(material: MaterialDataModel, questionMode: Int, answerMode: Int) { - _currentQuestion.value = quizRepository.generateQuestion(material, questionMode, answerMode) - } - private val _shuffledAnswerLetters = MutableStateFlow>(emptyList()) val shuffledAnswerLetters: StateFlow> = _shuffledAnswerLetters @@ -101,7 +96,7 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), val listAnswer = _currentQuestion.value?.correctAnswer?.correctWord?.mapIndexed { index, char -> ModelAnswer( - id = (_indexQuiz.value * 100) + index + 1, // ID unik per pertanyaan + id = (_indexQuiz.value * 100) + index + 1, data = char.toString().lowercase() ) } ?: emptyList() @@ -135,24 +130,21 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), } fun checkAnswer(answerString: String): Boolean { - if (answerString.lowercase() == currentQuestion.value?.correctAnswer?.correctWord?.lowercase()) { - return true - } - return false + return (answerString.lowercase() == currentQuestion.value?.correctAnswer?.correctWord?.lowercase()) } fun observeData() { viewModelScope.launch { _questionShuffled.collect { newList -> - // Lakukan aksi setiap kali data berubah var answerString = "" newList.forEach { text -> answerString = answerString + text.data } if (answerString.contains("?") || answerString.isEmpty() || answerString.contains("null")) { + println("if-$answerString") _isButtonVisible.value = false } else { - Log.d("cobaaja", answerString) + println("else-$answerString") val checkData = checkAnswer(answerString); if (checkData) { triggerSnackbar("Jawaban Benar") @@ -185,6 +177,8 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), super.onCleared() } + // competition + private val _totalScore = MutableStateFlow(0) val totalScore: StateFlow get() = _totalScore.asStateFlow() @@ -288,12 +282,11 @@ class QuizViewModel(application: Application) : AndroidViewModel(application), val penaltyTime = max(0, responseTime - safeTime) * timePenalty val penaltyWrong = wrongAttempts * wrongPenalty var score = baseScore - penaltyTime - penaltyWrong - - if (responseTime <= 3) { + if (responseTime <= 5) { +// println("responset: $responseTime") score += bonusQuick } return max(minScore, score) } - } \ No newline at end of file diff --git a/app/src/main/res/font/fredoka.ttf b/app/src/main/res/font/fredoka.ttf new file mode 100644 index 0000000..3afd362 Binary files /dev/null and b/app/src/main/res/font/fredoka.ttf differ