first commit

This commit is contained in:
DimazzP 2025-02-07 01:34:36 +07:00
parent c41f4ce6fb
commit 0cdd33997d
128 changed files with 5531 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
*.iml
.idea
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

94
app/build.gradle.kts Normal file
View File

@ -0,0 +1,94 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
}
android {
namespace = "com.example.lexilearn"
compileSdk = 34
defaultConfig {
applicationId = "com.example.lexilearn"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.0")
implementation("androidx.activity:activity-compose:1.9.0")
implementation(platform("androidx.compose:compose-bom:2023.08.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.foundation:foundation:1.6.0-beta01")
implementation("androidx.compose.material3:material3")
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
implementation("androidx.navigation:navigation-runtime-ktx:2.7.7")
implementation("androidx.navigation:navigation-compose:2.7.7")
implementation("com.google.accompanist:accompanist-systemuicontroller:0.28.0")
implementation("com.github.tehras:charts:0.2.4-alpha")
implementation("com.google.accompanist:accompanist-flowlayout:0.24.9-beta")
implementation("com.google.accompanist:accompanist-placeholder:0.24.13-rc")
implementation("com.google.accompanist:accompanist-navigation-material:0.24.13-rc")
implementation("io.coil-kt:coil-compose:2.1.0")
implementation ("com.google.code.gson:gson:2.10.1")
implementation(platform("com.google.firebase:firebase-bom:32.0.0"))
implementation("com.google.firebase:firebase-analytics")
implementation("androidx.media3:media3-common:1.3.1")
implementation("androidx.draganddrop:draganddrop:1.0.0")
implementation("androidx.compose.runtime:runtime-livedata:1.7.7")
implementation("com.google.firebase:firebase-database-ktx:21.0.0")
implementation("com.google.firebase:firebase-storage-ktx:21.0.1")
implementation("androidx.compose.material:material-icons-extended:1.5.4")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}

49
app/google-services.json Normal file
View File

@ -0,0 +1,49 @@
{
"project_info": {
"project_number": "784959915731",
"firebase_url": "https://dataset-projectt-default-rtdb.asia-southeast1.firebasedatabase.app",
"project_id": "dataset-projectt",
"storage_bucket": "dataset-projectt.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:784959915731:android:d3b20158db1a3dd11af626",
"android_client_info": {
"package_name": "com.example.deteksi_hama"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyBGWCtQrDCRQmlzFDR6jFrdLY411fDnzyk"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:784959915731:android:7dc9b6e4a0b6da3b1af626",
"android_client_info": {
"package_name": "com.example.lexilearn"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyBGWCtQrDCRQmlzFDR6jFrdLY411fDnzyk"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.example.lexilearn
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.lexilearn", appContext.packageName)
}
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.LexiLearn"
tools:targetApi="31" >
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.LexiLearn" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- android:screenOrientation="portrait"-->

View File

@ -0,0 +1,78 @@
package com.example.lexilearn
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.ui.views.pDetailMaterial.DetailMaterialScreen
import com.example.lexilearn.ui.views.pHome.HomeScreen
import com.example.lexilearn.ui.views.pLearAlphabet.LearnAlphabetScreen
import com.example.lexilearn.ui.views.pNavMaterial.NavMaterialScreen
import com.example.lexilearn.ui.views.pLearnNumber.LearnNumberScreen
import com.example.lexilearn.ui.views.pLogin.LoginScreen
import com.example.lexilearn.ui.views.pQuiz.pRead.ReadScreen
import com.example.lexilearn.ui.views.pQuiz.pSpell.SpellScreen
import com.example.lexilearn.ui.views.pQuiz.pWrite.WriteScreen
import com.example.lexilearn.ui.views.pRegister.RegisterScreen
import com.example.lexilearn.ui.views.pResultScreening.ResultScreeningScreen
import com.example.lexilearn.ui.views.pScreening.ScreeningScreen
import com.example.lexilearn.ui.views.pSplashcreen.SplashScreen
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp()
}
}
}
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("splash") { SplashScreen(navController) }
composable("login") { LoginScreen(navController) }
composable("register") { RegisterScreen(navController) }
composable("home") { HomeScreen(navController) }
composable("read") { ReadScreen(navController) }
composable("spell") { SpellScreen(navController) }
composable("write") { WriteScreen(navController) }
composable("screening") { ScreeningScreen(navController) }
composable("resultscreening") { ResultScreeningScreen(navController) }
composable("learnAlphabet") { LearnAlphabetScreen(navController) }
composable("learnNumber") { LearnNumberScreen(navController) }
composable("navMaterial/{materialId}") { backStackEntry ->
val materialId = backStackEntry.arguments?.getString("materialId")
NavMaterialScreen(navController, materialId ?: "")
}
composable("detailMaterial") { backStackEntry ->
val materialList = navController.previousBackStackEntry
?.savedStateHandle
?.get<List<MaterialDataModel>>("materialList")
if (materialList != null) {
DetailMaterialScreen(navController, materialList)
}else{
println("masuk kondisi else")
}
}
// composable("detailMaterial/{materialJson}") { backStackEntry ->
// val jsonMaterial = backStackEntry.arguments?.getString("materialJson")
//
// // 🔥 Konversi JSON String kembali ke List<MaterialDataModel>
// val type = object : TypeToken<List<MaterialDataModel>>() {}.type
// val materialList: List<MaterialDataModel> = Gson().fromJson(jsonMaterial, type)
//
// DetailMaterialScreen(navController, materialList)
// }
}
}

View File

@ -0,0 +1,34 @@
package com.example.lexilearn.data.local
import android.content.Context
import android.content.SharedPreferences
import com.example.lexilearn.data.model.MaterialDataModel
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
class SharedPrefHelper(context: Context) {
private val sharedPreferences: SharedPreferences =
context.getSharedPreferences("MaterialDataPrefs", Context.MODE_PRIVATE)
fun saveMaterialList(materialList: List<MaterialDataModel>) {
val editor = sharedPreferences.edit()
val json = Gson().toJson(materialList)
editor.putString("material_list", json)
editor.apply()
}
fun getMaterialList(): List<MaterialDataModel>? {
val json = sharedPreferences.getString("material_list", null)
return if (json != null) {
val type = object : TypeToken<List<MaterialDataModel>>() {}.type
Gson().fromJson(json, type)
} else {
null
}
}
fun clearMaterialList() {
sharedPreferences.edit().remove("material_list").apply()
}
}

View File

@ -0,0 +1,21 @@
package com.example.lexilearn.data.model
import com.google.gson.annotations.SerializedName
data class MaterialDataModel (
@SerializedName("id") val id: String = "",
@SerializedName("idName") val idName: String = "",
@SerializedName("enName") val enName: String = "",
@SerializedName("idDescription") val idDescription: String = "",
@SerializedName("enDescription") val enDescription: String = "",
@SerializedName("image") val image: String = "",
@SerializedName("category") val category: String = ""
// val id: String = "",
// val idName: String = "",
// val enName: String = "",
// val idDescription: String = "",
// val enDescription: String = "",
// val image: String = "",
// val category: String = ""
)

View File

@ -0,0 +1,37 @@
package com.example.lexilearn.data.remote
import android.content.Context
import android.util.Log
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.local.SharedPrefHelper
import com.google.firebase.database.*
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
class FirebaseHelper(private val context: Context) {
private val databaseReference: DatabaseReference =
FirebaseDatabase.getInstance().getReference("data/animal")
private val sharedPrefHelper = SharedPrefHelper(context)
fun fetchMaterials(callback: (List<MaterialDataModel>) -> Unit) {
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val materialList = mutableListOf<MaterialDataModel>()
for (materialSnapshot in snapshot.children) {
val material = materialSnapshot.getValue(MaterialDataModel::class.java)
material?.let { materialList.add(it) }
}
// Simpan ke SharedPreferences agar bisa diakses tanpa internet
sharedPrefHelper.saveMaterialList(materialList)
callback(materialList)
}
override fun onCancelled(error: DatabaseError) {
Log.e("FirebaseHelper", "Error: ${error.message}")
}
})
}
}

View File

@ -0,0 +1,23 @@
package com.example.lexilearn.data.repository
import android.content.Context
import com.example.lexilearn.data.local.SharedPrefHelper
import com.example.lexilearn.data.remote.FirebaseHelper
import com.example.lexilearn.data.model.MaterialDataModel
class MaterialRepository(private val context: Context) {
private val sharedPrefHelper = SharedPrefHelper(context)
private val firebaseHelper = FirebaseHelper(context)
fun getMaterialData(callback: (List<MaterialDataModel>) -> Unit) {
val cachedData = sharedPrefHelper.getMaterialList()
if (cachedData != null) {
callback(cachedData)
} else {
firebaseHelper.fetchMaterials { materials ->
callback(materials)
}
}
}
}

View File

@ -0,0 +1,16 @@
package com.example.lexilearn.domain.models
data class ChartData(val x: Float, val y: Float)
val chartData = listOf(
ChartData(0f, 10f),
ChartData(1f, 20f),
ChartData(2f, 15f),
ChartData(3f, 25f),
ChartData(4f, 10f),
ChartData(5f, 30f),
ChartData(6f, 20f)
)
val daysOfWeek = listOf("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat")

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.domain.models
data class ModelAlphabetNumber(
var alpha: String,
var enRead: String,
)

View File

@ -0,0 +1,7 @@
package com.example.lexilearn.domain.models
data class ModelAnswerRead (
var id: Int,
var data: String,
var showCard: Boolean = true,
)

View File

@ -0,0 +1,7 @@
package com.example.lexilearn.domain.models
data class ModelScreening(
val id: Int,
val question: String,
var answer: Int
)

View File

@ -0,0 +1,10 @@
package com.example.lexilearn.domain.models
data class ModelSpell(
val id: Int,
val type: Boolean,
var data: String,
var emp: Int? = null,
var hasContent: Boolean = false,
var showCard: Boolean = false
)

View File

@ -0,0 +1,13 @@
package com.example.lexilearn.domain.models
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
data class ModelWords(
var id: Int,
var type: Boolean,
var data: String,
var emp: Int? = null,
var hasContent: Boolean = false,
var showCard: Boolean = false,
)

