diff --git a/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/ui/screen/home/HomeViewModel.kt b/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/ui/screen/home/HomeViewModel.kt index b85c0ec..d72dd04 100644 --- a/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/ui/screen/home/HomeViewModel.kt +++ b/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/ui/screen/home/HomeViewModel.kt @@ -1,184 +1,79 @@ package com.syaroful.agrilinkvocpro.ui.screen.home -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf +import android.util.Log import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -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.core.utils.DownloadState -import com.syaroful.agrilinkvocpro.data.repository.DynamicModuleRepository -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay +import com.syaroful.agrilinkvocpro.core.utils.ResultState +import com.syaroful.agrilinkvocpro.data.UserPreferences +import com.syaroful.agrilinkvocpro.data.model.SensorDataResponse +import com.syaroful.agrilinkvocpro.data.repository.SensorDataRepository import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch -import javax.inject.Inject -@HiltViewModel -class HomeViewModel @Inject constructor( - private val dynamicModuleRepository: DynamicModuleRepository + +private const val TAG = "HomeViewModel" + +class HomeViewModel( + private val sensorDataRepository: SensorDataRepository, + private val userPreferences: UserPreferences ) : ViewModel() { + private val _homeState = MutableStateFlow>(ResultState.Idle) + val homeState: StateFlow> = _homeState - var currentModuleToDownload = mutableStateOf(null) - private set - - var showProgressDialog = mutableStateOf(false) - private set - - var progressMessage = mutableStateOf("") - private set - - var progressPercent = mutableFloatStateOf(0f) - private set - - private val _downloadState = MutableStateFlow(DownloadState.Idle) - private val downloadState: StateFlow = _downloadState.asStateFlow() - - private val listener = SplitInstallStateUpdatedListener { state -> - if (state.sessionId() == dynamicModuleRepository.getSessionId()) { - when (state.status()) { - SplitInstallSessionStatus.DOWNLOADING -> { - val progress = if (state.totalBytesToDownload() > 0) - state.bytesDownloaded().toFloat() / state.totalBytesToDownload() - else 0f - _downloadState.value = DownloadState.DownloadingWithProgress(progress) - } - - SplitInstallSessionStatus.DOWNLOADED -> { - _downloadState.value = DownloadState.Downloaded - } - - SplitInstallSessionStatus.INSTALLED -> { - _downloadState.value = DownloadState.Installed - } - - SplitInstallSessionStatus.FAILED -> { - _downloadState.value = DownloadState.Failed("Installation Failed") - } - - SplitInstallSessionStatus.CANCELED -> { - _downloadState.value = DownloadState.Failed("Installation Canceled") - } - - else -> {} - } - } + init { + getGreenHouseData() } - - fun onFeatureClicked(moduleName: String, onLaunch: (String) -> Unit) { - if (isModuleDownloaded(moduleName)) { - startFeatureActivity(moduleName, onLaunch) - } else { - currentModuleToDownload.value = moduleName - } - } - - fun confirmDownload(onLaunch: (String) -> Unit) { - currentModuleToDownload.value?.let { - downloadDynamicModule(it, onLaunch) - } - currentModuleToDownload.value = null - } - - private fun downloadDynamicModule(moduleName: String, onLaunch: (String) -> Unit) { - _downloadState.value = DownloadState.Starting - - dynamicModuleRepository.startDownload(moduleName, listener) - .addOnFailureListener { exception -> - val errorCode = (exception as? SplitInstallException)?.errorCode ?: -1 - val errorMessage = when (errorCode) { - SplitInstallErrorCode.NETWORK_ERROR -> "No Internet Connection" - SplitInstallErrorCode.MODULE_UNAVAILABLE -> "Module Unavailable" - SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> "Active Session Limit Exceeded" - SplitInstallErrorCode.INSUFFICIENT_STORAGE -> "Insufficient Storage" - SplitInstallErrorCode.PLAY_STORE_NOT_FOUND -> "Play Store Not Found" - else -> "Unknown Error: $errorCode" - } - _downloadState.value = DownloadState.Failed(errorMessage) - } - - observeDownloadStatus(onLaunch) - } - - private fun isModuleDownloaded(moduleName: String): Boolean { - return dynamicModuleRepository.isModuleDownloaded(moduleName) - } - - private fun observeDownloadStatus(onLaunch: (String) -> Unit) { + fun printUserToken(){ viewModelScope.launch { - downloadState.collect { status -> - when (status) { - is DownloadState.Idle -> { - showProgressDialog.value = false - progressMessage.value = "" - progressPercent.floatValue = 0f - } + val token = userPreferences.tokenFlow.first() + Log.d(TAG, "Token: $token") + } + } - is DownloadState.Starting -> { - showProgressDialog.value = true - progressMessage.value = "Starting download..." - progressPercent.floatValue = 0f - } - - is DownloadState.Downloading -> { - showProgressDialog.value = true - progressMessage.value = "Downloading module..." - } - - is DownloadState.Downloaded -> { - progressMessage.value = "Download completed" - progressPercent.floatValue = 1f - } - - is DownloadState.Installed -> { - progressMessage.value = "Install completed" - progressPercent.floatValue = 1f - delayAndDismissDialog(onLaunch) - } - - is DownloadState.Failed -> { - progressMessage.value = "Failed: ${status.message}" - showProgressDialog.value = true - progressPercent.floatValue = 0f - } - - is DownloadState.DownloadingWithProgress -> { - showProgressDialog.value = true - progressPercent.floatValue = status.progress - } + fun getGreenHouseData(){ + _homeState.value = ResultState.Loading + viewModelScope.launch { + val token = userPreferences.tokenFlow.first() + val authHeader = "Bearer $token" + try { + val response = sensorDataRepository.getLatestSensorData(authHeader) + if (response.isSuccessful) { + _homeState.value = ResultState.Success(response.body()) + Log.d(TAG, "Success to fetch data: ${response.body()} \nCurl response: ${response.raw()}") + } else { + val errorBody = response.errorBody()?.string() + Log.d(TAG, "Failed to fetch data. Code: ${response.code()}, Error Body: $errorBody \nCurl response: ${response.raw()}") + val errorMessage = errorBody?.let { + org.json.JSONObject(it) + .optString("message", "Failed to fetch data: ${response.code()}") + } ?: "Failed to fetch data: ${response.code()}" + _homeState.value = ResultState.Error(errorMessage) + // Log.d(TAG, errorMessage) // Sudah di-log di atas + } + } catch (e: Exception){ + val errorMessage = mapToUserFriendlyError(e) + _homeState.value = ResultState.Error(errorMessage) + Log.d(TAG, "Failed to fetch data: ${e.message}") + } + } + } + private fun mapToUserFriendlyError(e: Exception): String { + return when (e) { + is java.net.UnknownHostException -> "Tidak dapat terhubung ke server. Periksa koneksi internet Anda." + is java.net.SocketTimeoutException -> "Waktu koneksi habis. Mohon coba lagi." + is java.io.IOException -> "Terjadi kesalahan jaringan. Silakan cek koneksi Anda." + is retrofit2.HttpException -> { + when (e.code()) { + 401 -> "Akses ditolak. Silakan logout dan login kembali." + 403 -> "Anda tidak memiliki izin untuk mengakses ini." + 404 -> "Data tidak ditemukan." + 500 -> "Terjadi kesalahan pada server. Silakan coba lagi nanti." + else -> "Terjadi kesalahan. Kode: ${e.code()}" } } + else -> "Terjadi kesalahan. Silakan coba lagi." } } - - private fun delayAndDismissDialog(onLaunch: (String) -> Unit) { - viewModelScope.launch { - delay(1500) - showProgressDialog.value = false - currentModuleToDownload.value?.let { - startFeatureActivity(it, onLaunch) - } - currentModuleToDownload.value = null - } - } - - private fun startFeatureActivity(moduleName: String, onLaunch: (String) -> Unit) { - val className = 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 - } - className?.let { onLaunch(it) } - } - - override fun onCleared() { - super.onCleared() - dynamicModuleRepository.unregisterListener(listener) - } } \ No newline at end of file