feat: Add new drawables and project configuration files

This commit is contained in:
Cutiful 2025-05-19 21:22:41 +07:00
parent 7e4a678e56
commit a58fb72209
7 changed files with 317 additions and 187 deletions

View File

@ -6,27 +6,15 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import com.syaroful.agrilinkvocpro.core.components.DownloadModuleConfirmationDialog
import com.syaroful.agrilinkvocpro.core.components.DownloadProgressDialog
import com.syaroful.agrilinkvocpro.core.components.MenuItemButton
import com.syaroful.agrilinkvocpro.ui.pages.GreenHouseInformationSection
import com.syaroful.agrilinkvocpro.core.utils.DownloadState
import com.syaroful.agrilinkvocpro.presentation.dynamicModule.DynamicModuleViewModel
import com.syaroful.agrilinkvocpro.ui.screen.home.HomeScreen
import com.syaroful.agrilinkvocpro.ui.theme.AgrilinkVocproTheme
import com.syaroful.agrilinkvocpro.viewModel.DynamicModuleViewModel
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
@ -42,20 +30,36 @@ class MainActivity : ComponentActivity() {
private val progressMessage = mutableStateOf("")
private val progressPercent = mutableFloatStateOf(0f)
private var dialogState = mutableStateOf(false)
private val currentModuleToDownload = mutableStateOf<String?>(null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
AgrilinkVocproTheme {
HomeScreen()
HomeScreen(
onControlFeatureClick = { onFeatureClicked("control_feature") },
onRecipeFeatureClick = { onFeatureClicked("recipe_feature") },
onPricePredictionFeatureClick = { onFeatureClicked("price_prediction_feature") },
onPlantDiseaseDetectionFeatureClick = { onFeatureClicked("diseasedetection_feature") },
dialogState = currentModuleToDownload
)
DownloadProgressDialog(
showDialog = showProgressDialog.value,
message = progressMessage.value,
progress = progressPercent.floatValue,
onDismiss = { showProgressDialog.value = false }
)
DownloadModuleConfirmationDialog(
moduleName = currentModuleToDownload.value,
onDismiss = { currentModuleToDownload.value = null },
onClickConfirm = {
currentModuleToDownload.value?.let { moduleName ->
downloadDynamicModule(moduleName)
}
currentModuleToDownload.value = null
}
)
}
}
@ -111,85 +115,43 @@ class MainActivity : ComponentActivity() {
}
}
private fun onFeatureClicked(moduleName: String) {
if (viewModel.isModuleDownloaded(moduleName)) {
startFeatureActivity(moduleName)
} else {
currentModuleToDownload.value = moduleName
}
}
private fun delayAndDismissDialog() {
lifecycleScope.launch {
kotlinx.coroutines.delay(1500)
showProgressDialog.value = false
openControlFeature()
currentModuleToDownload.value?.let { startFeatureActivity(it) }
currentModuleToDownload.value = null
}
}
@Composable
fun HomeScreen() {
AgrilinkVocproTheme {
Scaffold { padding ->
Column(
modifier = Modifier
.padding(padding)
.fillMaxWidth()
) {
GreenHouseInformationSection()
Spacer(modifier = Modifier.height(20.dp))
DynamicFeatureSection()
Spacer(modifier = Modifier.height(32.dp))
DownloadModuleConfirmationDialog(dialogState, ::downloadDynamicModule)
}
}
}
private fun downloadDynamicModule(moduleName: String) {
viewModel.downloadModule(moduleName)
}
@Composable
fun DynamicFeatureSection() {
Row(
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
MenuItemButton(
label = "Kontrol\nAktuator",
icon = painterResource(id = R.drawable.control_actuator_icon),
onClick = { openControlFeature() })
MenuItemButton(
label = "Resep\nPertumbuhan",
icon = painterResource(id = R.drawable.growth_recipe_icon),
onClick = {}
)
MenuItemButton(
label = "Harga\nKomoditas",
icon = painterResource(id = R.drawable.commodity_price_prediction_icon),
onClick = {}
)
MenuItemButton(
label = "Deteksi\nPenyakit",
icon = painterResource(id = R.drawable.plant_disease_detection_icon),
onClick = {}
)
private fun startFeatureActivity(moduleName: String) {
val activityClassName = when (moduleName) {
"control_feature" -> "com.syaroful.agrilinkvocpro.control_feature.ControlActuatorActivity"
"recipe_feature" -> "com.syaroful.agrilinkvocpro.recipe_feature.RecipeActivity"
"price_prediction_feature" -> "com.syaroful.agrilinkvocpro.price_prediction_feature.PricePredictionActivity"
"plant_disease_detection_feature" -> "com.syaroful.agrilinkvocpro.diseasedetection_feature.PlantDiseaseDetectionActivity"
else -> null
}
}
private fun openControlFeature() {
if (viewModel.isModuleDownloaded(CONTROL_FEATURE_MODULE_NAME)) {
startControlActuatorActivity()
} else {
dialogState.value = true
}
}
private fun downloadDynamicModule() {
viewModel.downloadModule(CONTROL_FEATURE_MODULE_NAME)
}
private fun startControlActuatorActivity() {
activityClassName?.let {
val intent = Intent().apply {
setClassName(
"com.syaroful.agrilinkvocpro",
"com.syaroful.agrilinkvocpro.control_feature.ControlActuatorActivity"
)
setClassName("com.syaroful.agrilinkvocpro", it)
}
startActivity(intent)
}
}
}

View File

@ -6,9 +6,6 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -17,8 +14,20 @@ import androidx.compose.ui.unit.dp
import com.syaroful.agrilinkvocpro.R
@Composable
fun DownloadModuleConfirmationDialog(state: MutableState<Boolean>, onClickConfirm: () -> Unit) {
if (!state.value) return
fun DownloadModuleConfirmationDialog(
moduleName: String?,
onDismiss: () -> Unit,
onClickConfirm: () -> Unit
) {
if (moduleName == null) return
val message = when (moduleName) {
"control_feature" -> "Apakah Anda ingin mendownload fitur Kontrol Aktuator?"
"recipe_feature" -> "Apakah Anda ingin mendownload fitur Resep Pertumbuhan?"
"price_prediction_feature" -> "Apakah Anda ingin mendownload fitur Prediksi Harga Komoditas?"
"plant_disease_detection_feature" -> "Apakah Anda ingin mendownload fitur Deteksi Penyakit Tanaman?"
else -> "Apakah Anda ingin mendownload modul ini?"
}
AlertDialog(
icon = {
@ -28,23 +37,27 @@ fun DownloadModuleConfirmationDialog(state: MutableState<Boolean>, onClickConfir
modifier = Modifier.height(70.dp),
)
},
title = { Text(stringResource(id = R.string.download_module_title)) },
text = { Text(stringResource(id = R.string.download_module_message)) },
onDismissRequest = { state.value = false },
title = {
Text(
stringResource(id = R.string.download_module_title),
style = textTheme.bodyLarge,
)
},
text = { Text(message) },
onDismissRequest = onDismiss,
confirmButton = {
TextButton(
onClick = {
onClickConfirm.invoke()
state.value = false
onClickConfirm()
onDismiss()
}
) {
Text(stringResource(id = R.string.download))
}
},
dismissButton = {
TextButton(
onClick = { state.value = false }
) {
TextButton(onClick = onDismiss) {
Text(stringResource(id = R.string.cancel))
}
}
@ -54,12 +67,10 @@ fun DownloadModuleConfirmationDialog(state: MutableState<Boolean>, onClickConfir
@Preview(showBackground = true)
@Composable
fun PreviewDownloadModuleConfirmationDialog() {
// Use remember just for preview context
val showDialog = remember { mutableStateOf(true) }
// Provide fake implementation for confirmation click
DownloadModuleConfirmationDialog(
state = showDialog,
onClickConfirm = { /* No-op for preview */ }
moduleName = "price_prediction_feature",
onClickConfirm = {},
onDismiss = {}
)
}

View File

@ -1,4 +1,4 @@
package com.syaroful.agrilinkvocpro
package com.syaroful.agrilinkvocpro.core.utils
sealed class DownloadState {
data object Idle : DownloadState()

View File

@ -1,4 +1,4 @@
package com.syaroful.agrilinkvocpro.di
package com.syaroful.agrilinkvocpro.data.repository
import com.google.android.gms.tasks.Task
import com.google.android.play.core.splitinstall.SplitInstallManager

View File

@ -1,12 +1,12 @@
package com.syaroful.agrilinkvocpro.viewModel
package com.syaroful.agrilinkvocpro.presentation.dynamicModule
import androidx.lifecycle.ViewModel
import com.google.android.play.core.splitinstall.SplitInstallException
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
import com.google.android.play.core.splitinstall.model.SplitInstallErrorCode
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus
import com.syaroful.agrilinkvocpro.DownloadState
import com.syaroful.agrilinkvocpro.di.DynamicModuleRepository
import com.syaroful.agrilinkvocpro.core.utils.DownloadState
import com.syaroful.agrilinkvocpro.data.repository.DynamicModuleRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

View File

@ -1,79 +0,0 @@
package com.syaroful.agrilinkvocpro.ui.pages
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
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.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.syaroful.agrilinkvocpro.R
import com.syaroful.agrilinkvocpro.core.components.MenuItemButton
import com.syaroful.agrilinkvocpro.core.components.textTheme
import com.syaroful.agrilinkvocpro.ui.theme.MainGreen
@Composable
fun GreenHouseInformationSection() {
Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) {
IconButton(
onClick = {},
modifier = Modifier.align(Alignment.CenterVertically)
) {
Icon(
imageVector = Icons.Default.Person,
contentDescription = "profile",
tint = MainGreen
)
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(20.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(text = "2 Komoditas", color = MainGreen, style = textTheme.bodyMedium)
Spacer(modifier = Modifier.height(24.dp))
Text(text = "Green House Bumiaji", style = textTheme.bodyLarge)
Text(
text = "Jl. Kopral Kasdi 2, Bulukerto, Kec. Bumiaji, Kota Batu, Jawa Timur 65334 ",
style = textTheme.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Spacer(modifier = Modifier.width(24.dp))
Image(
painter = painterResource(id = R.drawable.green_house_image),
contentDescription = "profile",
modifier = Modifier
.size(90.dp)
.clip(RoundedCornerShape(10.dp))
)
}
}

View File

@ -0,0 +1,236 @@
package com.syaroful.agrilinkvocpro.ui.screen.home
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Person
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.syaroful.agrilinkvocpro.R
import com.syaroful.agrilinkvocpro.core.components.MenuItemButton
import com.syaroful.agrilinkvocpro.core.components.textTheme
import com.syaroful.agrilinkvocpro.ui.theme.AgrilinkVocproTheme
import com.syaroful.agrilinkvocpro.ui.theme.LightGrey
import com.syaroful.agrilinkvocpro.ui.theme.MainGreen
@Composable
fun HomeScreen(
onControlFeatureClick: () -> Unit,
onRecipeFeatureClick: () -> Unit,
onPricePredictionFeatureClick: () -> Unit,
onPlantDiseaseDetectionFeatureClick: () -> Unit,
dialogState: MutableState<String?>
) {
AgrilinkVocproTheme {
Scaffold { padding ->
Column(
modifier = Modifier
.padding(padding)
.fillMaxWidth()
) {
GreenHouseInformationSection()
Spacer(modifier = Modifier.height(20.dp))
DynamicFeatureSection(
onControlFeatureClick = onControlFeatureClick,
onRecipeFeatureClick = onRecipeFeatureClick,
onPricePredictionFeatureClick = onPricePredictionFeatureClick,
onPlantDiseaseDetectionFeatureClick = onPlantDiseaseDetectionFeatureClick
)
Spacer(modifier = Modifier.height(32.dp))
Text(
modifier = Modifier.padding(horizontal = 20.dp).fillMaxWidth(),
text = "Data Sensor Green House",
textAlign = TextAlign.Center,
style = textTheme.bodyMedium
)
BetDataComponent()
BetDataComponent()
BetDataComponent()
}
}
}
}
@Composable
private fun BetDataComponent() {
Column(Modifier.padding(horizontal = 20.dp, vertical = 8.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Absolute.SpaceBetween
) {
Text("Bet 1")
Text("Labu Kabocha", color = MainGreen)
}
Spacer(modifier = Modifier.height(4.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.border(
width = 1.dp,
color = MainGreen.copy(alpha = 0.4f),
shape = RoundedCornerShape(16.dp)
)
.padding(horizontal = 16.dp, vertical = 8.dp)
)
{
SensorDataItem(
icon = "N",
label = "Nitrogen",
value = 230.toString()
)
SensorDataItem(
icon = "P",
label = "Phosphor",
value = 50.toString()
)
SensorDataItem(
icon = "K",
label = "Potassium",
value = 40.toString()
)
Image(
modifier = Modifier.height(60.dp),
painter = painterResource(id = R.drawable.kabocha),
contentDescription = "Commodity"
)
}
}
}
@Composable
private fun SensorDataItem(
icon: String = "-",
label: String,
value: String = "0"
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = icon, color = MainGreen, style = textTheme.headlineMedium)
Text(text = label, color = LightGrey, style = textTheme.bodySmall)
Text(text = value, style = textTheme.headlineSmall)
}
}
@Composable
fun DynamicFeatureSection(
onControlFeatureClick: () -> Unit,
onRecipeFeatureClick: () -> Unit,
onPricePredictionFeatureClick: () -> Unit,
onPlantDiseaseDetectionFeatureClick: () -> Unit
) {
Row(
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
MenuItemButton(
label = "Kontrol\nAktuator",
icon = painterResource(id = R.drawable.control_actuator_icon),
onClick = onControlFeatureClick
)
MenuItemButton(
label = "Resep\nPertumbuhan",
icon = painterResource(id = R.drawable.growth_recipe_icon),
onClick = onRecipeFeatureClick
)
MenuItemButton(
label = "Harga\nKomoditas",
icon = painterResource(id = R.drawable.commodity_price_prediction_icon),
onClick = onPricePredictionFeatureClick
)
MenuItemButton(
label = "Deteksi\nPenyakit",
icon = painterResource(id = R.drawable.plant_disease_detection_icon),
onClick = onPlantDiseaseDetectionFeatureClick
)
}
}
@Composable
fun GreenHouseInformationSection() {
Row(horizontalArrangement = Arrangement.End, modifier = Modifier.fillMaxWidth()) {
IconButton(
onClick = {},
modifier = Modifier.align(Alignment.CenterVertically)
) {
Icon(
imageVector = Icons.Default.Person,
contentDescription = "profile",
tint = MainGreen
)
}
}
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(20.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(text = "2 Komoditas", color = MainGreen, style = textTheme.bodyMedium)
Spacer(modifier = Modifier.height(24.dp))
Text(text = "Green House Bumiaji", style = textTheme.bodyLarge)
Text(
text = "Jl. Kopral Kasdi 2, Bulukerto, Kec. Bumiaji, Kota Batu, Jawa Timur 65334 ",
style = textTheme.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
Spacer(modifier = Modifier.width(24.dp))
Image(
painter = painterResource(id = R.drawable.green_house_image),
contentDescription = "profile",
modifier = Modifier
.size(90.dp)
.clip(RoundedCornerShape(10.dp))
)
}
}
@Preview(showBackground = true, name = "Light Mode")
@Preview(showBackground = true, name = "Dark Mode", uiMode = UI_MODE_NIGHT_YES)
@Composable
fun HomePreview() {
val state = remember { mutableStateOf<String?>("control_feature") }
HomeScreen(
onControlFeatureClick = {},
onRecipeFeatureClick = {},
onPricePredictionFeatureClick = {},
onPlantDiseaseDetectionFeatureClick = {},
dialogState = state
)
}