View File

@ -0,0 +1,157 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.InlineTextContent
import androidx.compose.foundation.text.InternalFoundationTextApi
import androidx.compose.foundation.text.TextDelegate
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalFontFamilyResolver
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.min
import androidx.compose.ui.unit.sp
@Composable
fun AutoSizeText(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
suggestedFontSizes: List<TextUnit> = emptyList(),
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current,
) {
AutoSizeText(
AnnotatedString(text),
modifier,
color,
suggestedFontSizes,
fontStyle,
fontWeight,
fontFamily,
letterSpacing,
textDecoration,
textAlign,
lineHeight,
overflow,
softWrap,
maxLines,
emptyMap(),
onTextLayout,
style,
)
}
@Composable
fun AutoSizeText(
text: AnnotatedString,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
suggestedFontSizes: List<TextUnit> = emptyList(),
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
inlineContent: Map<String, InlineTextContent> = mapOf(),
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current,
) {
BoxWithConstraints(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
var combinedTextStyle = (LocalTextStyle.current + style).copy(
fontSize = min(maxWidth, maxHeight).value.sp
)
val fontSizes = suggestedFontSizes.ifEmpty {
MutableList(combinedTextStyle.fontSize.value.toInt()) {
(combinedTextStyle.fontSize.value - it).sp
}
}
var currentFontIndex = 0
while (shouldShrink(text, combinedTextStyle, maxLines) && currentFontIndex < fontSizes.size - 1) {
combinedTextStyle = combinedTextStyle.copy(fontSize = fontSizes[++currentFontIndex])
}
Text(
text = text,
modifier = Modifier.fillMaxWidth(),
color = color,
fontSize = combinedTextStyle.fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
inlineContent = inlineContent,
onTextLayout = onTextLayout,
style = combinedTextStyle,
)
}
}
@OptIn(InternalFoundationTextApi::class)
@Composable
private fun BoxWithConstraintsScope.shouldShrink(
text: AnnotatedString,
textStyle: TextStyle,
maxLines: Int
): Boolean {
val textDelegate = TextDelegate(
text = text,
style = textStyle,
maxLines = maxLines,
softWrap = true,
overflow = TextOverflow.Clip,
density = LocalDensity.current,
fontFamilyResolver = LocalFontFamilyResolver.current,
placeholders = listOf()
)
val textLayoutResult = textDelegate.layout(
constraints,
LocalLayoutDirection.current,
)
return textLayoutResult.hasVisualOverflow
}

View File

@ -0,0 +1,36 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.lexilearn.R
@Composable
fun BackButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
backgroundColor: Color = MaterialTheme.colorScheme.surface,
contentColor: Color = MaterialTheme.colorScheme.onSurface,
) {
Card(
colors = CardDefaults.cardColors(containerColor = backgroundColor),
modifier = modifier.size(34.dp)
) {
IconButton(onClick = onClick) {
Icon(
painter = painterResource(id = R.drawable.ic_back),
contentDescription = "Back",
tint = contentColor
)
}
}
}

View File

@ -0,0 +1,30 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun ButtonHome(
onClick: () -> Unit,
color: Color = cwhite,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Button(
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = color
),
shape = RoundedCornerShape(16.dp),
modifier = modifier
.padding(horizontal = 10.dp, vertical = 8.dp)
) {
content()
}
}

View File

@ -0,0 +1,52 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.ctextBlack
@Composable
fun ButtonNext(onclick: () -> Unit, text: String, painter: Painter, modifier: Modifier) {
Button(
onClick = onclick,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = cAccent // Background color
), modifier = modifier
.border(2.dp, cAccent, RoundedCornerShape(12.dp))
.background(cAccent, RoundedCornerShape(12.dp))
)
{
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = text,
fontWeight = FontWeight.SemiBold,
fontSize = 16.sp,
color = ctextBlack
)
Spacer(modifier = Modifier.width(8.dp))
Icon(
painter = painter,
contentDescription = "",
tint = ctextBlack
)
}
}
}

View File

@ -0,0 +1,24 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.ctextBlack
@Composable
fun CardQuiz(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Box(
modifier = modifier
.border(2.dp, ctextBlack, RoundedCornerShape(16.dp))
.background(cprimary, shape = RoundedCornerShape(16.dp)),
contentAlignment = Alignment.Center,
) {
content()
}
}

View File

@ -0,0 +1,96 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.R
import com.example.lexilearn.ui.theme.ctextGray
@Composable
fun CardScreening(question: String, onOptionSelected: (Int) -> Unit) {
// r.string
val textYes = stringResource(id = R.string.screenyes)
val textNo = stringResource(id = R.string.screenno)
val textKnow = stringResource(id = R.string.screenknow)
var selectedOption by remember { mutableStateOf("") }
Card(
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier
.background(Color(0xFFECEFF1))
.padding(16.dp)
) {
Text(
text = question,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = stringResource(id = R.string.screenselect),
fontSize = 14.sp,
color = ctextGray,
modifier = Modifier.padding(bottom = 4.dp, top = 6.dp)
)
OptionItem(
text = textYes,
iconColor = Color(0xFF4CAF50),
isSelected = selectedOption == textYes,
painter = painterResource(id = R.drawable.cl_check),
onSelect = {
selectedOption = textYes
onOptionSelected(1)
}
)
OptionItem(
text =textNo,
iconColor = Color(0xFFF44336),
isSelected = selectedOption ==textNo,
painter = painterResource(id = R.drawable.cl_close),
onSelect = {
selectedOption = textNo
onOptionSelected(2)
}
)
OptionItem(
text = textKnow,
iconColor = Color(0xFFFFC107),
isSelected = selectedOption == textKnow,
painter = painterResource(id = R.drawable.cl_helper),
onSelect = {
selectedOption = textKnow
onOptionSelected(3)
}
)
}
}
}

View File

@ -0,0 +1,31 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun CircleAvatar(
imageResId: Int,
modifier: Modifier = Modifier
) {
Image(
painter = painterResource(id = imageResId),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = modifier
.size(54.dp)
.clip(CircleShape)
.background(cwhite)
.border(2.dp, cwhite, CircleShape)
)
}

View File

@ -0,0 +1,42 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.ctextBlack
@Composable
fun CustomButton(
text: String,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Button(
onClick = onClick,
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(
containerColor = cAccent // Background color
),
modifier = modifier
.border(2.dp, cAccent, RoundedCornerShape(12.dp)) // Border color and shape
.background(cAccent, RoundedCornerShape(12.dp)) // Background color and shape
.padding(8.dp) // Padding inside the button
) {
Text(
text = text,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = ctextBlack // Text color
)
}
}

View File

@ -0,0 +1,164 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun DraggableAnswerCard(
item: String,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
) {
Card(
colors = CardDefaults.cardColors(
containerColor = cwhite,
),
shape = RoundedCornerShape(16.dp),
modifier = Modifier
.padding(2.dp)
.border(2.dp, cprimary, RoundedCornerShape(16.dp))
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
AutoSizeText(
text = item,
textAlign = TextAlign.Center,
color = ctextBlack,
fontWeight = FontWeight.Bold,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
)
}
}
}
}
//@Composable
//fun DraggableAnswerCard(
// item: String,
// modifier: Modifier = Modifier
//) {
//
// Box(
// modifier = modifier
//
// ) {
// Card(
// colors = CardDefaults.cardColors(
// containerColor = cwhite,
// ),
// shape = RoundedCornerShape(16.dp),
// modifier = Modifier
// .padding(2.dp)
// .width(280.dp)
// .height(60.dp)
// .border(2.dp, cprimary, RoundedCornerShape(16.dp))
// ) {
// Box(
// contentAlignment = Alignment.Center,
// modifier = Modifier.fillMaxSize()
// ) {
// Text(
// text = item,
// textAlign = TextAlign.Center,
// color = ctextBlack,
// fontWeight = FontWeight.Bold,
// modifier = Modifier
// .fillMaxWidth()
// .padding(vertical = 12.dp)
// )
// }
// }
// }
//}
//fun DraggableAnswerCard(
// item: String,
// onDragStart: () -> Unit,
// onDragEnd: (isDropped: Boolean) -> Unit,
// modifier: Modifier = Modifier
//) {
// var offsetX by remember { mutableStateOf(0f) }
// var offsetY by remember { mutableStateOf(0f) }
// var dragging by remember { mutableStateOf(false) }
// var initialOffsetX by remember { mutableStateOf(0f) }
// var initialOffsetY by remember { mutableStateOf(0f) }
//
// Box(
// modifier = modifier
// .zIndex(if (dragging) 1f else 0f)
// .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
// .pointerInput(Unit) {
// detectDragGestures(
// onDragStart = {
// dragging = true
// initialOffsetX = offsetX
// initialOffsetY = offsetY
// onDragStart()
// },
// onDrag = { change, dragAmount ->
// change.consume()
// offsetX += dragAmount.x
// offsetY += dragAmount.y
// },
// onDragEnd = {
// dragging = false
// onDragEnd(false)
// offsetX = initialOffsetX
// offsetY = initialOffsetY
// }
// )
// }
// ) {
// Card(
// colors = CardDefaults.cardColors(
// containerColor = cwhite,
// ),
// shape = RoundedCornerShape(16.dp),
// modifier = Modifier
// .padding(2.dp)
// .width(280.dp)
// .height(60.dp)
// .border(2.dp, cprimary, RoundedCornerShape(16.dp))
// ) {
// Box(
// contentAlignment = Alignment.Center,
// modifier = Modifier.fillMaxSize()
// ) {
// Text(
// text = item,
// textAlign = TextAlign.Center,
// color = ctextBlack,
// fontWeight = FontWeight.Bold,
// modifier = Modifier
// .fillMaxWidth()
// .padding(vertical = 12.dp)
// )
// }
// }
// }
//}

View File

