diff --git a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/PlantDiseaseDetectionActivity.kt b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/PlantDiseaseDetectionActivity.kt index 0437ee8..6f8f89f 100644 --- a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/PlantDiseaseDetectionActivity.kt +++ b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/PlantDiseaseDetectionActivity.kt @@ -11,6 +11,7 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.navigation.compose.rememberNavController import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.di.cameraModule +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.di.databaseModule import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.di.diagnosisModule import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.navigation.NavGraph import com.syaroful.agrilinkvocpro.presentation.theme.AgrilinkVocproTheme @@ -21,7 +22,7 @@ class PlantDiseaseDetectionActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // load Module - loadKoinModules(listOf(cameraModule, diagnosisModule)) + loadKoinModules(listOf(cameraModule, diagnosisModule, databaseModule)) Log.d("KOIN_DEBUG", "โœ… cameraModule loaded, yeay ๐Ÿค—") @@ -39,7 +40,7 @@ class PlantDiseaseDetectionActivity : ComponentActivity() { override fun onDestroy() { super.onDestroy() - unloadKoinModules(listOf(cameraModule, diagnosisModule)) + unloadKoinModules(listOf(cameraModule, diagnosisModule, databaseModule)) Log.d("KOIN_DEBUG", "๐Ÿงน cameraModule unloaded") } diff --git a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/di/PlantDiagnosisModule.kt b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/di/PlantDiagnosisModule.kt index 2fbbe74..76e7b7b 100644 --- a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/di/PlantDiagnosisModule.kt +++ b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/di/PlantDiagnosisModule.kt @@ -5,6 +5,7 @@ import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.network. import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.PlantDiagnosisRepository import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.PlantDiagnosisRepositoryImpl import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.detail.PlantDiagnosisViewModel +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.history.HistoryViewModel import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType @@ -43,5 +44,6 @@ val diagnosisModule = module { get().create(GeminiApiService::class.java) } single { PlantDiagnosisRepositoryImpl(get()) } - viewModel { PlantDiagnosisViewModel(get()) } + viewModel { PlantDiagnosisViewModel(get(), get()) } + viewModel { HistoryViewModel(get()) } } \ No newline at end of file diff --git a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/navigation/NavGraph.kt b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/navigation/NavGraph.kt index 7cd5bc5..27c8bb6 100644 --- a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/navigation/NavGraph.kt +++ b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/navigation/NavGraph.kt @@ -2,27 +2,48 @@ package com.syaroful.agrilinkvocpro.plant_disease_detection_feature.navigation import androidx.compose.runtime.Composable import androidx.navigation.NavHostController +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable +import androidx.navigation.navArgument import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera.CameraScreen import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera.CameraViewModel import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.detail.DetailScreen +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.history.DetailHistoryScreen +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.history.HistoryScreen +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.history.HistoryViewModel import org.koin.androidx.compose.koinViewModel @Composable fun NavGraph(navController: NavHostController) { val cameraViewModel: CameraViewModel = koinViewModel() + val historyViewModel: HistoryViewModel = koinViewModel() NavHost(navController, startDestination = "camera_screen") { composable("camera_screen") { CameraScreen(navController, cameraViewModel) } - composable("detail_screen"){ + composable("detail_screen") { DetailScreen( cameraViewModel = cameraViewModel, onBack = { navController.popBackStack() } ) } + composable("history") { + HistoryScreen(navController = navController, viewModel = historyViewModel) + } + composable( + route = "detail-history/{id}", + arguments = listOf(navArgument("id") { type = NavType.IntType }) + ) { backStackEntry -> + val id = backStackEntry.arguments?.getInt("id") ?: return@composable + + DetailHistoryScreen( + navController = navController, + diagnosisId = id, + viewModel = historyViewModel + ) + } } } \ No newline at end of file diff --git a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/DetailScreen.kt b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/DetailScreen.kt index 69ec858..c43ad01 100644 --- a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/DetailScreen.kt +++ b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/DetailScreen.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.syaroful.agrilinkvocpro.R +import com.syaroful.agrilinkvocpro.core.components.AppButton import com.syaroful.agrilinkvocpro.core.components.DefaultErrorComponent import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.core.AppConstant import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.presentation.camera.CameraViewModel @@ -53,6 +54,7 @@ fun DetailScreen( val bitmap by cameraViewModel.bitmap.collectAsState() val diagnosisState by diagnosisViewModel.state.collectAsState() + val saveLoading by diagnosisViewModel.saveLoadingState.collectAsState() LaunchedEffect(bitmap) { if (bitmap != null && diagnosisState == null) { @@ -101,6 +103,7 @@ fun DetailScreen( shape = RoundedCornerShape(8.dp), ) { Image( + modifier = Modifier.fillMaxSize(), bitmap = it.asImageBitmap(), contentDescription = "Gambar Tanaman", contentScale = ContentScale.Crop, // Make image fill the Card @@ -141,7 +144,6 @@ fun DetailScreen( ) } } else if (data?.diagnosis != null) { - // Tampilkan detail diagnosis Box( modifier = Modifier .background( @@ -213,6 +215,12 @@ fun DetailScreen( } } } + AppButton( + label = if (saveLoading) "Menyimpan..." else "Simpan Hasil Deteksi", + isEnable = !saveLoading + ) { + diagnosisViewModel.saveResultToLocal(data, bitmap!!) + } } else { Text("Gagal parsing response") } diff --git a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/PlantDiagnosisViewModel.kt b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/PlantDiagnosisViewModel.kt index bfe08a1..bc002cb 100644 --- a/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/PlantDiagnosisViewModel.kt +++ b/agrilinkvocpro/plant_disease_detection_feature/src/main/java/com/syaroful/agrilinkvocpro/plant_disease_detection_feature/presentation/detail/PlantDiagnosisViewModel.kt @@ -5,18 +5,26 @@ import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.core.extention.toBase64 +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.core.extention.toByteArray +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.local.entity.PlantDiagnosisEntity import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.model.PlantDiseaseDetectionResponse +import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.PlantDiagnosisLocalRepository import com.syaroful.agrilinkvocpro.plant_disease_detection_feature.data.repository.PlantDiagnosisRepository +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch class PlantDiagnosisViewModel( - private val repository: PlantDiagnosisRepository + private val repository: PlantDiagnosisRepository, + private val localRepository: PlantDiagnosisLocalRepository ) : ViewModel() { private val _state = MutableStateFlow?>(null) val state: StateFlow?> = _state + private val _saveLoadingState = MutableStateFlow(false) + val saveLoadingState: StateFlow = _saveLoadingState + fun analyzePlant(bitmap: Bitmap, prompt: String) { Log.d("PlantDiagnosisViewModel", "analyzePlant() called") viewModelScope.launch { @@ -31,4 +39,23 @@ class PlantDiagnosisViewModel( } } } + + fun saveResultToLocal(response: PlantDiseaseDetectionResponse, image: Bitmap) { + _saveLoadingState.value = true + viewModelScope.launch { + delay(1000L) + val entity = PlantDiagnosisEntity( + diagnosis = response.diagnosis ?: "Unknown", + cause = response.cause ?: "-", + description = response.description ?: "-", + prevention = response.prevention?.joinToString(", ") ?: "-", + image = image.toByteArray(), + treatment = response.treatment?.joinToString(", ") { + "${it.method}: ${it.description}" + } ?: "-", + ) + localRepository.saveDiagnosis(entity) + _saveLoadingState.value = false + } + } } \ No newline at end of file