feat: add download confirmation and progress dialogs
This commit is contained in:
parent
8e5e73bdf0
commit
40011a18d4
|
|
@ -35,7 +35,11 @@ fun DownloadProgressDialog(
|
|||
) {
|
||||
Text(text = message)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
LinearProgressIndicator(color = MainGreen, trackColor = MainGreen.copy(alpha = 0.1f))
|
||||
LinearProgressIndicator(
|
||||
progress = {progress},
|
||||
color = MainGreen,
|
||||
trackColor = MainGreen.copy(alpha = 0.1f),
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ 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(
|
||||
class DynamicModuleRepository (
|
||||
private val splitInstallManager: SplitInstallManager
|
||||
) {
|
||||
private var sessionId = 0
|
||||
|
|
@ -17,7 +16,7 @@ class DynamicModuleRepository @Inject constructor(
|
|||
|
||||
fun startDownload(
|
||||
moduleName: String,
|
||||
listener: SplitInstallStateUpdatedListener
|
||||
listener: SplitInstallStateUpdatedListener,
|
||||
): Task<Int> {
|
||||
val request = SplitInstallRequest.newBuilder()
|
||||
.addModule(moduleName)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,15 @@
|
|||
package com.syaroful.agrilinkvocpro.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.android.play.core.splitinstall.SplitInstallManager
|
||||
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import com.syaroful.agrilinkvocpro.data.repository.DynamicModuleRepository
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object DynamicFeatureModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSplitInstallManager(@ApplicationContext context: Context): SplitInstallManager {
|
||||
return SplitInstallManagerFactory.create(context)
|
||||
val dynamicFeatureModule = module {
|
||||
single<SplitInstallManager> {
|
||||
SplitInstallManagerFactory.create(androidContext())
|
||||
}
|
||||
|
||||
single { DynamicModuleRepository(get()) }
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
package com.syaroful.agrilinkvocpro.ui.screen.home
|
||||
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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 kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DynamicModuleViewModel(
|
||||
private val dynamicModuleRepository: DynamicModuleRepository
|
||||
) : ViewModel() {
|
||||
|
||||
var currentModuleToDownload = mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
private var onLaunchCallback: ((String) -> Unit)? = null
|
||||
|
||||
|
||||
private val _showProgressDialog = MutableStateFlow(false)
|
||||
val showProgressDialog: StateFlow<Boolean> = _showProgressDialog
|
||||
|
||||
private val _progressMessage = MutableStateFlow("")
|
||||
val progressMessage: StateFlow<String> = _progressMessage
|
||||
|
||||
private val _progressPercent = MutableStateFlow(0f)
|
||||
val progressPercent: StateFlow<Float> = _progressPercent
|
||||
|
||||
fun setDialogVisibility(show: Boolean) {
|
||||
_showProgressDialog.value = show
|
||||
}
|
||||
|
||||
private val _downloadState = MutableStateFlow<DownloadState>(DownloadState.Idle)
|
||||
private val downloadState: StateFlow<DownloadState> = _downloadState.asStateFlow()
|
||||
|
||||
init {
|
||||
observeDownloadStatus()
|
||||
}
|
||||
|
||||
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 -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
onLaunchCallback = onLaunch
|
||||
_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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun isModuleDownloaded(moduleName: String): Boolean {
|
||||
return dynamicModuleRepository.isModuleDownloaded(moduleName)
|
||||
}
|
||||
|
||||
private fun observeDownloadStatus() {
|
||||
viewModelScope.launch {
|
||||
downloadState.collect { status ->
|
||||
when (status) {
|
||||
is DownloadState.Idle -> {
|
||||
_progressMessage.value = ""
|
||||
_progressPercent.value = 0f
|
||||
}
|
||||
|
||||
is DownloadState.Starting -> {
|
||||
_showProgressDialog.value = true
|
||||
_progressMessage.value = "Starting download..."
|
||||
_progressPercent.value = 0f
|
||||
}
|
||||
|
||||
is DownloadState.Downloading -> {
|
||||
_showProgressDialog.value = true
|
||||
_progressMessage.value = "Downloading module..."
|
||||
}
|
||||
|
||||
is DownloadState.Downloaded -> {
|
||||
_progressMessage.value = "Download completed"
|
||||
_showProgressDialog.value = true
|
||||
_progressPercent.value = 1f
|
||||
}
|
||||
|
||||
is DownloadState.Installed -> {
|
||||
_progressMessage.value = "Install completed"
|
||||
_progressPercent.value = 1f
|
||||
delayAndDismissDialog()
|
||||
}
|
||||
|
||||
is DownloadState.Failed -> {
|
||||
_progressMessage.value = "Failed: ${status.message}"
|
||||
_showProgressDialog.value = true
|
||||
_progressPercent.value = 0f
|
||||
}
|
||||
|
||||
is DownloadState.DownloadingWithProgress -> {
|
||||
_showProgressDialog.value = true
|
||||
_progressPercent.value = status.progress
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun delayAndDismissDialog() {
|
||||
viewModelScope.launch {
|
||||
delay(1500)
|
||||
_showProgressDialog.value = false
|
||||
currentModuleToDownload.value?.let {
|
||||
onLaunchCallback?.invoke(it)
|
||||
}
|
||||
currentModuleToDownload.value = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun startFeatureActivity(moduleName: String, onLaunch: (String) -> Unit) {
|
||||
val className = when (moduleName) {
|
||||
"control_feature" -> "com.syaroful.agrilinkvocpro.control_feature.ControlActuatorActivity"
|
||||
"growth_recipe_feature" -> "com.syaroful.agrilinkvocpro.growth_recipe_feature.GrowthRecipeActivity"
|
||||
"commodity_price_prediction_feature" -> "com.syaroful.agrilinkvocpro.commodity_price_prediction_feature.PricePredictionActivity"
|
||||
"plant_disease_detection_feature" -> "com.syaroful.agrilinkvocpro.plant_disease_detection_feature.PlantDiseaseDetectionActivity"
|
||||
else -> null
|
||||
}
|
||||
className?.let { onLaunch(it) }
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
dynamicModuleRepository.unregisterListener(listener)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user