@ -0,0 +1,52 @@
import android.graphics.Bitmap
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.toSize
import com.example.lexilearn.ui.theme.cGray
import com.example.lexilearn.ui.theme.ctextGray
@Composable
fun DrawBox(
modifier: Modifier = Modifier,
onPathReady: (android.graphics.Path, androidx.compose.ui.geometry.Size) -> Unit
) {
var path by remember { mutableStateOf(Path()) }
var androidPath by remember { mutableStateOf(android.graphics.Path()) }
var canvasSize by remember { mutableStateOf(androidx.compose.ui.geometry.Size.Zero) }
Canvas(
modifier = modifier.background(cGray, shape = RoundedCornerShape(16.dp))
.onGloballyPositioned { coordinates ->
canvasSize = coordinates.size.toSize()
}
.pointerInput(Unit) {
detectDragGestures(onDragStart = { offset ->
path.moveTo(offset.x, offset.y)
androidPath.moveTo(offset.x, offset.y)
}, onDrag = { change, _ ->
change.consume()
path.lineTo(change.position.x, change.position.y)
androidPath.lineTo(change.position.x, change.position.y)
path = Path().also { it.addPath(path) }
})
}
) {
drawPath(
path = path,
color = Color.Black,
style = Stroke(width = 5f, cap = StrokeCap.Round, join = StrokeJoin.Round)
)
}
onPathReady(androidPath, canvasSize)
}

View File

@ -0,0 +1,54 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.R
@Composable
fun EmailTextField(
value: TextFieldValue,
onValueChange: (TextFieldValue) -> Unit,
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_email), // Replace with your email icon drawable resource
contentDescription = "Email Icon",
tint = Color.Gray
)
},
placeholder = { Text(text = stringResource(id = R.string.email)) },
shape = RoundedCornerShape(8.dp),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.White,
unfocusedContainerColor = Color.White,
disabledContainerColor = Color.White,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
modifier = modifier
.fillMaxWidth()
.background(Color.White, RoundedCornerShape(8.dp))
)
}

View File

@ -0,0 +1,47 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.Image
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import com.example.lexilearn.utils.getFirebaseImageUrl
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
@Composable
fun FirebaseImage(path: String, contentScale: ContentScale = ContentScale.None, modifier: Modifier = Modifier) {
val imageUrlFlow = remember { MutableStateFlow<String?>(null) }
val coroutineScope = rememberCoroutineScope()
// Ambil URL dari Firebase Storage secara async
LaunchedEffect(path) {
coroutineScope.launch {
getFirebaseImageUrl(path) { url ->
imageUrlFlow.value = url
}
}
}
val imageUrl by imageUrlFlow.collectAsState()
if (imageUrl != null) {
AsyncImage(
model = imageUrl,
contentDescription = "Firebase Image",
modifier = modifier,
contentScale = contentScale
)
} else {
Text(text = "Loading..")
}
}

View File

@ -0,0 +1,43 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import com.example.lexilearn.R
@Composable
fun GradientLogin(content: @Composable () -> Unit) {
Box(
modifier = Modifier
.fillMaxSize()
) {
Image(
painter = painterResource(id = R.drawable.bg_login), // Replace with your background image resource
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(Color(0x809F87FF), Color(0xFF3C34AA), Color(0xFF3C34AA)),
start = Offset(0f, 0f),
end = Offset(0f, Float.POSITIVE_INFINITY)
)
)
) {
content()
}
}
}

View File

@ -0,0 +1,93 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.lexilearn.R
@Composable
fun GradientQuiz(
navController: NavController,
headerText: String,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Box(
modifier = modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(Color(0xFF6A69DB), Color(0xFF9B79F1)),
start = androidx.compose.ui.geometry.Offset(0f, 0f),
end = androidx.compose.ui.geometry.Offset(
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY
)
)
)
) {
Box(
modifier = Modifier
.width(150.dp)
.height(80.dp)
.align(alignment = Alignment.TopEnd)
.clip(shape = RoundedCornerShape(bottomStart = 30.dp))
.background(Color(0x1AEEEDFA)) // Warna transparan 10% #EEEDFA
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
// Header
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp, start = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
BackButton(onClick = { navController.popBackStack() })
Spacer(modifier = Modifier.width(12.dp))
Text(
text = headerText,
style = TextStyle(
color = Color.White,
fontSize = 20.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier.weight(1f)
)
}
// Half circle at the bottom left
Box(
modifier = Modifier
.size(200.dp)
.offset(x = (-200).dp, y = 100.dp)
.clip(shape = RoundedCornerShape(360.dp))
.background(Color(0x1AEEEDFA)) // Warna transparan 10% #EEEDFA
)
}
}
content()
}

View File

@ -0,0 +1,43 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import com.example.lexilearn.R
@Composable
fun GradientRegister(content: @Composable () -> Unit) {
Box(
modifier = Modifier
.fillMaxSize()
) {
Image(
painter = painterResource(id = R.drawable.bg_register), // Replace with your background image resource
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(Color(0x809F87FF), Color(0xFF3C34AA), Color(0xFF3C34AA)),
start = Offset(0f, 0f),
end = Offset(0f, Float.POSITIVE_INFINITY)
)
)
) {
content()
}
}
}

View File

@ -0,0 +1,103 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun GradientScreening(
backButton: () -> Unit,
headerText: String,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Box(
modifier = modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(Color(0xFF6A69DB), Color(0xFF9B79F1)),
start = androidx.compose.ui.geometry.Offset(0f, 0f),
end = androidx.compose.ui.geometry.Offset(
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY
)
)
)
) {
Box(
modifier = Modifier
.width(150.dp)
.height(80.dp)
.align(alignment = Alignment.TopEnd)
.clip(shape = RoundedCornerShape(bottomStart = 30.dp))
.background(Color(0x1AEEEDFA)) // Warna transparan 10% #EEEDFA
)
Column(
modifier = Modifier
.fillMaxSize()
.padding(4.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
// Header
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
){
val (backRef, textRef) = createRefs()
IconButton(onClick = backButton, modifier = Modifier.constrainAs(backRef){
top.linkTo(parent.top)
start.linkTo(parent.start)
}) {
Icon(painter = painterResource(id = R.drawable.ic_close), contentDescription = "icon close", tint = cwhite)
}
Text(
text = headerText,
style = TextStyle(
color = Color.White,
fontSize = 20.sp,
),
modifier = Modifier.constrainAs(textRef){
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
)
}
Box(
modifier = Modifier
.size(200.dp)
.offset(x = (-200).dp, y = 100.dp)
.clip(shape = RoundedCornerShape(360.dp))
.background(Color(0x1AEEEDFA)) // Warna transparan 10% #EEEDFA
)
}
}
content()
}

View File

@ -0,0 +1,28 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.csecondary
@Composable
fun GradientSplash(content: @Composable () -> Unit) {
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(cprimary, csecondary),
start = Offset(0f, 0f),
end = Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY)
)
)
) {
content()
}
}

View File

@ -0,0 +1,21 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
fun HorizontalLine() {
Box(
modifier = Modifier
.height(2.dp)
.fillMaxWidth()
.background(Color(0xFFD4D4D4))
)
}

View File

@ -0,0 +1,47 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.ui.theme.ctextWhite
@Composable
fun LoginTextButton(
textHelper: String,
textBtn: String,
onclick: () -> Unit,
modifier: Modifier = Modifier
) {
val annotatedText = buildAnnotatedString {
append(textHelper)
pushStringAnnotation(tag = "register", annotation = "register")
withStyle(style = SpanStyle(color = Color(0xFFFFA500), textDecoration = TextDecoration.Underline)) {
append(textBtn)
}
pop()
}
Text(
text = annotatedText,
modifier = modifier
.padding(16.dp)
.clickable {
onclick()
},
fontSize = 16.sp,
textAlign = TextAlign.Center,
color = ctextWhite// Pastikan Anda mengganti ctextWhite dengan Color.White atau definisikan ctextWhite
)
}

View File

@ -0,0 +1,24 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun MyCard(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Card(
colors = CardDefaults.cardColors(
containerColor = cwhite,
),
shape = RoundedCornerShape(16.dp),
modifier = modifier
.padding(16.dp)
.fillMaxWidth() // Atur sesuai kebutuhan Anda
) {
content()
}
}

View File

@ -0,0 +1,28 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun MyShadowCard(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Box(
modifier = modifier
.background(cwhite, shape = RoundedCornerShape(16.dp))
.fillMaxWidth()
) {
content()
}
}

View File

@ -0,0 +1,49 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.R
@Composable
fun NameTextField(placeholder: String, value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, ic: Int) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
leadingIcon = {
Icon(
painter = painterResource(id = ic), // Replace with your email icon drawable resource
contentDescription = "Email Icon",
tint = Color.Gray
)
},
placeholder = { Text(text = placeholder) },
shape = RoundedCornerShape(8.dp),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.White,
unfocusedContainerColor = Color.White,
disabledContainerColor = Color.White,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
modifier = modifier
.fillMaxWidth()
.background(Color.White, RoundedCornerShape(8.dp))
)
}

View File

@ -0,0 +1,63 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lexilearn.ui.theme.cGray
import com.example.lexilearn.ui.theme.csecondary
@Composable
fun OptionItem(text: String, iconColor: Color, isSelected: Boolean, painter: Painter, onSelect: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.background(
if (isSelected) csecondary else Color(0xffCFCFD0),
shape = MaterialTheme.shapes.small
)
.clickable { onSelect() }
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier
.size(24.dp)
.clip(CircleShape)
.background(iconColor)
.padding(4.dp),
tint = Color.White,
painter = painter,
contentDescription = null
)
Spacer(modifier = Modifier.width(16.dp))
Text(
text = text,
fontSize = 16.sp,
fontWeight = FontWeight.Medium,
color = if (isSelected) Color.White else Color.Black
)
}
}

View File

@ -0,0 +1,61 @@
package com.example.lexilearn.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import com.example.lexilearn.R
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PasswordTextField(value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier) {
var passwordVisibility by remember { mutableStateOf(false) }
OutlinedTextField(
value = value,
onValueChange = onValueChange,
leadingIcon = {
Icon(
painter = painterResource(id = R.drawable.ic_password), // Replace with your password icon drawable resource
contentDescription = "Password Icon",
tint = Color.Gray
)
},
placeholder = { Text(text = stringResource(id = R.string.password)) },
shape = RoundedCornerShape(8.dp),
visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
val image = if (passwordVisibility) {
painterResource(id = R.drawable.ic_visibility) // Replace with your visibility on icon drawable resource
} else {
painterResource(id = R.drawable.ic_visibility_off) // Replace with your visibility off icon drawable resource
}
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(painter = image, contentDescription = "Toggle Password Visibility")
}
},
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.White,
unfocusedContainerColor = Color.White,
disabledContainerColor = Color.White,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
modifier = modifier
.fillMaxWidth()
.background(Color.White, RoundedCornerShape(8.dp))
)
}

