diff --git a/app/src/main/java/com/aurora/store/view/custom/layouts/button/UpdateButton.kt b/app/src/main/java/com/aurora/store/view/custom/layouts/button/UpdateButton.kt index 312bec78a..6a48705e3 100644 --- a/app/src/main/java/com/aurora/store/view/custom/layouts/button/UpdateButton.kt +++ b/app/src/main/java/com/aurora/store/view/custom/layouts/button/UpdateButton.kt @@ -26,6 +26,7 @@ import com.aurora.extensions.getString import com.aurora.extensions.runOnUiThread import com.aurora.store.R import com.aurora.store.State +import com.aurora.store.data.model.DownloadStatus import com.aurora.store.databinding.ViewUpdateButtonBinding class UpdateButton : RelativeLayout { @@ -72,12 +73,12 @@ class UpdateButton : RelativeLayout { B.btnPositive.text = getString(text) } - fun updateState(state: State) { - val displayChild = when (state) { - State.IDLE, State.CANCELED -> 0 - State.QUEUED -> 1 - State.PROGRESS -> 2 - State.COMPLETE -> 3 + fun updateState(downloadStatus: DownloadStatus) { + val displayChild = when (downloadStatus) { + DownloadStatus.QUEUED, + DownloadStatus.DOWNLOADING -> 2 + DownloadStatus.COMPLETED -> 3 + else -> 0 } if (B.viewFlipper.displayedChild != displayChild) { diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt index 53bb4425f..50bde9f32 100644 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt +++ b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt @@ -34,13 +34,17 @@ import com.airbnb.epoxy.ModelView import com.airbnb.epoxy.OnViewRecycled import com.aurora.extensions.invisible import com.aurora.extensions.px -import com.aurora.extensions.show +import com.aurora.gplayapi.data.models.App import com.aurora.store.R import com.aurora.store.State -import com.aurora.store.data.model.UpdateFile +import com.aurora.store.data.model.DownloadStatus +import com.aurora.store.data.room.download.Download import com.aurora.store.databinding.ViewAppUpdateBinding import com.aurora.store.util.CommonUtil +import com.aurora.store.util.PathUtil import com.aurora.store.view.epoxy.views.BaseView +import java.io.File +import kotlin.io.path.pathString @ModelView( autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT, @@ -72,76 +76,77 @@ class AppUpdateView : RelativeLayout { } @ModelProp - fun updateFile(updateFile: UpdateFile?) { - if (updateFile != null) { - - /*Inflate App details*/ - with(updateFile.app) { - B.txtLine1.text = displayName - B.imgIcon.load(iconArtwork.url) { - placeholder(R.drawable.bg_placeholder) - transformations(RoundedCornersTransformation(8.px.toFloat())) - } - - B.txtLine2.text = developerName - B.txtLine3.text = ("${CommonUtil.addSiPrefix(size)} • $updatedOn") - B.txtLine4.text = ("$versionName (${versionCode})") - B.txtChangelog.text = if (changes.isNotEmpty()) - HtmlCompat.fromHtml( - changes, - HtmlCompat.FROM_HTML_OPTION_USE_CSS_COLORS - ) - else - context.getString(R.string.details_changelog_unavailable) - - B.headerIndicator.setOnClickListener { - if (B.txtChangelog.isVisible) { - B.headerIndicator.icon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_down) - B.txtChangelog.visibility = View.GONE - } else { - B.headerIndicator.icon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_up) - B.txtChangelog.visibility = View.VISIBLE - } - } + fun app(app: App) { + /*Inflate App details*/ + with(app) { + B.txtLine1.text = displayName + B.imgIcon.load(iconArtwork.url) { + placeholder(R.drawable.bg_placeholder) + transformations(RoundedCornersTransformation(8.px.toFloat())) } - /*Inflate Download details*/ - updateFile.group?.let { - when (updateFile.state) { - State.QUEUED -> { - B.progressDownload.progress = 0 - B.progressDownload.show() - B.btnAction.updateState(State.QUEUED) - } - State.IDLE, State.CANCELED -> { - B.progressDownload.progress = 0 - B.progressDownload.invisible() - B.btnAction.updateState(State.IDLE) - } - State.PROGRESS -> { - val progress = it.groupDownloadProgress - if (progress > 0) { - if (progress == 100) { - B.progressDownload.invisible() - } else { - B.progressDownload.progress = progress - B.progressDownload.show() - } - } - } - State.COMPLETE -> { - B.progressDownload.invisible() - B.btnAction.updateState(State.COMPLETE) - } + B.txtLine2.text = developerName + B.txtLine3.text = ("${CommonUtil.addSiPrefix(size)} • $updatedOn") + B.txtLine4.text = ("$versionName (${versionCode})") + B.txtChangelog.text = if (changes.isNotEmpty()) + HtmlCompat.fromHtml( + changes, + HtmlCompat.FROM_HTML_OPTION_USE_CSS_COLORS + ) + else + context.getString(R.string.details_changelog_unavailable) + + B.headerIndicator.setOnClickListener { + if (B.txtChangelog.isVisible) { + B.headerIndicator.icon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_down) + B.txtChangelog.visibility = View.GONE + } else { + B.headerIndicator.icon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_up) + B.txtChangelog.visibility = View.VISIBLE } } } } @ModelProp - fun state(state: State?) { - state?.let { - B.btnAction.updateState(it) + fun download(download: Download?) { + if (download != null) { + /*Inflate Download details*/ + B.btnAction.updateState(download.status) + when (download.status) { + DownloadStatus.QUEUED -> { + B.progressDownload.progress = 0 + B.progressDownload.show() + } + DownloadStatus.DOWNLOADING -> { + if (download.progress > 0) { + if (download.progress == 100) { + B.progressDownload.invisible() + } else { + B.progressDownload.progress = download.progress + B.progressDownload.show() + } + } + } + DownloadStatus.COMPLETED -> { + B.progressDownload.invisible() + + // It is possible that while the app was downloaded in past, files have been removed + // Check once again to reflect appropriate status + val files = File( + PathUtil.getAppDownloadDir( + context, + download.packageName, + download.versionCode + ).pathString + ).listFiles() + if (files.isNullOrEmpty()) B.btnAction.updateState(DownloadStatus.UNAVAILABLE) + } + else -> { + B.progressDownload.progress = 0 + B.progressDownload.invisible() + } + } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt index 479ce7304..d9a99e7a1 100644 --- a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt @@ -20,26 +20,21 @@ package com.aurora.store.view.ui.updates import android.Manifest -import android.content.ComponentName import android.content.Intent -import android.content.ServiceConnection import android.content.pm.PackageManager import android.os.Bundle import android.os.Environment -import android.os.IBinder import android.provider.Settings import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import com.aurora.extensions.isRAndAbove import com.aurora.extensions.toast import com.aurora.gplayapi.data.models.App import com.aurora.store.R -import com.aurora.store.State -import com.aurora.store.data.downloader.getGroupId -import com.aurora.store.data.model.UpdateFile -import com.aurora.store.data.service.UpdateService +import com.aurora.store.data.room.download.Download import com.aurora.store.databinding.FragmentUpdatesBinding import com.aurora.store.util.PathUtil import com.aurora.store.util.isExternalStorageEnable @@ -49,10 +44,12 @@ import com.aurora.store.view.epoxy.views.app.NoAppViewModel_ import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_ import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.viewmodel.all.UpdatesViewModel -import com.tonyodev.fetch2.AbstractFetchGroupListener -import com.tonyodev.fetch2.Download -import com.tonyodev.fetch2.FetchGroup import dagger.hilt.android.AndroidEntryPoint +import java.io.File +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch +import kotlin.io.path.pathString @AndroidEntryPoint class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { @@ -67,113 +64,30 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { private val startForStorageManagerResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (isRAndAbove() && Environment.isExternalStorageManager()) { - updateSingle(app) + viewModel.download(app) } else { toast(R.string.permissions_denied) } } private val startForPermissions = registerForActivityResult(ActivityResultContracts.RequestPermission()) { perm -> - if (perm) updateSingle(app) else toast(R.string.permissions_denied) + if (perm) viewModel.download(app) else toast(R.string.permissions_denied) } - val listOfActionsWhenServiceAttaches = ArrayList() - private lateinit var fetchListener: AbstractFetchGroupListener - - private var updateService: UpdateService? = null - private var attachToServiceCalled = false - private var serviceConnection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName, binder: IBinder) { - updateService = (binder as UpdateService.UpdateServiceBinder).getUpdateService() - updateService!!.registerFetchListener(fetchListener) - if (listOfActionsWhenServiceAttaches.isNotEmpty()) { - val iterator = listOfActionsWhenServiceAttaches.iterator() - while (iterator.hasNext()) { - val next = iterator.next() - next.run() - iterator.remove() - } - } - } - - override fun onServiceDisconnected(name: ComponentName) { - updateService = null - attachToServiceCalled = false - } - } - - private var updateFileMap: MutableMap = mutableMapOf() - - override fun onResume() { - getUpdateServiceInstance() - super.onResume() - } - - override fun onPause() { - if (updateService != null) { - updateService = null - attachToServiceCalled = false - requireContext().unbindService(serviceConnection) - } - super.onPause() - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - override fun onDestroy() { - super.onDestroy() - if (updateService != null) { - updateService = null - attachToServiceCalled = false - requireContext().unbindService(serviceConnection) - } - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentUpdatesBinding.bind(view) - fetchListener = object : AbstractFetchGroupListener() { - - override fun onAdded(groupId: Int, download: Download, fetchGroup: FetchGroup) { - viewModel.updateDownload(groupId, fetchGroup) - } - - override fun onProgress( - groupId: Int, - download: Download, - etaInMilliSeconds: Long, - downloadedBytesPerSecond: Long, - fetchGroup: FetchGroup - ) { - viewModel.updateDownload(groupId, fetchGroup) - } - - override fun onCompleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - if (fetchGroup.groupDownloadProgress == 100) { - viewModel.updateDownload(groupId, fetchGroup, isComplete = true) + viewModel.observe() + viewLifecycleOwner.lifecycleScope.launch { + viewModel.updates.combine(viewModel.downloadsList) { uList, dList -> + uList?.associateWith { a -> + dList.find { it.packageName == a.packageName && it.versionCode == a.versionCode } } + }.collectLatest { + updateController(it) + binding.swipeRefreshLayout.isRefreshing = false } - - override fun onCancelled(groupId: Int, download: Download, fetchGroup: FetchGroup) { - viewModel.updateDownload(groupId, fetchGroup, isCancelled = true) - } - - override fun onDeleted(groupId: Int, download: Download, fetchGroup: FetchGroup) { - viewModel.updateDownload(groupId, fetchGroup, isCancelled = true) - } - } - - getUpdateServiceInstance() - - viewModel.liveUpdateData.observe(viewLifecycleOwner) { - updateFileMap = it - updateController(updateFileMap) - binding.swipeRefreshLayout.isRefreshing = false - updateService?.liveUpdateData?.postValue(updateFileMap) } binding.swipeRefreshLayout.setOnRefreshListener { @@ -183,10 +97,15 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { updateController(null) } - private fun updateController(updateFileMap: MutableMap?) { + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + private fun updateController(appList: Map?) { binding.recycler.withModels { setFilterDuplicates(true) - if (updateFileMap == null) { + if (appList == null) { for (i in 1..10) { add( AppListViewShimmerModel_() @@ -194,7 +113,7 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { ) } } else { - if (updateFileMap.isEmpty()) { + if (appList.isEmpty()) { add( NoAppViewModel_() .id("no_update") @@ -206,8 +125,8 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { UpdateHeaderViewModel_() .id("header_all") .title( - "${updateFileMap.size} " + - if (updateFileMap.size == 1) + "${appList.size} " + + if (appList.size == 1) getString(R.string.update_available) else getString(R.string.updates_available) @@ -219,42 +138,46 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { getString(R.string.action_update_all) ) .click { _ -> - if (viewModel.updateAllEnqueued) + if (viewModel.updateAllEnqueued) { cancelAll() - else - updateFileMap.values.forEach { updateSingle(it.app, true) } - + } else { + appList.keys.forEach { updateSingle(it, true) } + } requestModelBuild() } ) - updateFileMap.values.forEach { updateFile -> + for ((app, download) in appList) { add( AppUpdateViewModel_() - .id(updateFile.hashCode()) - .updateFile(updateFile) - .click { _ -> - openDetailsFragment( - updateFile.app.packageName, - updateFile.app - ) - } + .id(app.packageName) + .app(app) + .download(download) + .click { _ -> openDetailsFragment(app.packageName, app) } .longClick { _ -> - openAppMenuSheet(updateFile.app) + openAppMenuSheet(app) false } - .positiveAction { _ -> updateSingle(updateFile.app) } - .negativeAction { _ -> cancelSingle(updateFile.app) } + .positiveAction { _ -> updateSingle(app) } + .negativeAction { _ -> cancelSingle(app) } .installAction { _ -> - updateFile.group?.downloads?.let { - viewModel.install( + val files = File( + PathUtil.getAppDownloadDir( requireContext(), - updateFile.app.packageName, - it - ) + app.packageName, + app.versionCode + ).pathString + ).listFiles() + + // Downloaded files are missing, trigger re-download + if (files.isNullOrEmpty()) { + updateSingle(app) + return@installAction } + + val apkFiles = files.filter { it.path.endsWith(".apk") } + viewModel.install(requireContext(), app.packageName, apkFiles) } - .state(updateFile.state) ) } } @@ -262,78 +185,42 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) { } } - private fun runInService(runnable: Runnable) { - if (updateService == null) { - listOfActionsWhenServiceAttaches.add(runnable) - getUpdateServiceInstance() - } else { - runnable.run() - } - } - private fun updateSingle(app: App, updateAll: Boolean = false) { this.app = app - runInService { - viewModel.updateState(app.getGroupId(requireContext()), State.QUEUED) - viewModel.updateAllEnqueued = updateAll + viewModel.updateAllEnqueued = updateAll - if (PathUtil.needsStorageManagerPerm(app.fileList) || - requireContext().isExternalStorageEnable() - ) { - if (isRAndAbove()) { - if (!Environment.isExternalStorageManager()) { - startForStorageManagerResult.launch( - Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) - ) - } else { - updateService?.updateApp(app) - } + if (PathUtil.needsStorageManagerPerm(app.fileList) || + requireContext().isExternalStorageEnable() + ) { + if (isRAndAbove()) { + if (!Environment.isExternalStorageManager()) { + startForStorageManagerResult.launch( + Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + ) } else { - if (ContextCompat.checkSelfPermission( - requireContext(), - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) == PackageManager.PERMISSION_GRANTED - ) { - updateService?.updateApp(app) - } else { - startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) - } + viewModel.download(app) } } else { - updateService?.updateApp(app) + if (ContextCompat.checkSelfPermission( + requireContext(), + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) == PackageManager.PERMISSION_GRANTED + ) { + viewModel.download(app) + } else { + startForPermissions.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } } + } else { + viewModel.download(app) } } private fun cancelSingle(app: App) { - runInService { - updateService?.fetch?.cancelGroup(app.getGroupId(requireContext())) - } + viewModel.cancelDownload(app) } private fun cancelAll() { - runInService { - viewModel.updateAllEnqueued = false - updateFileMap.values.forEach { - updateService?.fetch?.cancelGroup( - it.app.getGroupId( - requireContext() - ) - ) - } - } - } - - private fun getUpdateServiceInstance() { - if (updateService == null && !attachToServiceCalled) { - attachToServiceCalled = true - val intent = Intent(requireContext(), UpdateService::class.java) - requireContext().startService(intent) - requireContext().bindService( - intent, - serviceConnection, - 0 - ) - } + viewModel.cancelAll() } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt index c269b0b1f..f9dd10218 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/all/UpdatesViewModel.kt @@ -23,172 +23,74 @@ import android.app.Application import android.content.Context import android.util.Log import androidx.core.content.pm.PackageInfoCompat -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.App -import com.aurora.store.State -import com.aurora.store.data.RequestState -import com.aurora.store.data.downloader.RequestGroupIdBuilder -import com.aurora.store.data.downloader.getGroupId -import com.aurora.store.data.event.BusEvent -import com.aurora.store.data.event.InstallerEvent import com.aurora.store.data.installer.AppInstaller -import com.aurora.store.data.model.UpdateFile -import com.tonyodev.fetch2.Download -import com.tonyodev.fetch2.FetchGroup -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode +import com.aurora.store.util.DownloadWorkerUtil +import dagger.hilt.android.lifecycle.HiltViewModel import java.io.File import java.util.Locale +import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch -class UpdatesViewModel(application: Application) : BaseAppsViewModel(application) { +@HiltViewModel +class UpdatesViewModel @Inject constructor( + application: Application, + private val downloadWorkerUtil: DownloadWorkerUtil +) : BaseAppsViewModel(application) { private val TAG = UpdatesViewModel::class.java.simpleName - var updateFileMap: MutableMap = mutableMapOf() - var liveUpdateData: MutableLiveData> = MutableLiveData() var updateAllEnqueued: Boolean = false - init { - EventBus.getDefault().register(this) + private val _updates = MutableSharedFlow?>() + val updates = _updates.asSharedFlow() - requestState = RequestState.Init - observe() - } + val downloadsList get() = downloadWorkerUtil.downloadsList override fun observe() { viewModelScope.launch(Dispatchers.IO) { try { - val marketApps = getFilteredApps() - checkUpdate(marketApps) - } catch (e: Exception) { - requestState = RequestState.Pending - } - } - } - - private fun checkUpdate(subAppList: List) { - subAppList.filter { - val packageInfo = packageInfoMap[it.packageName] - if (packageInfo != null) { - it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) - } else { - false - } - }.sortedBy { it.displayName.lowercase(Locale.getDefault()) }.also { apps -> - updateFileMap.clear() - - apps.forEach { - updateFileMap[it.getGroupId(getApplication().applicationContext)] = UpdateFile(it) - } - - liveUpdateData.postValue(updateFileMap) - requestState = RequestState.Complete - } - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - fun onBusEvent(event: BusEvent) { - when (event) { - is BusEvent.InstallEvent -> { - updateListAndPost(event.packageName) - } - is BusEvent.UninstallEvent -> { - updateListAndPost(event.packageName) - } - is BusEvent.Blacklisted -> { - updateListAndPost(event.packageName) - } - else -> { - - } - } - } - - @Subscribe(threadMode = ThreadMode.BACKGROUND) - fun onInstallerEvent(event: InstallerEvent) { - when (event) { - is InstallerEvent.Success -> { - - } - is InstallerEvent.Cancelled -> { - - } - is InstallerEvent.Failed -> { - val packageName = event.packageName - packageName?.let { - val groupIDsOfPackageName = RequestGroupIdBuilder.getGroupIDsForApp(getApplication().applicationContext, packageName.hashCode()) - groupIDsOfPackageName.forEach { - updateDownload(it, null, true) + getFilteredApps().filter { + val packageInfo = packageInfoMap[it.packageName] + if (packageInfo != null) { + it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) + } else { + false } + }.sortedBy { it.displayName.lowercase(Locale.getDefault()) }.also { apps -> + _updates.emit(apps) } + } catch (exception: Exception) { + Log.d(TAG, "Failed to get updates", exception) } } } - fun updateState(id: Int, state: State) { - updateFileMap[id]?.state = state - liveUpdateData.postValue(updateFileMap) - } - - fun updateDownload( - id: Int, - group: FetchGroup?, - isCancelled: Boolean = false, - isComplete: Boolean = false - ) { - when { - isCancelled -> { - updateFileMap[id]?.state = State.IDLE - updateFileMap[id]?.group = null - } - isComplete -> { - updateFileMap[id]?.state = State.COMPLETE - updateFileMap[id]?.group = group - } - else -> { - updateFileMap[id]?.state = State.PROGRESS - updateFileMap[id]?.group = group - } - } - - liveUpdateData.postValue(updateFileMap) - } - @Synchronized - fun install(context: Context, packageName: String, files: List) { - if (files.all { File(it.file).exists() }) { + fun install(context: Context, packageName: String, files: List) { + try { viewModelScope.launch(Dispatchers.IO) { - try { - AppInstaller.getInstance(context).getPreferredInstaller().install( - packageName, - files.filter { it.file.endsWith(".apk") }.map { it.file }.toList() - ) - } catch (exception: Exception) { - Log.e(TAG, "Failed to install $packageName", exception) - } + AppInstaller.getInstance(context).getPreferredInstaller() + .install(packageName, files) } - } else { - Log.e(TAG, "Given files doesn't exists!") + } catch (exception: Exception) { + Log.e(TAG, "Failed to install app", exception) } } - private fun updateListAndPost(packageName: String) { - val groupIDsOfPackageName = RequestGroupIdBuilder.getGroupIDsForApp(getApplication().applicationContext, packageName.hashCode()) - groupIDsOfPackageName.forEach { - //Remove from map - updateFileMap.remove(it) - } - - //Post new update list - liveUpdateData.postValue(updateFileMap) + fun download(app: App) { + viewModelScope.launch { downloadWorkerUtil.enqueueApp(app) } } - override fun onCleared() { - EventBus.getDefault().unregister(this) - super.onCleared() + fun cancelDownload(app: App) { + viewModelScope.launch { downloadWorkerUtil.cancelDownload(app.packageName) } + } + + fun cancelAll() { + viewModelScope.launch { downloadWorkerUtil.cancelAll() } } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt index 05a0dfe36..4edf2cba1 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt @@ -17,6 +17,7 @@ import com.aurora.store.data.providers.AuthProvider import com.aurora.store.util.DownloadWorkerUtil import com.google.gson.GsonBuilder import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.File import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -146,7 +147,7 @@ class AppDetailsViewModel @Inject constructor( } @Synchronized - fun install(context: Context, packageName: String, files: List) { + fun install(context: Context, packageName: String, files: List) { try { viewModelScope.launch(Dispatchers.IO) { AppInstaller.getInstance(context).getPreferredInstaller()