From e15601f4ad5fc9ed390b38e72af10e89eff5c5fb Mon Sep 17 00:00:00 2001 From: Cutiful <113351087+Syaroful@users.noreply.github.com> Date: Fri, 16 May 2025 11:13:12 +0700 Subject: [PATCH] feat: Add dynamic module ViewModel and Repository --- .../di/DynamicModuleRepository.kt | 35 +++++++++ .../viewModel/DynamicModuleViewModel.kt | 75 +++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/di/DynamicModuleRepository.kt create mode 100644 agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/viewModel/DynamicModuleViewModel.kt diff --git a/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/di/DynamicModuleRepository.kt b/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/di/DynamicModuleRepository.kt new file mode 100644 index 0000000..60760c9 --- /dev/null +++ b/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/di/DynamicModuleRepository.kt @@ -0,0 +1,35 @@ +package com.syaroful.agrilinkvocpro.di + +import com.google.android.gms.tasks.Task +import com.google.android.play.core.splitinstall.SplitInstallManager +import com.google.android.play.core.splitinstall.SplitInstallRequest +import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener +import javax.inject.Inject + +class DynamicModuleRepository @Inject constructor( + private val splitInstallManager: SplitInstallManager +) { + private var sessionId = 0 + + fun isModuleDownloaded(moduleName: String): Boolean { + return splitInstallManager.installedModules.contains(moduleName) + } + + fun startDownload( + moduleName: String, + listener: SplitInstallStateUpdatedListener + ): Task { + val request = SplitInstallRequest.newBuilder() + .addModule(moduleName) + .build() + splitInstallManager.registerListener(listener) + return splitInstallManager.startInstall(request) + .addOnSuccessListener { id -> sessionId = id } + } + + fun unregisterListener(listener: SplitInstallStateUpdatedListener){ + splitInstallManager.unregisterListener(listener) + } + + fun getSessionId() = sessionId +} \ No newline at end of file diff --git a/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/viewModel/DynamicModuleViewModel.kt b/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/viewModel/DynamicModuleViewModel.kt new file mode 100644 index 0000000..a14d75c --- /dev/null +++ b/agrilinkvocpro/app/src/main/java/com/syaroful/agrilinkvocpro/viewModel/DynamicModuleViewModel.kt @@ -0,0 +1,75 @@ +package com.syaroful.agrilinkvocpro.viewModel + +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 dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class DynamicModuleViewModel @Inject constructor( + private val repository: DynamicModuleRepository +) : ViewModel() { + private val _downloadState = MutableStateFlow(DownloadState.Idle) + val downloadState: StateFlow = _downloadState.asStateFlow() + + private val listener = SplitInstallStateUpdatedListener { state -> + if (state.sessionId() == repository.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 -> {} + } + } + } + + fun isModuleDownloaded(moduleName: String): Boolean { + return repository.isModuleDownloaded(moduleName) + } + + fun downloadModule(moduleName: String) { + _downloadState.value = DownloadState.Starting + repository.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) + } + } + + override fun onCleared() { + super.onCleared() + repository.unregisterListener(listener) + } +} \ No newline at end of file