View File

@ -0,0 +1,25 @@
package com.example.lexilearn.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
val cprimary = Color(0xFF6A69DB)
val cTextPrimary = Color(0xFF5932be)
val csecondary = Color(0xFF9B79F1)
val cwhite = Color(0xFFFFFFFF)
val ctextBlack = Color(0xFF1B162B)
val ctextWhite = Color(0xFFF4F4F4)
val ctextGray = Color(0xFF8D8D8D)
val cAccent = Color(0xFFFFB337)
val ctransTextWhite = Color(0x99F4F4F4)
val cbackground = Color(0xFFEEEDFA)
val cGray = Color(0xFFEEEDFA)
//val primaryBackground = Color()

View File

@ -0,0 +1,70 @@
package com.example.lexilearn.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80,
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40,
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun LexiLearnTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.primary.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@ -0,0 +1,37 @@
package com.example.lexilearn.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
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
// 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
)
*/
)

View File

@ -0,0 +1,160 @@
package com.example.lexilearn.ui.views.pDetailMaterial
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.ui.components.FirebaseImage
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.HorizontalLine
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.cGray
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.ctextGray
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun DetailMaterialScreen(navController: NavController, materialListData: List<MaterialDataModel>) {
val viewModel: DetailMaterialViewModel = viewModel()
LaunchedEffect(materialListData) {
viewModel.fetchMaterial(materialListData)
}
val materialList by viewModel.materialList.collectAsState()
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = "Detail Hewan",
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(70.dp))
HorizontalLine()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp)
) {
items(materialList) { dataMat ->
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
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 {
Card(
modifier = Modifier
.fillMaxWidth()
.border(2.dp, color = ctextBlack, RoundedCornerShape(8.dp))
.clickable {
viewModel.speakLetter(dataMat.enName)
},
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White)
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Card(
modifier = Modifier
.border(
2.dp,
color = ctextBlack,
RoundedCornerShape(8.dp)
),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = Color.White)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(12.dp)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(120.dp) // 🔥 Ukuran gambar
.clip(RoundedCornerShape(12.dp)) // 🔥 Memberikan sudut lengkung (radius)
) {
FirebaseImage(
path = dataMat.image,
contentScale = ContentScale.Crop,
modifier = Modifier.matchParentSize()
) // 🔥 Pastikan gambar mengisi Box
}
Spacer(modifier = Modifier.height(20.dp))
Text(
text = "${dataMat.enName}",
color = cAccent,
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "${dataMat.idName}",
color = ctextGray,
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
}
}
Text(
text = "${dataMat.idDescription}",
color = ctextGray,
fontSize = 14.sp,
modifier = Modifier.padding(12.dp)
)
}
}
Spacer(modifier = Modifier.height(20.dp))
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,58 @@
package com.example.lexilearn.ui.views.pDetailMaterial
import android.app.Application
import android.speech.tts.TextToSpeech
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.repository.MaterialRepository
import com.example.lexilearn.utils.generateNumberList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.util.*
class DetailMaterialViewModel(application: Application) : AndroidViewModel(application),
TextToSpeech.OnInitListener {
private var tts: TextToSpeech? = null
private val _isTTSInitialized = MutableLiveData(false)
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
val alphabetList = generateNumberList()
private val repository = MaterialRepository(application)
private val _materialList = MutableStateFlow<List<MaterialDataModel>>(emptyList())
val materialList: StateFlow<List<MaterialDataModel>> = _materialList
fun fetchMaterial(materialListData: List<MaterialDataModel>) {
_materialList.value = materialListData
}
init {
tts = TextToSpeech(application, this)
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
tts?.language = Locale.ENGLISH
_isTTSInitialized.value = true
} else {
_isTTSInitialized.value = false
}
}
fun speakLetter(letter: String) {
if (_isTTSInitialized.value == true) {
tts?.speak(letter, TextToSpeech.QUEUE_FLUSH, null, null)
}
}
override fun onCleared() {
tts?.stop()
tts?.shutdown()
super.onCleared()
}
}

View File

@ -0,0 +1,337 @@
package com.example.lexilearn.ui.views.pHome
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.ui.components.AutoSizeText
import com.example.lexilearn.ui.components.ButtonHome
import com.example.lexilearn.ui.theme.cGray
import com.example.lexilearn.ui.theme.cTextPrimary
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.csecondary
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.ctextWhite
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun HomeScreen(navController: NavController) {
val viewModel: HomeViewModel = viewModel()
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.linearGradient(
colors = listOf(cprimary, csecondary),
start = Offset(0f, 0f), // Kiri atas
end = Offset(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY) // Kanan bawah
)
)
) {
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
) {
val (titleRef, dateRef) = createRefs()
Text(
text = "Name",
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = ctextBlack,
modifier = Modifier.constrainAs(titleRef) {
top.linkTo(parent.top, margin = 24.dp)
start.linkTo(parent.start, margin = 16.dp)
bottom.linkTo(parent.bottom, margin = 24.dp)
}
)
Text(
text = "Selamat Datang",
fontSize = 14.sp,
color = ctextWhite,
modifier = Modifier.constrainAs(dateRef) {
top.linkTo(titleRef.bottom, margin = 4.dp)
start.linkTo(titleRef.start)
}
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(color = cGray)
)
Box(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
.border(
width = 2.dp,
color = cwhite,
shape = RoundedCornerShape(12.dp)
)
.clip(RoundedCornerShape(12.dp))
.background(
brush = Brush.linearGradient(
colors = listOf(
cprimary,
csecondary
), // Gradient dari cprimary ke csecondary
start = Offset(0f, 0f), // Mulai dari kiri atas
end = Offset(
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY
) // Berakhir di kanan bawah
)
)
) {
Column(Modifier.padding(14.dp)) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Bottom,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = "Materi Terselesaikan",
color = ctextWhite,
fontWeight = FontWeight.Bold,
fontSize = 16.sp
)
Image(
painter = painterResource(id = R.drawable.bg_children),
contentDescription = "image",
modifier = Modifier.size(80.dp)
)
}
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(scrollState)
) {
Text(
text = "Belajar Mengenal Huruf & Angka",
color = cTextPrimary,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(10.dp)
)
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
) {
val (quizRef, alphabetRef) = createRefs()
ButtonHome(
onClick = { navController.navigate("learnNumber") }, modifier = Modifier
.constrainAs(quizRef) {
start.linkTo(parent.start)
top.linkTo(parent.top)
width = Dimension.percent(0.5f)
}, color = Color(0xfff99155)
) {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
Image(
painter = painterResource(id = R.drawable.bg_number),
contentDescription = null,
alignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
)
Text(
text = "Pengenalan Angka",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
color = ctextWhite
)
}
}
ButtonHome(
onClick = { navController.navigate("learnAlphabet") }, modifier = Modifier
.constrainAs(alphabetRef) {
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(quizRef.bottom)
width = Dimension.percent(0.5f)
}, color = Color(0xff3dd470)
) {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
Image(
painter = painterResource(id = R.drawable.bg_alphabet),
contentDescription = null,
alignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
)
Text(
text = "Pengenalan Alfabet",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
color = ctextWhite
)
}
}
}
Text(
text = "Latihan Bahasa Inggris Interaktif",
color = cTextPrimary,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(10.dp)
)
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
) {
val (quizRef, alphabetRef, screeningRef, newsRef) = createRefs()
ButtonHome(onClick = { navController.navigate("read") },
color = Color(0xff47bff0),
modifier = Modifier
.constrainAs(quizRef) {
start.linkTo(parent.start)
top.linkTo(parent.top)
width = Dimension.percent(0.5f)
}) {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
Image(
painter = painterResource(id = R.drawable.bg_limb),
contentDescription = null,
alignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
)
Text(
text = "Anggota Tubuh",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
color = ctextWhite
)
}
}
ButtonHome(onClick = { },
color = Color(0xffea2b72),
modifier = Modifier
.constrainAs(alphabetRef) {
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(quizRef.bottom)
width = Dimension.percent(0.5f)
}) {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
Image(
painter = painterResource(id = R.drawable.bg_house),
contentDescription = null,
alignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
)
Text(
text = "Bagian Rumah",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
color = ctextWhite
)
}
}
ButtonHome(onClick = { navController.navigate("navMaterial/animal") },
color = Color(0xff4264e7), modifier = Modifier
.constrainAs(screeningRef) {
top.linkTo(quizRef.bottom)
start.linkTo(quizRef.start)
width = Dimension.percent(0.5f)
}) {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
Image(
painter = painterResource(id = R.drawable.bg_animal),
contentDescription = null,
alignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
)
AutoSizeText(
text = "Pengenalan Hewan",
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
color = ctextWhite,
maxLines = 1
)
}
}
ButtonHome(onClick = { },
color = Color(0xffedb92f),
modifier = Modifier
.constrainAs(newsRef) {
top.linkTo(quizRef.bottom)
end.linkTo(parent.end)
width = Dimension.percent(0.5f)
}) {
Column(modifier = Modifier.padding(vertical = 10.dp)) {
Image(
painter = painterResource(id = R.drawable.bg_family),
contentDescription = null,
alignment = Alignment.Center,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 12.dp)
)
AutoSizeText(
text = "Anggota Keluarga",
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
color = ctextWhite,
maxLines = 1
)
}
}
}
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.ui.views.pHome
import androidx.lifecycle.ViewModel
class HomeViewModel: ViewModel() {
}

View File

