Update New Feature
This commit is contained in:
parent
0cdd33997d
commit
3824e79c2a
|
@ -1,6 +1,8 @@
|
||||||
package com.example.lexilearn
|
package com.example.lexilearn
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -8,6 +10,8 @@ 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 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.repository.MaterialRepository
|
||||||
import com.example.lexilearn.ui.views.pDetailMaterial.DetailMaterialScreen
|
import com.example.lexilearn.ui.views.pDetailMaterial.DetailMaterialScreen
|
||||||
import com.example.lexilearn.ui.views.pHome.HomeScreen
|
import com.example.lexilearn.ui.views.pHome.HomeScreen
|
||||||
import com.example.lexilearn.ui.views.pLearAlphabet.LearnAlphabetScreen
|
import com.example.lexilearn.ui.views.pLearAlphabet.LearnAlphabetScreen
|
||||||
|
@ -21,13 +25,49 @@ import com.example.lexilearn.ui.views.pRegister.RegisterScreen
|
||||||
import com.example.lexilearn.ui.views.pResultScreening.ResultScreeningScreen
|
import com.example.lexilearn.ui.views.pResultScreening.ResultScreeningScreen
|
||||||
import com.example.lexilearn.ui.views.pScreening.ScreeningScreen
|
import com.example.lexilearn.ui.views.pScreening.ScreeningScreen
|
||||||
import com.example.lexilearn.ui.views.pSplashcreen.SplashScreen
|
import com.example.lexilearn.ui.views.pSplashcreen.SplashScreen
|
||||||
import com.google.gson.Gson
|
|
||||||
import com.google.gson.reflect.TypeToken
|
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
val repository = MaterialRepository(this)
|
||||||
|
|
||||||
|
// 🔥 Default email & password
|
||||||
|
val email = "example@gmail.com"
|
||||||
|
val password = "example123"
|
||||||
|
|
||||||
|
// 🔥 Coba login
|
||||||
|
repository.loginUser(email, password) { success, userId ->
|
||||||
|
if (success) {
|
||||||
|
Toast.makeText(this, "Login Berhasil! ID: $userId", Toast.LENGTH_SHORT).show()
|
||||||
|
Log.d("MainActivity", "Login Berhasil! User ID: $userId")
|
||||||
|
} else {
|
||||||
|
Log.e("MainActivity", "User tidak ditemukan, membuat akun baru...")
|
||||||
|
|
||||||
|
// 🔥 Jika tidak ditemukan, buat user baru
|
||||||
|
val newUser = UserModel(
|
||||||
|
email = email,
|
||||||
|
name = "Example User",
|
||||||
|
password = password,
|
||||||
|
unlock_data = UserModel.UnlockData()
|
||||||
|
)
|
||||||
|
|
||||||
|
repository.createUser(newUser) { success, newUserId ->
|
||||||
|
if (success) {
|
||||||
|
Toast.makeText(this, "User Baru Dibuat! Login Sekarang...", Toast.LENGTH_SHORT).show()
|
||||||
|
repository.loginUser(email, password) { loginSuccess, loggedInUserId ->
|
||||||
|
if (loginSuccess) {
|
||||||
|
Log.d("MainActivity", "Login Berhasil setelah buat akun! User ID: $loggedInUserId")
|
||||||
|
} else {
|
||||||
|
Log.e("MainActivity", "Login Gagal setelah membuat akun!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.e("MainActivity", "Gagal membuat user baru!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
setContent {
|
setContent {
|
||||||
MyApp()
|
MyApp()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,42 @@ package com.example.lexilearn.data.local
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import com.example.lexilearn.data.model.MaterialDataModel
|
import com.example.lexilearn.data.model.MaterialDataModel
|
||||||
|
import com.example.lexilearn.data.model.UserModel
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
class SharedPrefHelper(context: Context) {
|
class SharedPrefHelper(context: Context) {
|
||||||
|
|
||||||
private val sharedPreferences: SharedPreferences =
|
private val sharedPreferences: SharedPreferences =
|
||||||
context.getSharedPreferences("MaterialDataPrefs", Context.MODE_PRIVATE)
|
context.getSharedPreferences("UserPrefs", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
fun saveMaterialList(materialList: List<MaterialDataModel>) {
|
// 🔥 Simpan User ID setelah login
|
||||||
|
fun saveUserId(userId: String) {
|
||||||
val editor = sharedPreferences.edit()
|
val editor = sharedPreferences.edit()
|
||||||
val json = Gson().toJson(materialList)
|
editor.putString("user_id", userId)
|
||||||
editor.putString("material_list", json)
|
|
||||||
editor.apply()
|
editor.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMaterialList(): List<MaterialDataModel>? {
|
// 🔥 Ambil User ID yang tersimpan
|
||||||
val json = sharedPreferences.getString("material_list", null)
|
fun getUserId(): String? {
|
||||||
|
return sharedPreferences.getString("user_id", null)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 Hapus data user saat logout
|
||||||
|
fun clearUserData() {
|
||||||
|
sharedPreferences.edit().clear().apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 Fungsi SharedPreferences untuk menyimpan daftar material (kode lama, tetap dipertahankan)
|
||||||
|
fun saveMaterialList(filterMaterial: String, materialList: List<MaterialDataModel>) {
|
||||||
|
val editor = sharedPreferences.edit()
|
||||||
|
val json = Gson().toJson(materialList)
|
||||||
|
editor.putString("material_list_$filterMaterial", json)
|
||||||
|
editor.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMaterialList(filterMaterial: String): List<MaterialDataModel>? {
|
||||||
|
val json = sharedPreferences.getString("material_list_$filterMaterial", null)
|
||||||
return if (json != null) {
|
return if (json != null) {
|
||||||
val type = object : TypeToken<List<MaterialDataModel>>() {}.type
|
val type = object : TypeToken<List<MaterialDataModel>>() {}.type
|
||||||
Gson().fromJson(json, type)
|
Gson().fromJson(json, type)
|
||||||
|
@ -28,7 +47,27 @@ class SharedPrefHelper(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearMaterialList() {
|
fun clearMaterialList(filterMaterial: String) {
|
||||||
sharedPreferences.edit().remove("material_list").apply()
|
sharedPreferences.edit().remove("material_list_$filterMaterial").apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearAllMaterialData() {
|
||||||
|
sharedPreferences.edit().clear().apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserData(): UserModel? {
|
||||||
|
val json = sharedPreferences.getString("user_data", null)
|
||||||
|
return if (json != null) {
|
||||||
|
Gson().fromJson(json, UserModel::class.java)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveUserData(user: UserModel) {
|
||||||
|
val editor = sharedPreferences.edit()
|
||||||
|
val json = Gson().toJson(user)
|
||||||
|
editor.putString("user_data", json)
|
||||||
|
editor.apply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.example.lexilearn.data.model
|
||||||
|
|
||||||
|
data class UserModel(
|
||||||
|
var email: String = "",
|
||||||
|
var name: String = "",
|
||||||
|
var password: String = "",
|
||||||
|
var unlock_data: UnlockData = UnlockData() // 🔥 Perbaiki cara mendeklarasikan UnlockData
|
||||||
|
) {
|
||||||
|
data class UnlockData(
|
||||||
|
var animal: Int = 0,
|
||||||
|
var family: Int = 0,
|
||||||
|
var house: Int = 0,
|
||||||
|
var limb: Int = 0
|
||||||
|
)
|
||||||
|
}
|
|
@ -5,17 +5,19 @@ import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.example.lexilearn.data.model.MaterialDataModel
|
import com.example.lexilearn.data.model.MaterialDataModel
|
||||||
import com.example.lexilearn.data.local.SharedPrefHelper
|
import com.example.lexilearn.data.local.SharedPrefHelper
|
||||||
|
import com.example.lexilearn.data.model.UserModel
|
||||||
import com.google.firebase.database.*
|
import com.google.firebase.database.*
|
||||||
import com.google.firebase.database.DatabaseReference
|
import com.google.firebase.database.DatabaseReference
|
||||||
import com.google.firebase.database.FirebaseDatabase
|
import com.google.firebase.database.FirebaseDatabase
|
||||||
|
|
||||||
class FirebaseHelper(private val context: Context) {
|
class FirebaseHelper(private val context: Context) {
|
||||||
private val databaseReference: DatabaseReference =
|
|
||||||
FirebaseDatabase.getInstance().getReference("data/animal")
|
|
||||||
|
|
||||||
private val sharedPrefHelper = SharedPrefHelper(context)
|
private val sharedPrefHelper = SharedPrefHelper(context)
|
||||||
|
|
||||||
fun fetchMaterials(callback: (List<MaterialDataModel>) -> Unit) {
|
// 🔥 Fungsi untuk mengambil data berdasarkan filterMaterial (kode lama, tetap dipertahankan)
|
||||||
|
fun fetchMaterials(filterMaterial: String, callback: (List<MaterialDataModel>) -> Unit) {
|
||||||
|
val databaseReference: DatabaseReference =
|
||||||
|
FirebaseDatabase.getInstance().getReference("data/$filterMaterial")
|
||||||
|
|
||||||
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
||||||
override fun onDataChange(snapshot: DataSnapshot) {
|
override fun onDataChange(snapshot: DataSnapshot) {
|
||||||
val materialList = mutableListOf<MaterialDataModel>()
|
val materialList = mutableListOf<MaterialDataModel>()
|
||||||
|
@ -24,8 +26,7 @@ class FirebaseHelper(private val context: Context) {
|
||||||
material?.let { materialList.add(it) }
|
material?.let { materialList.add(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simpan ke SharedPreferences agar bisa diakses tanpa internet
|
sharedPrefHelper.saveMaterialList(filterMaterial, materialList)
|
||||||
sharedPrefHelper.saveMaterialList(materialList)
|
|
||||||
callback(materialList)
|
callback(materialList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,4 +35,53 @@ class FirebaseHelper(private val context: Context) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ✅ 🔥 Fungsi Login - Mencari user berdasarkan email dan password
|
||||||
|
fun loginUser(email: String, password: String, callback: (Boolean, String?) -> Unit) {
|
||||||
|
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users")
|
||||||
|
|
||||||
|
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
||||||
|
override fun onDataChange(snapshot: DataSnapshot) {
|
||||||
|
for (userSnapshot in snapshot.children) {
|
||||||
|
val user = userSnapshot.getValue(UserModel::class.java)
|
||||||
|
if (user != null && user.email == email && user.password == password) {
|
||||||
|
val userId = userSnapshot.key // Ambil ID pengguna
|
||||||
|
sharedPrefHelper.saveUserId(userId!!) // Simpan ID ke SharedPreferences
|
||||||
|
callback(true, userId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback(false, null) // Jika tidak ditemukan
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancelled(error: DatabaseError) {
|
||||||
|
Log.e("FirebaseHelper", "Error: ${error.message}")
|
||||||
|
callback(false, null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ 🔥 Fungsi Fetch User Data Berdasarkan ID (Dapatkan data user setelah login)
|
||||||
|
fun fetchUserData(callback: (UserModel?) -> Unit) {
|
||||||
|
val userId = sharedPrefHelper.getUserId()
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
callback(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val databaseReference: DatabaseReference = FirebaseDatabase.getInstance().getReference("users/$userId")
|
||||||
|
|
||||||
|
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
|
||||||
|
override fun onDataChange(snapshot: DataSnapshot) {
|
||||||
|
val user = snapshot.getValue(UserModel::class.java)
|
||||||
|
callback(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCancelled(error: DatabaseError) {
|
||||||
|
Log.e("FirebaseHelper", "Error: ${error.message}")
|
||||||
|
callback(null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,20 +4,129 @@ import android.content.Context
|
||||||
import com.example.lexilearn.data.local.SharedPrefHelper
|
import com.example.lexilearn.data.local.SharedPrefHelper
|
||||||
import com.example.lexilearn.data.remote.FirebaseHelper
|
import com.example.lexilearn.data.remote.FirebaseHelper
|
||||||
import com.example.lexilearn.data.model.MaterialDataModel
|
import com.example.lexilearn.data.model.MaterialDataModel
|
||||||
|
import com.example.lexilearn.data.model.UserModel
|
||||||
|
import com.google.firebase.database.FirebaseDatabase
|
||||||
|
|
||||||
class MaterialRepository(private val context: Context) {
|
class MaterialRepository(private val context: Context) {
|
||||||
private val sharedPrefHelper = SharedPrefHelper(context)
|
private val sharedPrefHelper = SharedPrefHelper(context)
|
||||||
private val firebaseHelper = FirebaseHelper(context)
|
private val firebaseHelper = FirebaseHelper(context)
|
||||||
|
|
||||||
fun getMaterialData(callback: (List<MaterialDataModel>) -> Unit) {
|
fun getMaterialData(filterMaterial: String, callback: (List<MaterialDataModel>) -> Unit) {
|
||||||
val cachedData = sharedPrefHelper.getMaterialList()
|
val cachedData =
|
||||||
|
sharedPrefHelper.getMaterialList(filterMaterial) // Ambil data dengan filter
|
||||||
if (cachedData != null) {
|
if (cachedData != null) {
|
||||||
callback(cachedData)
|
callback(cachedData)
|
||||||
} else {
|
} else {
|
||||||
firebaseHelper.fetchMaterials { materials ->
|
firebaseHelper.fetchMaterials(filterMaterial) { materials ->
|
||||||
|
sharedPrefHelper.saveMaterialList(
|
||||||
|
filterMaterial,
|
||||||
|
materials
|
||||||
|
)
|
||||||
callback(materials)
|
callback(materials)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
fun createUser(user: UserModel, callback: (Boolean, String?) -> Unit) {
|
||||||
|
val databaseReference = FirebaseDatabase.getInstance().getReference("users")
|
||||||
|
|
||||||
|
// Buat user baru dengan ID unik
|
||||||
|
val userId = databaseReference.push().key
|
||||||
|
|
||||||
|
if (userId != null) {
|
||||||
|
databaseReference.child(userId).setValue(user).addOnCompleteListener { task ->
|
||||||
|
if (task.isSuccessful) {
|
||||||
|
sharedPrefHelper.saveUserId(userId) // Simpan user ID
|
||||||
|
callback(true, userId)
|
||||||
|
} else {
|
||||||
|
callback(false, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(false, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loginUser(email: String, password: String, callback: (Boolean, String?) -> Unit) {
|
||||||
|
firebaseHelper.loginUser(email, password) { success, userId ->
|
||||||
|
if (success && userId != null) {
|
||||||
|
sharedPrefHelper.saveUserId(userId) // Simpan ID user
|
||||||
|
callback(true, userId)
|
||||||
|
} else {
|
||||||
|
callback(false, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUserData(callback: (UserModel?) -> Unit) {
|
||||||
|
firebaseHelper.fetchUserData { user ->
|
||||||
|
callback(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateUnlockData(idUnlock: String, value: Int, callback: (Boolean) -> Unit) {
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sharedPrefHelper.saveUserData(updatedUser!!)
|
||||||
|
callback(true)
|
||||||
|
} else {
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(false) // 🔥 Jika nilai baru <= nilai lama, tidak update
|
||||||
|
}
|
||||||
|
}.addOnFailureListener {
|
||||||
|
callback(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUnlockValue(idUnlock: String, callback: (Int?) -> Unit) {
|
||||||
|
val userId = sharedPrefHelper.getUserId()
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
callback(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val userRef = FirebaseDatabase.getInstance().getReference("users/$userId/unlock_data/$idUnlock")
|
||||||
|
|
||||||
|
// 🔥 Ambil nilai dari Firebase
|
||||||
|
userRef.get().addOnSuccessListener { snapshot ->
|
||||||
|
val currentValue = snapshot.getValue(Int::class.java) ?: 0
|
||||||
|
callback(currentValue)
|
||||||
|
}.addOnFailureListener {
|
||||||
|
callback(null) // 🔥 Gagal mengambil data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun logoutUser() {
|
||||||
|
sharedPrefHelper.clearUserData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -49,13 +49,14 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List<Ma
|
||||||
}
|
}
|
||||||
|
|
||||||
val materialList by viewModel.materialList.collectAsState()
|
val materialList by viewModel.materialList.collectAsState()
|
||||||
|
val textTitle by viewModel.textTitle.observeAsState("")
|
||||||
|
|
||||||
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
|
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
|
||||||
|
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
GradientQuiz(
|
GradientQuiz(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
headerText = "Detail Hewan",
|
headerText = "Belajar materi $textTitle",
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -72,30 +73,11 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List<Ma
|
||||||
items(materialList) { dataMat ->
|
items(materialList) { dataMat ->
|
||||||
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
|
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
|
||||||
val (refButton) = createRefs()
|
val (refButton) = createRefs()
|
||||||
IconButton(
|
|
||||||
onClick = {
|
|
||||||
viewModel.speakLetter(dataMat.enName)
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.size(80.dp).constrainAs(refButton) {
|
|
||||||
top.linkTo(parent.top)
|
|
||||||
end.linkTo(parent.end)
|
|
||||||
} // 🔥 Ukuran tombol bisa disesuaikan
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.VolumeUp, // 🔥 Ikon Speaker dari Material Icons
|
|
||||||
contentDescription = "Speaker Icon",
|
|
||||||
tint = Color.Black // 🔥 Warna ikon
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Column {
|
Column {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.border(2.dp, color = ctextBlack, RoundedCornerShape(8.dp))
|
.border(2.dp, color = ctextBlack, RoundedCornerShape(8.dp)),
|
||||||
.clickable {
|
|
||||||
viewModel.speakLetter(dataMat.enName)
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = Color.White)
|
colors = CardDefaults.cardColors(containerColor = Color.White)
|
||||||
) {
|
) {
|
||||||
|
@ -151,6 +133,23 @@ fun DetailMaterialScreen(navController: NavController, materialListData: List<Ma
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
}
|
}
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
viewModel.speakLetter(dataMat.enName)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.size(80.dp)
|
||||||
|
.constrainAs(refButton) {
|
||||||
|
top.linkTo(parent.top)
|
||||||
|
end.linkTo(parent.end)
|
||||||
|
} // 🔥 Ukuran tombol bisa disesuaikan
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.VolumeUp, // 🔥 Ikon Speaker dari Material Icons
|
||||||
|
contentDescription = "Speaker Icon",
|
||||||
|
tint = Color.Black // 🔥 Warna ikon
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,11 @@ import android.speech.tts.TextToSpeech
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
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.utils.generateNumberList
|
import com.example.lexilearn.utils.generateNumberList
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class DetailMaterialViewModel(application: Application) : AndroidViewModel(application),
|
class DetailMaterialViewModel(application: Application) : AndroidViewModel(application),
|
||||||
|
@ -27,8 +25,22 @@ class DetailMaterialViewModel(application: Application) : AndroidViewModel(appli
|
||||||
private val _materialList = MutableStateFlow<List<MaterialDataModel>>(emptyList())
|
private val _materialList = MutableStateFlow<List<MaterialDataModel>>(emptyList())
|
||||||
val materialList: StateFlow<List<MaterialDataModel>> = _materialList
|
val materialList: StateFlow<List<MaterialDataModel>> = _materialList
|
||||||
|
|
||||||
|
private val _textTitle = MutableLiveData("Hello, Jetpack Compose!")
|
||||||
|
val textTitle: LiveData<String> = _textTitle
|
||||||
|
|
||||||
fun fetchMaterial(materialListData: List<MaterialDataModel>) {
|
fun fetchMaterial(materialListData: List<MaterialDataModel>) {
|
||||||
_materialList.value = materialListData
|
_materialList.value = materialListData
|
||||||
|
if (materialListData.isNotEmpty()) {
|
||||||
|
if (materialListData[0].category == "animal") {
|
||||||
|
_textTitle.value = "hewan"
|
||||||
|
} else if (materialListData[0].category == "limb") {
|
||||||
|
_textTitle.value = "anggota tubuh"
|
||||||
|
}else if (materialListData[0].category == "house") {
|
||||||
|
_textTitle.value = "bagian rumah"
|
||||||
|
}else if (materialListData[0].category == "family") {
|
||||||
|
_textTitle.value = "anggota keluarga"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
|
@ -222,7 +222,7 @@ fun HomeScreen(navController: NavController) {
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
val (quizRef, alphabetRef, screeningRef, newsRef) = createRefs()
|
val (quizRef, alphabetRef, screeningRef, newsRef) = createRefs()
|
||||||
ButtonHome(onClick = { navController.navigate("read") },
|
ButtonHome(onClick = { navController.navigate("navMaterial/limb") },
|
||||||
color = Color(0xff47bff0),
|
color = Color(0xff47bff0),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.constrainAs(quizRef) {
|
.constrainAs(quizRef) {
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
package com.example.lexilearn.ui.views.pNavMaterial
|
package com.example.lexilearn.ui.views.pNavMaterial
|
||||||
|
|
||||||
|
import android.graphics.Shader
|
||||||
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.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Lock
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
@ -17,6 +22,9 @@ import androidx.compose.runtime.livedata.observeAsState
|
||||||
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.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.RenderEffect
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
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
|
||||||
|
@ -31,7 +39,7 @@ 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.ctextGray
|
import com.example.lexilearn.ui.theme.ctextGray
|
||||||
import com.example.lexilearn.ui.theme.cwhite
|
import com.example.lexilearn.ui.theme.cwhite
|
||||||
import com.google.gson.Gson
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavMaterialScreen(navController: NavController, materialId: String) {
|
fun NavMaterialScreen(navController: NavController, materialId: String) {
|
||||||
|
@ -41,13 +49,15 @@ fun NavMaterialScreen(navController: NavController, materialId: String) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val materialList by viewModel.materialList.collectAsState()
|
val materialList by viewModel.materialList.collectAsState()
|
||||||
|
val textTitle by viewModel.textTitle.observeAsState("")
|
||||||
|
val sizeUnlock by viewModel.sizeUnlock.observeAsState()
|
||||||
|
|
||||||
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
|
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
|
||||||
|
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
GradientQuiz(
|
GradientQuiz(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
headerText = "Belajar Hewan",
|
headerText = "Belajar ${textTitle}",
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
|
@ -61,9 +71,9 @@ fun NavMaterialScreen(navController: NavController, materialId: String) {
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentPadding = PaddingValues(16.dp)
|
contentPadding = PaddingValues(16.dp)
|
||||||
) {
|
) {
|
||||||
items(materialList) { chunk ->
|
itemsIndexed(materialList) { index, chunk ->
|
||||||
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
|
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
|
||||||
val (space, boxItem, centerLine) = createRefs()
|
val (space, boxItem, centerLine, lockIcon, cardIcon) = createRefs()
|
||||||
Box(modifier = Modifier
|
Box(modifier = Modifier
|
||||||
.background(color = cwhite, shape = RoundedCornerShape(12.dp))
|
.background(color = cwhite, shape = RoundedCornerShape(12.dp))
|
||||||
.constrainAs(boxItem) {
|
.constrainAs(boxItem) {
|
||||||
|
@ -75,37 +85,83 @@ fun NavMaterialScreen(navController: NavController, materialId: String) {
|
||||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(12.dp).clickable {
|
.padding(12.dp)
|
||||||
println("testingkus")
|
.clickable {
|
||||||
// val jsonList = Gson().toJson(chunk) // 🔥 Konversi List ke JSON String
|
if (index <= (sizeUnlock ?: 0)) {
|
||||||
// navController.navigate("detailMaterial/$jsonList")
|
navController.currentBackStackEntry?.savedStateHandle?.set(
|
||||||
navController.currentBackStackEntry?.savedStateHandle?.set("materialList", chunk)
|
"materialList",
|
||||||
navController.navigate("detailMaterial")
|
chunk
|
||||||
|
)
|
||||||
|
navController.navigate("detailMaterial")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
chunk.forEachIndexed { index, material ->
|
chunk.forEachIndexed { index, material ->
|
||||||
if(index!=1){
|
if (index != 1) {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Box(
|
Box(
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(80.dp) // 🔥 Ukuran gambar
|
.size(80.dp) // 🔥 Ukuran gambar
|
||||||
.clip(RoundedCornerShape(12.dp)) // 🔥 Memberikan sudut lengkung (radius)
|
.clip(RoundedCornerShape(12.dp)) // 🔥 Memberikan sudut lengkung (radius)
|
||||||
.border(2.dp, cGray, RoundedCornerShape(12.dp)) // 🔥 Memberikan border
|
.border(
|
||||||
|
2.dp,
|
||||||
|
cGray,
|
||||||
|
RoundedCornerShape(12.dp)
|
||||||
|
) // 🔥 Memberikan border
|
||||||
) {
|
) {
|
||||||
FirebaseImage(path = material.image, contentScale = ContentScale.Crop, modifier = Modifier.matchParentSize()) // 🔥 Pastikan gambar mengisi Box
|
FirebaseImage(
|
||||||
|
path = material.image,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.matchParentSize()
|
||||||
|
) // 🔥 Pastikan gambar mengisi Box
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(50.dp))
|
Spacer(modifier = Modifier.height(50.dp))
|
||||||
Text(text = "${material.enName}", color = cAccent, fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
Text(
|
||||||
Text(text = "${material.idName}", color = ctextGray, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
text = "${material.enName}",
|
||||||
|
color = cAccent,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${material.idName}",
|
||||||
|
color = ctextGray,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Text(text = "${material.enName}", color = cAccent, fontSize = 18.sp, fontWeight = FontWeight.Bold)
|
Text(
|
||||||
Text(text = "${material.idName}", color = ctextGray, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
text = "${material.enName}",
|
||||||
|
color = cAccent,
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "${material.idName}",
|
||||||
|
color = ctextGray,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
Spacer(modifier = Modifier.height(50.dp))
|
Spacer(modifier = Modifier.height(50.dp))
|
||||||
FirebaseImage(path = material.image, modifier = Modifier.size(80.dp))
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(80.dp) // 🔥 Ukuran gambar
|
||||||
|
.clip(RoundedCornerShape(12.dp)) // 🔥 Memberikan sudut lengkung (radius)
|
||||||
|
.border(
|
||||||
|
2.dp,
|
||||||
|
cGray,
|
||||||
|
RoundedCornerShape(12.dp)
|
||||||
|
) // 🔥 Memberikan border
|
||||||
|
) {
|
||||||
|
FirebaseImage(
|
||||||
|
path = material.image,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.matchParentSize()
|
||||||
|
) // 🔥 Pastikan gambar mengisi Box
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +183,25 @@ fun NavMaterialScreen(navController: NavController, materialId: String) {
|
||||||
top.linkTo(boxItem.bottom)
|
top.linkTo(boxItem.bottom)
|
||||||
start.linkTo(boxItem.start)
|
start.linkTo(boxItem.start)
|
||||||
})
|
})
|
||||||
|
if (index > (sizeUnlock ?: 0)) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.constrainAs(cardIcon) {
|
||||||
|
start.linkTo(parent.start)
|
||||||
|
top.linkTo(parent.top)
|
||||||
|
bottom.linkTo(parent.bottom)
|
||||||
|
end.linkTo(parent.end)
|
||||||
|
}
|
||||||
|
.background(Color.White.copy(alpha = 0.5f)) // Transparan blur effect
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Lock,
|
||||||
|
contentDescription = "Lock Icon",
|
||||||
|
modifier = Modifier.size(60.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,12 @@ class NavMaterialViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
|
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
|
||||||
val alphabetList = generateNumberList()
|
val alphabetList = generateNumberList()
|
||||||
|
|
||||||
|
private val _textTitle = MutableLiveData("Hello, Jetpack Compose!")
|
||||||
|
val textTitle: LiveData<String> = _textTitle
|
||||||
|
|
||||||
|
private val _sizeUnlock = MutableLiveData(0)
|
||||||
|
val sizeUnlock: LiveData<Int> = _sizeUnlock
|
||||||
|
|
||||||
private val repository = MaterialRepository(application)
|
private val repository = MaterialRepository(application)
|
||||||
|
|
||||||
private val _materialList = MutableStateFlow<List<List<MaterialDataModel>>>(emptyList())
|
private val _materialList = MutableStateFlow<List<List<MaterialDataModel>>>(emptyList())
|
||||||
|
@ -29,9 +35,21 @@ class NavMaterialViewModel(application: Application) : AndroidViewModel(applicat
|
||||||
|
|
||||||
fun fetchMaterial(materialId: String) {
|
fun fetchMaterial(materialId: String) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
repository.getMaterialData { materials ->
|
repository.getUnlockValue(materialId) { value ->
|
||||||
val filteredMaterials = materials.filter { it.category == materialId }
|
_sizeUnlock.value = value
|
||||||
_materialList.value = filteredMaterials.chunked(3)
|
}
|
||||||
|
repository.getMaterialData(materialId) { materials ->
|
||||||
|
// val filteredMaterials = materials.filter { it.category == materialId }
|
||||||
|
_materialList.value = materials.chunked(3)
|
||||||
|
if (materials[0].category == "animal") {
|
||||||
|
_textTitle.value = "hewan"
|
||||||
|
} else if (materials[0].category == "limb") {
|
||||||
|
_textTitle.value = "anggota tubuh"
|
||||||
|
} else if (materials[0].category == "house") {
|
||||||
|
_textTitle.value = "bagian rumah"
|
||||||
|
} else if (materials[0].category == "family") {
|
||||||
|
_textTitle.value = "anggota keluarga"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue