feat: implement sensor data fetching in HomeViewModel
This commit is contained in:
parent
c2fbd6fa25
commit
6111391bed
|
|
@ -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<SensorDataResponse>>(ResultState.Idle)
|
||||
val homeState: StateFlow<ResultState<SensorDataResponse>> = _homeState
|
||||
|
||||
var currentModuleToDownload = mutableStateOf<String?>(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>(DownloadState.Idle)
|
||||
private val downloadState: StateFlow<DownloadState> = _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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user