@ -0,0 +1,117 @@
package com.example.lexilearn.ui.views.pLearAlphabet
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.ui.components.ButtonHome
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.HorizontalLine
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.ctextBlack
@Composable
fun LearnAlphabetScreen(navController: NavController) {
val viewModel: LearAlphabetViewModel = viewModel()
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = "Belajar Alfabet Inggris",
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(70.dp))
HorizontalLine()
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier
.fillMaxSize()
.padding(16.dp, 0.dp, 16.dp, 0.dp)
) {
// Gunakan itemsIndexed untuk iterasi menggunakan index dan item
itemsIndexed(viewModel.alphabetList) { index, model ->
// Setiap item di grid adalah Button dengan huruf dan cara bacaannya
ButtonHome(
onClick = { viewModel.speakLetter(model.alpha) },
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = model.alpha,
fontWeight = FontWeight.Bold,
fontSize = 30.sp,
color = ctextBlack
)
Text(
text = model.enRead,
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
color = cAccent
)
}
}
}
}
// Pesan jika TTS gagal diinisialisasi
if (!isTTSInitialized.value) {
Text(text = "TTS gagal diinisialisasi", color = cAccent)
}
// taruh grid disini
ButtonHome(onClick = { viewModel.speakLetter("A") }) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "A",
fontWeight = FontWeight.Bold,
fontSize = 30.sp,
color = ctextBlack
)
Text(
text = "ei",
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
color = cAccent
)
}
}
// Menampilkan loading atau pesan error jika TTS gagal
if (isTTSInitialized.value) {
// TTS berhasil diinisialisasi
} else {
Text(text = "TTS gagal diinisialisasi", color = cAccent)
}
}
}
}
}

View File

@ -0,0 +1,50 @@
package com.example.lexilearn.ui.views.pLearAlphabet
import android.app.Application
import android.speech.tts.TextToSpeech
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.State
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.example.lexilearn.utils.generateAlphabetList
import java.util.*
class LearAlphabetViewModel(application: Application) : AndroidViewModel(application),
TextToSpeech.OnInitListener {
private var tts: TextToSpeech? = null
private val _isTTSInitialized = MutableLiveData(false)
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
val alphabetList = generateAlphabetList()
init {
// Inisialisasi TextToSpeech saat ViewModel pertama kali diinisialisasi
tts = TextToSpeech(application, this)
}
// Fungsi yang dipanggil ketika TextToSpeech sudah diinisialisasi
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
tts?.language = Locale.ENGLISH
_isTTSInitialized.value = true
} else {
_isTTSInitialized.value = false
}
}
// Fungsi untuk berbicara
fun speakLetter(letter: String) {
// Memastikan TextToSpeech sudah diinisialisasi
if (_isTTSInitialized.value == true) {
tts?.speak(letter, TextToSpeech.QUEUE_FLUSH, null, null)
}
}
// Jangan lupa untuk melepaskan resource saat ViewModel dihancurkan
override fun onCleared() {
tts?.stop()
tts?.shutdown()
super.onCleared()
}
}

View File

@ -0,0 +1,49 @@
package com.example.lexilearn.ui.views.pLearnNumber
import android.app.Application
import android.speech.tts.TextToSpeech
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.example.lexilearn.utils.generateAlphabetList
import com.example.lexilearn.utils.generateNumberList
import java.util.*
class LearNumberViewModel(application: Application) : AndroidViewModel(application),
TextToSpeech.OnInitListener {
private var tts: TextToSpeech? = null
private val _isTTSInitialized = MutableLiveData(false)
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
val alphabetList = generateNumberList()
init {
// Inisialisasi TextToSpeech saat ViewModel pertama kali diinisialisasi
tts = TextToSpeech(application, this)
}
// Fungsi yang dipanggil ketika TextToSpeech sudah diinisialisasi
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
tts?.language = Locale.ENGLISH
_isTTSInitialized.value = true
} else {
_isTTSInitialized.value = false
}
}
// Fungsi untuk berbicara
fun speakLetter(letter: String) {
// Memastikan TextToSpeech sudah diinisialisasi
if (_isTTSInitialized.value == true) {
tts?.speak(letter, TextToSpeech.QUEUE_FLUSH, null, null)
}
}
// Jangan lupa untuk melepaskan resource saat ViewModel dihancurkan
override fun onCleared() {
tts?.stop()
tts?.shutdown()
super.onCleared()
}
}

View File

@ -0,0 +1,114 @@
package com.example.lexilearn.ui.views.pLearnNumber
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.ui.components.ButtonHome
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.HorizontalLine
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.ctextBlack
@Composable
fun LearnNumberScreen(navController: NavController) {
val viewModel: LearNumberViewModel = viewModel()
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = "Belajar Alfabet Inggris",
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(70.dp))
HorizontalLine()
Spacer(modifier = Modifier.height(120.dp))
LazyVerticalGrid(
columns = GridCells.Fixed(3),
modifier = Modifier
.fillMaxSize()
.padding(16.dp, 0.dp, 16.dp, 0.dp)
) {
// Gunakan itemsIndexed untuk iterasi menggunakan index dan item
itemsIndexed(viewModel.alphabetList) { index, model ->
// Setiap item di grid adalah Button dengan huruf dan cara bacaannya
ButtonHome(
onClick = { viewModel.speakLetter(model.alpha) },
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = model.alpha,
fontWeight = FontWeight.Bold,
fontSize = 30.sp,
color = ctextBlack
)
Text(
text = model.enRead,
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
color = cAccent
)
}
}
}
}
// Pesan jika TTS gagal diinisialisasi
if (!isTTSInitialized.value) {
Text(text = "TTS gagal diinisialisasi", color = cAccent)
}
// taruh grid disini
ButtonHome(onClick = { viewModel.speakLetter("A") }) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "A",
fontWeight = FontWeight.Bold,
fontSize = 30.sp,
color = ctextBlack
)
Text(
text = "ei",
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
color = cAccent
)
}
}
// Menampilkan loading atau pesan error jika TTS gagal
if (isTTSInitialized.value) {
// TTS berhasil diinisialisasi
} else {
Text(text = "TTS gagal diinisialisasi", color = cAccent)
}
}
}
}
}

View File

@ -0,0 +1,104 @@
package com.example.lexilearn.ui.views.pLogin
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.ui.components.CustomButton
import com.example.lexilearn.ui.components.EmailTextField
import com.example.lexilearn.ui.components.GradientLogin
import com.example.lexilearn.ui.components.LoginTextButton
import com.example.lexilearn.ui.components.PasswordTextField
import com.example.lexilearn.ui.theme.ctransTextWhite
@Composable
fun LoginScreen(navController: NavController) {
val viewModel: LoginViewModel = viewModel()
var email by remember { mutableStateOf(TextFieldValue("")) }
var password by remember { mutableStateOf(TextFieldValue("")) }
GradientLogin {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val (txtTitle, txtDesc, emailRef, passwordRef, loginButtonRef, registerTextRef) = createRefs()
Text(
text = stringResource(id = R.string.login),
color = ctransTextWhite,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.constrainAs(txtTitle){
bottom.linkTo(txtDesc.top, margin = 6.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
}
)
Text(
text = stringResource(id = R.string.logindesc),
color = ctransTextWhite,
fontSize = 16.sp,
modifier = Modifier.constrainAs(txtDesc){
bottom.linkTo(emailRef.top, margin = 44.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
}
)
EmailTextField(
value = email,
onValueChange = { email = it },
modifier = Modifier.constrainAs(emailRef) {
bottom.linkTo(passwordRef.top, margin = 12.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
}
)
PasswordTextField(
value = password,
onValueChange = { password = it },
modifier = Modifier.constrainAs(passwordRef) {
bottom.linkTo(loginButtonRef.top, margin = 16.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
}
)
CustomButton(
text = stringResource(id = R.string.login),
onClick = { navController.navigate("home") },
modifier = Modifier.constrainAs(loginButtonRef) {
bottom.linkTo(registerTextRef.top, margin = 32.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
})
LoginTextButton(
textBtn = stringResource(id = R.string.regis),
textHelper = stringResource(id = R.string.loginhave) + " ",
onclick = { navController.navigate("register") },
modifier = Modifier.constrainAs(registerTextRef) {
bottom.linkTo(parent.bottom, margin = 20.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.wrapContent
}
)
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.ui.views.pLogin
import androidx.lifecycle.ViewModel
class LoginViewModel: ViewModel() {
}

View File

@ -0,0 +1,136 @@
package com.example.lexilearn.ui.views.pNavMaterial
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.lexilearn.ui.components.FirebaseImage
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.HorizontalLine
import com.example.lexilearn.ui.theme.cAccent
import com.example.lexilearn.ui.theme.cGray
import com.example.lexilearn.ui.theme.ctextGray
import com.example.lexilearn.ui.theme.cwhite
import com.google.gson.Gson
@Composable
fun NavMaterialScreen(navController: NavController, materialId: String) {
val viewModel: NavMaterialViewModel = viewModel()
LaunchedEffect(materialId) {
viewModel.fetchMaterial(materialId)
}
val materialList by viewModel.materialList.collectAsState()
val isTTSInitialized = viewModel.isTTSInitialized.observeAsState(false)
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = "Belajar Hewan",
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(70.dp))
HorizontalLine()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp)
) {
items(materialList) { chunk ->
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
val (space, boxItem, centerLine) = createRefs()
Box(modifier = Modifier
.background(color = cwhite, shape = RoundedCornerShape(12.dp))
.constrainAs(boxItem) {
start.linkTo(parent.start)
top.linkTo(parent.top)
}
) {
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp).clickable {
println("testingkus")
// val jsonList = Gson().toJson(chunk) // 🔥 Konversi List ke JSON String
// navController.navigate("detailMaterial/$jsonList")
navController.currentBackStackEntry?.savedStateHandle?.set("materialList", chunk)
navController.navigate("detailMaterial")
},
) {
chunk.forEachIndexed { index, material ->
if(index!=1){
Column(horizontalAlignment = Alignment.CenterHorizontally) {
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
}
Spacer(modifier = Modifier.height(50.dp))
Text(text = "${material.enName}", color = cAccent, fontSize = 18.sp, fontWeight = FontWeight.Bold)
Text(text = "${material.idName}", color = ctextGray, fontSize = 14.sp, fontWeight = FontWeight.Bold)
}
}else{
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(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))
FirebaseImage(path = material.image, modifier = Modifier.size(80.dp))
}
}
}
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.background(color = cGray)
.height(2.dp)
.constrainAs(centerLine) {
top.linkTo(boxItem.top)
bottom.linkTo(boxItem.bottom)
})
Spacer(modifier = Modifier
.height(20.dp)
.constrainAs(space) {
top.linkTo(boxItem.bottom)
start.linkTo(boxItem.start)
})
}
}
}
}
}
}
}

View File

@ -0,0 +1,63 @@
package com.example.lexilearn.ui.views.pNavMaterial
import android.app.Application
import android.speech.tts.TextToSpeech
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.lexilearn.data.model.MaterialDataModel
import com.example.lexilearn.data.repository.MaterialRepository
import com.example.lexilearn.utils.generateNumberList
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import java.util.*
class NavMaterialViewModel(application: Application) : AndroidViewModel(application),
TextToSpeech.OnInitListener {
private var tts: TextToSpeech? = null
private val _isTTSInitialized = MutableLiveData(false)
val isTTSInitialized: LiveData<Boolean> = _isTTSInitialized
val alphabetList = generateNumberList()
private val repository = MaterialRepository(application)
private val _materialList = MutableStateFlow<List<List<MaterialDataModel>>>(emptyList())
val materialList: StateFlow<List<List<MaterialDataModel>>> = _materialList
fun fetchMaterial(materialId: String) {
viewModelScope.launch {
repository.getMaterialData { materials ->
val filteredMaterials = materials.filter { it.category == materialId }
_materialList.value = filteredMaterials.chunked(3)
}
}
}
init {
tts = TextToSpeech(application, this)
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
tts?.language = Locale.ENGLISH
_isTTSInitialized.value = true
} else {
_isTTSInitialized.value = false
}
}
fun speakLetter(letter: String) {
if (_isTTSInitialized.value == true) {
tts?.speak(letter, TextToSpeech.QUEUE_FLUSH, null, null)
}
}
override fun onCleared() {
tts?.stop()
tts?.shutdown()
super.onCleared()
}
}

View File

@ -0,0 +1,398 @@
package com.example.lexilearn.ui.views.pQuiz.pRead
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.domain.models.ModelAnswerRead
import com.example.lexilearn.domain.models.ModelWords
import com.example.lexilearn.ui.components.ButtonNext
import com.example.lexilearn.ui.components.CardQuiz
import com.example.lexilearn.ui.components.DraggableAnswerCard
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.HorizontalLine
import com.example.lexilearn.ui.components.MyShadowCard
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.ctextWhite
import kotlin.math.roundToInt
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun ReadScreen(navController: NavController) {
var rectColumnAnswer by remember { mutableStateOf(Rect.Zero) }
val cardWidth = remember {
mutableStateMapOf<Int, Dp>()
}
val cardHeight = remember {
mutableStateMapOf<Int, Dp>()
}
val maxWidthR = 280.dp
val maxHeightR = 60.dp
val minWidtR = 90.dp
val minHeightR = 40.dp
var dataQuiz = remember {
mutableStateListOf(
ModelWords(1, false, "The dog ", showCard = false),
ModelWords(2, true, "?", showCard = false),
ModelWords(3, false, " and The Cat ", showCard = false),
ModelWords(4, true, "?", showCard = false),
)
}
val listAnswer =
remember {
mutableStateListOf(
ModelAnswerRead(1, "chases"),
ModelAnswerRead(2, "run"),
ModelAnswerRead(3, "watches")
)
}
val quizXOffset = remember {
mutableStateMapOf<Int, Float>()
}
val quizYOffset = remember {
mutableStateMapOf<Int, Float>()
}
val boxRectDragable = remember {
mutableStateMapOf<Int, Rect>()
}
val boxRectQuiz = remember {
mutableStateMapOf<Int, Rect>()
}
val answerXOffset = remember {
mutableStateMapOf<Int, Float>()
}
val answerYOffset = remember {
mutableStateMapOf<Int, Float>()
}
val boxRectAnswer = remember {
mutableStateMapOf<Int, Rect>()
}
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = "Learn to Read",
modifier = Modifier.fillMaxSize()
) {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val (buttonRef) = createRefs()
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
Text(
"Level: 3",
modifier = Modifier.padding(22.dp),
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
color = ctextWhite
)
}
MyShadowCard(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
) {
FlowRow(
modifier = Modifier.padding(12.dp),
) {
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
if (dt.type) {
CardQuiz(
modifier = Modifier
.padding(vertical = 10.dp)
.width(minWidtR)
.height(minHeightR)
.onGloballyPositioned { coordinates ->
boxRectQuiz[id] = coordinates.boundsInWindow()
}
) {
Text(
text = dt.data, // Use the state to display text
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
)
) {
dataQuiz = dataQuiz.apply {
this[ind] = this[ind].copy(
data = dt.data,
showCard = true,
emp = dt.emp,
hasContent = true
)
}
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
cardWidth[emDt] = maxWidthR
cardHeight[emDt] = maxHeightR
quizXOffset[id] = 0f
quizYOffset[id] = 0f
dt.data = "?"
}
}
if (!checkNull) {
quizXOffset[id] = 0f
quizYOffset[id] = 0f
}
}
)
}
)
}
}
} else {
Box(modifier = Modifier.align(Alignment.CenterVertically)) {
Text(
text = dt.data,
color = ctextBlack,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
)
}
}
}
}
}
Spacer(modifier = Modifier.height(30.dp))
HorizontalLine()
Spacer(modifier = Modifier.height(40.dp))
Column(
modifier = Modifier
.fillMaxSize()
.onGloballyPositioned {
rectColumnAnswer = it.boundsInWindow()
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
for (i in 0 until listAnswer.size) {
val item = listAnswer[i]
val id = item.id
if (!boxRectAnswer.containsKey(id))
boxRectAnswer[id] = Rect.Zero
if (!answerXOffset.containsKey(id))
answerXOffset[id] = 0f
if (!answerYOffset.containsKey(id))
answerYOffset[id] = 0f
if (!cardWidth.containsKey(id))
cardWidth[id] = maxWidthR
if (!cardHeight.containsKey(id))
cardHeight[id] = maxHeightR
if (item.showCard) {
DraggableAnswerCard(
item = item.data,
modifier = Modifier
.padding(vertical = 4.dp)
.offset {
IntOffset(
answerXOffset[id]!!.roundToInt(),
answerYOffset[id]!!.roundToInt()
)
}
.onGloballyPositioned {
boxRectAnswer[id] = it.boundsInWindow()
}
.width(cardWidth[id]!!)
.height(cardHeight[id]!!)
.pointerInput(Unit) {
detectDragGestures(
onDrag = { change, dragAmount ->
change.consume()
answerXOffset[id] =
answerXOffset[id]!! + dragAmount.x
answerYOffset[id] =
answerYOffset[id]!! + dragAmount.y
cardWidth[id] = minWidtR
cardHeight[id] = minHeightR
},
onDragEnd = {
var checkNull = false
for ((ind, entry) in boxRectQuiz.entries.withIndex()) {
val (_, rect) = entry
if (dataQuiz[ind].hasContent)
continue
if (boxRectAnswer[id]!!.overlaps(rect)) {
cardWidth[id] = minWidtR
cardHeight[id] = minHeightR
dataQuiz = dataQuiz
.apply {
this[ind] = this[ind].copy(
data = item.data,
showCard = true,
emp = id,
hasContent = true
)
}
checkNull = true
cardWidth[id] = 0.dp
cardHeight[id] = 0.dp
answerXOffset[id] = 0f
answerYOffset[id] = 0f
break
}
}
if (!checkNull) {
cardWidth[id] = maxWidthR
cardHeight[id] = maxHeightR
answerXOffset[id] = 0f
answerYOffset[id] = 0f
}
}
)
}
)
}
}
}
}
ButtonNext(
onclick = { navController.navigate("spell")
},
text = stringResource(id = R.string.next),
painter = painterResource(id = R.drawable.ic_next),
modifier = Modifier
.padding(vertical = 30.dp, horizontal = 50.dp)
.fillMaxWidth().constrainAs(buttonRef){
bottom.linkTo(parent.bottom)
}
)
}
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.ui.views.pQuiz.pRead
import androidx.lifecycle.ViewModel
class ReadViewModel: ViewModel() {
}

View File

@ -0,0 +1,425 @@
package com.example.lexilearn.ui.views.pQuiz.pSpell
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.example.lexilearn.domain.models.ModelAnswerRead
import com.example.lexilearn.ui.components.CardQuiz
import com.example.lexilearn.ui.components.DraggableAnswerCard
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.MyShadowCard
import com.example.lexilearn.ui.theme.ctextWhite
import kotlin.math.roundToInt
import com.example.lexilearn.R
import com.example.lexilearn.domain.models.ModelSpell
import com.example.lexilearn.ui.components.ButtonNext
import com.example.lexilearn.ui.theme.ctextGray
@Composable
fun SpellScreen(navController: NavController) {
var rectColumnAnswer by remember { mutableStateOf(Rect.Zero) }
val cardSize = remember {
mutableStateMapOf<Int, Dp>()
}
val maxSize = 120.dp
val minSize = 70.dp
var dataQuiz = remember {
mutableStateListOf(
ModelSpell(1, false, "r ", showCard = false),
ModelSpell(2, false, "i", showCard = false),
ModelSpell(3, true, "?", showCard = false),
ModelSpell(4, false, "e", showCard = false),
)
}
val listAnswer =
remember {
mutableStateListOf(
ModelAnswerRead(1, "a"),
ModelAnswerRead(2, "c"),
ModelAnswerRead(3, "d"),
ModelAnswerRead(4, "k")
)
}
val quizXOffset = remember {
mutableStateMapOf<Int, Float>()
}
val quizYOffset = remember {
mutableStateMapOf<Int, Float>()
}
val boxRectDragable = remember {
mutableStateMapOf<Int, Rect>()
}
val boxRectQuiz = remember {
mutableStateMapOf<Int, Rect>()
}
val answerXOffset = remember {
mutableStateMapOf<Int, Float>()
}
val answerYOffset = remember {
mutableStateMapOf<Int, Float>()
}
val boxRectAnswer = remember {
mutableStateMapOf<Int, Rect>()
}
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = stringResource(id = R.string.spelltitle),
modifier = Modifier.fillMaxSize()
) {
ConstraintLayout {
val buttonRef = createRef()
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
Text(
"Level: 3",
modifier = Modifier.padding(22.dp),
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
color = ctextWhite
)
}
MyShadowCard(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(12.dp))
Text(
text = "Susun Kata",
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
Spacer(modifier = Modifier.height(12.dp))
Image(
painter = painterResource(id = R.drawable.ic_news),
contentDescription = "image",
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceAround
) {
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
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, // Use the state to display text
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
)
) {
dataQuiz =
dataQuiz.apply {
this[ind] =
this[ind].copy(
data = dt.data,
showCard = true,
emp = dt.emp,
hasContent = true
)
}
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.align(Alignment.CenterVertically)) {
Text(
text = dt.data,
color = ctextWhite,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
)
}
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
}
}
Spacer(modifier = Modifier.height(12.dp))
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(ctextGray)
)
Spacer(modifier = Modifier.height(12.dp))
Column(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned {
rectColumnAnswer = it.boundsInWindow()
},
) {
listAnswer.chunked(2).forEach { rowItems ->
Row(
modifier = Modifier.fillMaxWidth(),
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(10.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
dataQuiz = dataQuiz
.apply {
this[ind] =
this[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
}
}
)
}
)
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
}
ButtonNext(
onclick = {
navController.navigate("write")
},
text = stringResource(id = R.string.next),
painter = painterResource(id = R.drawable.ic_next),
modifier = Modifier
.padding(vertical = 30.dp, horizontal = 50.dp)
.fillMaxWidth().constrainAs(buttonRef){
bottom.linkTo(parent.bottom)
}
)
}
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.ui.views.pQuiz.pSpell
import androidx.lifecycle.ViewModel
class SpellViewModel: ViewModel() {
}

View File

@ -0,0 +1,242 @@
package com.example.lexilearn.ui.views.pQuiz.pWrite
import DrawBox
import android.graphics.Bitmap
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.domain.models.ModelAnswerRead
import com.example.lexilearn.domain.models.ModelSpell
import com.example.lexilearn.ui.components.AutoSizeText
import com.example.lexilearn.ui.components.ButtonNext
import com.example.lexilearn.ui.components.CardQuiz
import com.example.lexilearn.ui.components.CustomButton
import com.example.lexilearn.ui.components.GradientQuiz
import com.example.lexilearn.ui.components.MyShadowCard
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.ctextGray
import com.example.lexilearn.ui.theme.ctextWhite
@Composable
fun WriteScreen(navController: NavController) {
val minSize = 70.dp
var pathReady by remember { mutableStateOf<android.graphics.Path?>(null) }
var canvasSize by remember { mutableStateOf(androidx.compose.ui.geometry.Size.Zero) }
// gunakan variabel ini untuk mengambil hasil gambar
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
var dataQuiz = remember {
mutableStateListOf(
ModelSpell(1, false, "r ", showCard = false),
ModelSpell(2, false, "i", showCard = false),
ModelSpell(3, true, "?", showCard = false),
ModelSpell(4, false, "e", showCard = false),
)
}
Surface(modifier = Modifier.fillMaxSize()) {
GradientQuiz(
navController = navController,
headerText = stringResource(id = R.string.spelltitle),
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
) {
Text(
"Level: 3",
modifier = Modifier.padding(22.dp),
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
color = ctextWhite
)
}
MyShadowCard(
modifier = Modifier
.padding(12.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(12.dp))
Text(text = "Susun Kata", fontSize = 20.sp, fontWeight = FontWeight.Bold)
Spacer(modifier = Modifier.height(12.dp))
Image(
painter = painterResource(id = R.drawable.ic_news),
contentDescription = "image",
modifier = Modifier.size(120.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceAround
) {
dataQuiz.forEach { dt ->
CardQuiz(
modifier = Modifier
.padding(vertical = 10.dp)
.size(minSize)
) {
if (dt.type) {
Text(
text = dt.data,
color = ctextWhite,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
} else {
Box(modifier = Modifier.align(Alignment.CenterVertically)) {
Text(
text = dt.data,
color = ctextWhite,
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
textAlign = TextAlign.Center,
)
}
}
}
}
}
Spacer(modifier = Modifier.height(12.dp))
Spacer(
modifier = Modifier
.fillMaxWidth()
.height(1.dp)
.background(ctextGray)
)
Spacer(modifier = Modifier.height(12.dp))
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
val (drawRef, textBoxRef) = createRefs()
DrawBox(
modifier = Modifier
.size(300.dp, 300.dp)
.constrainAs(drawRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
},
onPathReady = { path, size ->
pathReady = path
canvasSize = size
}
)
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(300.dp)
.constrainAs(textBoxRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
}) {
AutoSizeText(
text = "c",
fontFamily = FontFamily(
Font(R.font.raleway_dots),
),
textAlign = TextAlign.Center,
color = ctextGray,
modifier = Modifier.fillMaxSize()
)
}
}
Spacer(modifier = Modifier.height(12.dp))
ButtonNext(
onclick = {
// fungsi untuk mengubah coretan menjadi bitmap
pathReady?.let { path ->
val newBitmap = Bitmap.createBitmap(
canvasSize.width.toInt(),
canvasSize.height.toInt(),
Bitmap.Config.ARGB_8888
)
val canvas = android.graphics.Canvas(newBitmap)
canvas.drawColor(android.graphics.Color.WHITE)
canvas.drawPath(path, android.graphics.Paint().apply {
isAntiAlias = true
color = android.graphics.Color.BLACK
style = android.graphics.Paint.Style.STROKE
strokeJoin = android.graphics.Paint.Join.ROUND
strokeCap = android.graphics.Paint.Cap.ROUND
strokeWidth = 5f
})
bitmap = newBitmap
}
},
text = stringResource(id = R.string.next),
painter = painterResource(id = R.drawable.ic_next),
modifier = Modifier
.padding(vertical = 30.dp, horizontal = 50.dp)
.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
//contoh jika ingin menampilkan gambar
// bitmap?.let {
// Image(
// bitmap = it.asImageBitmap(),
// contentDescription = null,
// modifier = Modifier.size(300.dp)
// )
// }
}
}
}
}
}
}

View File

@ -0,0 +1,118 @@
package com.example.lexilearn.ui.views.pRegister
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.lexilearn.ui.components.CustomButton
import com.example.lexilearn.ui.components.GradientRegister
import com.example.lexilearn.ui.components.LoginTextButton
import com.example.lexilearn.ui.components.NameTextField
import com.example.lexilearn.R
import com.example.lexilearn.ui.components.EmailTextField
import com.example.lexilearn.ui.components.PasswordTextField
import com.example.lexilearn.ui.theme.ctransTextWhite
@Composable
fun RegisterScreen(navController: NavController) {
val viewModel: RegisterViewModel = viewModel()
var name by remember { mutableStateOf(TextFieldValue("")) }
var email by remember { mutableStateOf(TextFieldValue("")) }
var password by remember { mutableStateOf(TextFieldValue("")) }
GradientRegister {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val (txtTitle, txtDesc, nameRef, emailRef, passwordRef, registerRef, loginRef) = createRefs()
Text(
text = stringResource(id = R.string.regissignup),
color = ctransTextWhite,
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
modifier = Modifier.constrainAs(txtTitle) {
bottom.linkTo(txtDesc.top, margin = 6.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
}
)
Text(
text = stringResource(id = R.string.regisdesc),
color = ctransTextWhite,
fontSize = 16.sp,
modifier = Modifier.constrainAs(txtDesc) {
bottom.linkTo(nameRef.top, margin = 44.dp)
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
width = Dimension.fillToConstraints
}
)
NameTextField(
placeholder = stringResource(id = R.string.fullname),
value = name,
onValueChange = { name = it },
ic = R.drawable.ic_user,
modifier = Modifier.constrainAs(nameRef) {
bottom.linkTo(emailRef.top, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
start.linkTo(parent.start, margin = 12.dp)
width = Dimension.fillToConstraints
})
EmailTextField(
value = email,
onValueChange = { email = it },
modifier = Modifier.constrainAs(emailRef) {
bottom.linkTo(passwordRef.top, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
start.linkTo(parent.start, margin = 12.dp)
width = Dimension.fillToConstraints
})
PasswordTextField(
value = password,
onValueChange = { password = it },
modifier = Modifier.constrainAs(passwordRef) {
bottom.linkTo(registerRef.top, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
start.linkTo(parent.start, margin = 12.dp)
width = Dimension.fillToConstraints
})
CustomButton(
text = stringResource(id = R.string.regis),
onClick = { },
modifier = Modifier.constrainAs(registerRef) {
bottom.linkTo(loginRef.top, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
start.linkTo(parent.start, margin = 12.dp)
width = Dimension.fillToConstraints
})
LoginTextButton(
textHelper = stringResource(id = R.string.regishave) + " ",
textBtn = stringResource(id = R.string.login),
onclick = { navController.popBackStack() },
modifier = Modifier.constrainAs(loginRef) {
start.linkTo(parent.start, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
bottom.linkTo(parent.bottom, margin = 20.dp)
width = Dimension.wrapContent
}
)
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.ui.views.pRegister
import androidx.lifecycle.ViewModel
class RegisterViewModel: ViewModel() {
}

View File

@ -0,0 +1,143 @@
package com.example.lexilearn.ui.views.pResultScreening
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.ui.components.GradientScreening
import com.example.lexilearn.ui.theme.ctextGray
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.RoundRect
import androidx.compose.ui.graphics.Path
import androidx.constraintlayout.compose.Dimension
import com.example.lexilearn.ui.components.CustomButton
import com.example.lexilearn.ui.theme.cprimary
import com.example.lexilearn.ui.theme.ctextBlack
import com.example.lexilearn.ui.theme.cwhite
@Composable
fun ResultScreeningScreen(navController: NavController) {
GradientScreening(
backButton = { navController.popBackStack() },
headerText = stringResource(id = R.string.rescreentitle),
modifier = Modifier.fillMaxSize()
) {
ConstraintLayout(
modifier = Modifier
.fillMaxSize()
.padding(top = 60.dp)
) {
val (lineRef, boxRef, buttonRef) = createRefs()
Spacer(modifier = Modifier
.fillMaxWidth()
.height(1.5.dp)
.background(ctextGray)
.constrainAs(lineRef) {
top.linkTo(parent.top)
}
)
Box(modifier = Modifier.constrainAs(boxRef) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}) {
ConstraintLayout(modifier = Modifier.padding(bottom = 24.dp)) {
val (topRef, bottomRef) = createRefs()
Box(
modifier = Modifier
.size(200.dp)
.clip(CircleShape)
.background(cwhite)
.constrainAs(topRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Spacer(modifier = Modifier.height(30.dp))
Text(
text = "Dyslexia",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = ctextBlack
)
Text(
text = "66%",
fontSize = 50.sp,
fontWeight = FontWeight.Bold,
color = cprimary,
modifier = Modifier.padding(top = 10.dp)
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 40.dp)
.offset(y = (-40).dp)
.background(cwhite, shape = RoundedCornerShape(16.dp))
.constrainAs(bottomRef) {
top.linkTo(topRef.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "Anda menjawab \"ya\" pada 2 dari 3 pertanyaan yang menunjukkan 66% kemungkinan Anda menderita disleksia.",
fontSize = 16.sp,
color = ctextBlack,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 24.dp)
)
Text(
text = stringResource(id = R.string.rescreendesc),
fontSize = 14.sp,
color = ctextGray,
textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 16.dp, bottom = 20.dp)
)
}
}
}
}
CustomButton(
text = stringResource(id = R.string.close),
onClick = { },
modifier = Modifier.padding(horizontal = 24.dp, vertical = 12.dp).constrainAs(buttonRef) {
bottom.linkTo(parent.bottom, margin = 12.dp)
end.linkTo(parent.end, margin = 12.dp)
start.linkTo(parent.start, margin = 12.dp)
width = Dimension.fillToConstraints
})
}
}
}

View File

@ -0,0 +1,93 @@
package com.example.lexilearn.ui.views.pScreening
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.domain.models.ModelScreening
import com.example.lexilearn.ui.components.ButtonNext
import com.example.lexilearn.ui.components.CardScreening
import com.example.lexilearn.ui.components.GradientScreening
import com.example.lexilearn.ui.theme.ctextGray
@Composable
fun ScreeningScreen(navController: NavController) {
val itemQuestion = remember {
mutableStateListOf(
ModelScreening(1, "Is there a family history of learning disorders?", 0),
ModelScreening(1, "Is there a family history of learning disorders?", 0),
ModelScreening(1, "Is there a family history of learning disorders?", 0),
ModelScreening(1, "Is there a family history of learning disorders?", 0),
ModelScreening(1, "Is there a family history of learning disorders?", 0),
ModelScreening(1, "Is there a family history of learning disorders?", 0),
)
}
GradientScreening(
backButton = { navController.popBackStack() },
headerText = stringResource(id = R.string.screentitle),
modifier = Modifier.fillMaxSize()
) {
ConstraintLayout(
modifier = Modifier
.fillMaxSize()
.padding(top = 60.dp)
) {
val (lineRef, lazyRef, buttonRef) = createRefs()
Spacer(modifier = Modifier
.fillMaxWidth()
.height(1.5.dp)
.background(ctextGray)
.constrainAs(lineRef) {
top.linkTo(parent.top)
})
LazyColumn(
contentPadding = PaddingValues(16.dp),
modifier = Modifier.constrainAs(lazyRef) {
top.linkTo(lineRef.bottom)
}) {
itemsIndexed(itemQuestion) { index, item ->
CardScreening(
question = "${index + 1}. ${item.question}",
onOptionSelected = { selectOption ->
itemQuestion[index].answer = selectOption
})
}
item {
ButtonNext(
onclick = { navController.navigate("resultscreening") },
text = stringResource(id = R.string.screensend),
painter = painterResource(id = R.drawable.ic_send),
modifier = Modifier
.padding(vertical = 30.dp, horizontal = 50.dp)
.fillMaxWidth()
)
}
}
Box(modifier = Modifier
.padding(20.dp)
.constrainAs(buttonRef) {
bottom.linkTo(parent.bottom)
end.linkTo(parent.end)
}) {
}
}
}
}

View File

@ -0,0 +1,6 @@
package com.example.lexilearn.ui.views.pScreening
import androidx.lifecycle.ViewModel
class ScreeningViewModel: ViewModel() {
}

View File

@ -0,0 +1,84 @@
package com.example.lexilearn.ui.views.pSplashcreen
import android.os.Handler
import android.os.Looper
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.example.lexilearn.R
import com.example.lexilearn.ui.components.GradientSplash
import com.example.lexilearn.ui.theme.ctextGray
import com.example.lexilearn.ui.theme.ctextWhite
@Composable
fun SplashScreen(navController: NavController) {
LaunchedEffect(Unit) {
Handler(Looper.getMainLooper()).postDelayed({
navController.navigate("login")
}, 500) // Delay for 3 seconds
}
GradientSplash {
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val (image, title, byText, author) = createRefs()
Image(
painter = painterResource(id = R.drawable.img_logo), // Replace with your logo drawable resource
contentDescription = "App Logo",
modifier = Modifier
.size(150.dp) // Adjust the size as needed
.constrainAs(image) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom, margin = 50.dp)
}
)
Text(
text = "LexiLearn",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = ctextWhite, // Using color from theme
modifier = Modifier.constrainAs(title) {
top.linkTo(image.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Text(
text = "by",
fontSize = 16.sp,
color = ctextGray, // Using color from theme
modifier = Modifier.constrainAs(byText) {
bottom.linkTo(author.top, margin = 4.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Text(
text = "MPUS BEKERJA",
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
color = ctextWhite, // Using color from theme
modifier = Modifier.constrainAs(author) {
bottom.linkTo(parent.bottom, margin = 16.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
}
}
}

View File

@ -0,0 +1,35 @@
package com.example.lexilearn.utils
import com.example.lexilearn.domain.models.ModelAlphabetNumber
fun generateAlphabetList(): List<ModelAlphabetNumber> {
return listOf(
ModelAlphabetNumber("A", "ei"),
ModelAlphabetNumber("B", "bi"),
ModelAlphabetNumber("C", "si"),
ModelAlphabetNumber("D", "di"),
ModelAlphabetNumber("E", "i"),
ModelAlphabetNumber("F", "ef"),
ModelAlphabetNumber("G", "dji"),
ModelAlphabetNumber("H", "eych"),
ModelAlphabetNumber("I", "ai"),
ModelAlphabetNumber("J", "djei"),
ModelAlphabetNumber("K", "kei"),
ModelAlphabetNumber("L", "el"),
ModelAlphabetNumber("M", "em"),
ModelAlphabetNumber("N", "en"),
ModelAlphabetNumber("O", "ou"),
ModelAlphabetNumber("P", "pi"),
ModelAlphabetNumber("Q", "kiu"),
ModelAlphabetNumber("R", "ar"),
ModelAlphabetNumber("S", "es"),
ModelAlphabetNumber("T", "ti"),
ModelAlphabetNumber("U", "ju"),
ModelAlphabetNumber("V", "vi"),
ModelAlphabetNumber("W", "double-u"),
ModelAlphabetNumber("X", "eks"),
ModelAlphabetNumber("Y", "wai"),
ModelAlphabetNumber("Z", "zed")
)
}

View File

@ -0,0 +1,17 @@
package com.example.lexilearn.utils
import com.example.lexilearn.domain.models.ModelAlphabetNumber
fun generateNumberList(): List<ModelAlphabetNumber> {
return listOf(
ModelAlphabetNumber("1", "one"),
ModelAlphabetNumber("2", "two"),
ModelAlphabetNumber("3", "three"),
ModelAlphabetNumber("4", "four"),
ModelAlphabetNumber("5", "five"),
ModelAlphabetNumber("6", "six"),
ModelAlphabetNumber("7", "seven"),
ModelAlphabetNumber("8", "eight"),
ModelAlphabetNumber("9", "nine"),
)
}

View File

@ -0,0 +1,16 @@
package com.example.lexilearn.utils
import com.google.firebase.storage.FirebaseStorage
fun getFirebaseImageUrl(path: String, callback: (String?) -> Unit) {
val storageReference = FirebaseStorage.getInstance().reference.child(path)
storageReference.downloadUrl
.addOnSuccessListener { uri ->
println("🔥 towtow Firebase Image URL: $uri") // 🔥 Debug: Cek URL yang dihasilkan
callback(uri.toString()) // ✅ Kirim URL ke callback
}
.addOnFailureListener { exception ->
println("❌ towtow ${path} Firebase Error: ${exception.message}") // 🔥 Debug: Cek jika error terjadi
callback(null) // ❌ Jika gagal, callback NULL
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#F4F4F4"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#F4F4F4"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#F4F4F4"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.07,12.85c0.77,-1.39 2.25,-2.21 3.11,-3.44c0.91,-1.29 0.4,-3.7 -2.18,-3.7c-1.69,0 -2.52,1.28 -2.87,2.34L6.54,6.96C7.25,4.83 9.18,3 11.99,3c2.35,0 3.96,1.07 4.78,2.41c0.7,1.15 1.11,3.3 0.03,4.9c-1.2,1.77 -2.35,2.31 -2.97,3.45c-0.25,0.46 -0.35,0.76 -0.35,2.24h-2.89C10.58,15.22 10.46,13.95 11.07,12.85zM14,20c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2S14,18.9 14,20z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="32dp"
android:tint="#1B162B" android:viewportHeight="24"
android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M15.41,16.59L10.83,12l4.58,-4.59L14,6l-6,6 6,6 1.41,-1.41z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#F4F4F4"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#8D8D8D"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="32dp"
android:tint="#1B162B" android:viewportHeight="24"
android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#8D8D8D"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#1B162B" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#DCDCDC"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#8D8D8D"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#8D8D8D"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#8D8D8D"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Some files were not shown because too many files have changed in this diff Show More