From d1df329f18fd20dad1542c18e071da4a0835f419 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Mon, 3 Jul 2023 13:34:45 +0600 Subject: [PATCH] refactor: initiating install process --- .../foundation/e/apps/data/enums/Origin.kt | 1 - .../e/apps/data/fused/FusedAPIRepository.kt | 1 - .../e/apps/data/fused/FusedApiImpl.kt | 3 - .../data/fusedDownload/FusedManagerImpl.kt | 7 +- .../fusedDownload/FusedManagerRepository.kt | 13 +- .../apps/data/fusedDownload/IFusedManager.kt | 2 - .../foundation/e/apps/di/CommonUtilsModule.kt | 25 --- .../e/apps/install/updates/UpdatesWorker.kt | 65 +------- .../workmanager/AppInstallProcessor.kt | 155 ++++++++++++++++++ .../java/foundation/e/apps/ui/MainActivity.kt | 126 ++++++-------- .../e/apps/ui/MainActivityViewModel.kt | 114 +------------ .../model/ApplicationScreenshotsRVAdapter.kt | 3 - .../application/model/ScreenshotRVAdapter.kt | 5 +- .../e/apps/utils/eventBus/AppEvent.kt | 4 + 14 files changed, 226 insertions(+), 298 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/data/enums/Origin.kt b/app/src/main/java/foundation/e/apps/data/enums/Origin.kt index ae1218ccd..b8bd98c83 100644 --- a/app/src/main/java/foundation/e/apps/data/enums/Origin.kt +++ b/app/src/main/java/foundation/e/apps/data/enums/Origin.kt @@ -20,6 +20,5 @@ package foundation.e.apps.data.enums enum class Origin { CLEANAPK, - GITLAB, GPLAY } diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt index 9af50e152..0aa269ab6 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedAPIRepository.kt @@ -76,7 +76,6 @@ class FusedAPIRepository @Inject constructor(private val fusedAPIImpl: FusedApi) } suspend fun updateFusedDownloadWithDownloadingInfo( - authData: AuthData, origin: Origin, fusedDownload: FusedDownload ) { diff --git a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt index f688d83e6..2f4e967b8 100644 --- a/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fused/FusedApiImpl.kt @@ -532,9 +532,6 @@ class FusedApiImpl @Inject constructor( fusedDownload.files = downloadList list.addAll(downloadList.map { it.url }) } - - Origin.GITLAB -> { - } } fusedDownload.downloadURLList = list } diff --git a/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerImpl.kt index 6ed1bfb67..2169c3416 100644 --- a/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerImpl.kt @@ -85,11 +85,6 @@ class FusedManagerImpl @Inject constructor( return fusedDownloadRepository.getDownloadLiveList() } - override suspend fun clearInstallationIssue(fusedDownload: FusedDownload) { - flushOldDownload(fusedDownload.packageName) - fusedDownloadRepository.deleteDownload(fusedDownload) - } - @OptIn(DelicateCoroutinesApi::class) override suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) { if (status == Status.INSTALLED) { @@ -258,9 +253,9 @@ class FusedManagerImpl @Inject constructor( "$cacheDir/${fusedDownload.packageName}/${fusedDownload.packageName}_1.apk" override suspend fun installationIssue(fusedDownload: FusedDownload) { - flushOldDownload(fusedDownload.packageName) fusedDownload.status = Status.INSTALLATION_ISSUE fusedDownloadRepository.updateDownload(fusedDownload) + flushOldDownload(fusedDownload.packageName) } override suspend fun updateAwaiting(fusedDownload: FusedDownload) { diff --git a/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerRepository.kt b/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerRepository.kt index 42935ee21..89e3572ab 100644 --- a/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/fusedDownload/FusedManagerRepository.kt @@ -41,7 +41,7 @@ class FusedManagerRepository @Inject constructor( } // We don't want to add any thing, if it already exists without INSTALLATION_ISSUE - if (existingFusedDownload != null && existingFusedDownload.status != Status.INSTALLATION_ISSUE) { + if (existingFusedDownload != null && !isStatusEligibleToInstall(existingFusedDownload)) { return false } @@ -49,6 +49,13 @@ class FusedManagerRepository @Inject constructor( return true } + private fun isStatusEligibleToInstall(existingFusedDownload: FusedDownload) = + listOf( + Status.UNAVAILABLE, + Status.INSTALLATION_ISSUE, + Status.PURCHASE_NEEDED + ).contains(existingFusedDownload.status) + private fun isInstallWorkRunning( existingFusedDownload: FusedDownload?, fusedDownload: FusedDownload @@ -61,10 +68,6 @@ class FusedManagerRepository @Inject constructor( fusedManagerImpl.insertFusedDownloadPurchaseNeeded(fusedDownload) } - suspend fun clearInstallationIssue(fusedDownload: FusedDownload) { - return fusedManagerImpl.clearInstallationIssue(fusedDownload) - } - suspend fun getDownloadList(): List { return fusedManagerImpl.getDownloadList() } diff --git a/app/src/main/java/foundation/e/apps/data/fusedDownload/IFusedManager.kt b/app/src/main/java/foundation/e/apps/data/fusedDownload/IFusedManager.kt index 18320fe88..54f7ecb0c 100644 --- a/app/src/main/java/foundation/e/apps/data/fusedDownload/IFusedManager.kt +++ b/app/src/main/java/foundation/e/apps/data/fusedDownload/IFusedManager.kt @@ -33,8 +33,6 @@ interface IFusedManager { suspend fun getDownloadList(): List fun getDownloadLiveList(): LiveData> - suspend fun clearInstallationIssue(fusedDownload: FusedDownload) - suspend fun updateDownloadStatus(fusedDownload: FusedDownload, status: Status) suspend fun downloadApp(fusedDownload: FusedDownload) diff --git a/app/src/main/java/foundation/e/apps/di/CommonUtilsModule.kt b/app/src/main/java/foundation/e/apps/di/CommonUtilsModule.kt index f118902f8..bb14ac08d 100644 --- a/app/src/main/java/foundation/e/apps/di/CommonUtilsModule.kt +++ b/app/src/main/java/foundation/e/apps/di/CommonUtilsModule.kt @@ -20,8 +20,6 @@ package foundation.e.apps.di import android.content.ClipboardManager import android.content.Context -import android.net.ConnectivityManager -import android.net.NetworkCapabilities import android.os.Build import android.os.Bundle import android.os.Environment @@ -117,29 +115,6 @@ object CommonUtilsModule { return Cache(context.cacheDir, cacheSize) } - /** - * Checks if device has internet connection available or not - * @param context [Context] - * @return true if internet connection is available, false otherwise - */ - @Singleton - @Provides - fun isNetworkAvailable(@ApplicationContext context: Context): Boolean { - val connectivityManager = - context.getSystemService(ConnectivityManager::class.java) - val capabilities = - connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) - - if (capabilities != null) { - if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && - capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) - ) { - return true - } - } - return false - } - /** * Prevents calling a route if the navigation is already done, i.e. prevents duplicate calls. * Source: https://nezspencer.medium.com/navigation-components-a-fix-for-navigation-action-cannot-be-found-in-the-current-destination-95b63e16152e diff --git a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt index 32c7d597b..d1b136736 100644 --- a/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt +++ b/app/src/main/java/foundation/e/apps/install/updates/UpdatesWorker.kt @@ -17,16 +17,14 @@ import dagger.assisted.AssistedInject import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.ResultStatus -import foundation.e.apps.data.enums.Type import foundation.e.apps.data.enums.User import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedManagerRepository -import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.LoginSourceRepository import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.data.updates.UpdatesManagerRepository -import foundation.e.apps.install.workmanager.InstallWorkManager +import foundation.e.apps.install.workmanager.AppInstallProcessor import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.Dispatchers @@ -43,6 +41,7 @@ class UpdatesWorker @AssistedInject constructor( private val fusedManagerRepository: FusedManagerRepository, private val dataStoreManager: DataStoreManager, private val loginSourceRepository: LoginSourceRepository, + private val appInstallProcessor: AppInstallProcessor ) : CoroutineWorker(context, params) { companion object { @@ -213,47 +212,7 @@ class UpdatesWorker @AssistedInject constructor( return@forEach } - val fusedDownload = FusedDownload( - fusedApp._id, - fusedApp.origin, - fusedApp.status, - fusedApp.name, - fusedApp.package_name, - mutableListOf(), - mutableMapOf(), - fusedApp.status, - fusedApp.type, - fusedApp.icon_image_path, - fusedApp.latest_version_code, - fusedApp.offer_type, - fusedApp.isFree, - fusedApp.originalSize - ) - - try { - updateFusedDownloadWithAppDownloadLink(fusedApp, authData, fusedDownload) - } catch (e: Exception) { - Timber.e(e) - EventBus.invokeEvent( - AppEvent.UpdateEvent( - ResultSupreme.WorkError( - ResultStatus.UNKNOWN, - fusedDownload - ) - ) - ) - return@forEach - } - - val isSuccess = fusedManagerRepository.addDownload(fusedDownload) - if (!isSuccess) { - Timber.i("Update adding ABORTED! status: $isSuccess") - return@forEach - } - - fusedManagerRepository.updateAwaiting(fusedDownload) - InstallWorkManager.enqueueWork(fusedDownload, true) - Timber.i("startUpdateProcess: Enqueued for update: ${fusedDownload.name} ${fusedDownload.id} ${fusedDownload.status}") + appInstallProcessor.initAppInstall(fusedApp, true) } } @@ -281,24 +240,6 @@ class UpdatesWorker @AssistedInject constructor( ) } - private suspend fun updateFusedDownloadWithAppDownloadLink( - app: FusedApp, - authData: AuthData, - fusedDownload: FusedDownload - ) { - val downloadList = mutableListOf() - if (app.type == Type.PWA) { - downloadList.add(app.url) - fusedDownload.downloadURLList = downloadList - } else { - fusedAPIRepository.updateFusedDownloadWithDownloadingInfo( - authData, - app.origin, - fusedDownload - ) - } - } - /* * Checks if the device is connected to a metered connection or not * @param context current Context diff --git a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt index 85020e64e..604365df4 100644 --- a/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt +++ b/app/src/main/java/foundation/e/apps/install/workmanager/AppInstallProcessor.kt @@ -19,16 +19,27 @@ package foundation.e.apps.install.workmanager import android.content.Context +import android.net.ConnectivityManager +import android.net.NetworkCapabilities +import android.os.Environment +import android.os.StatFs +import com.aurora.gplayapi.exceptions.ApiException import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R +import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Status +import foundation.e.apps.data.enums.Type +import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.UpdatesDao +import foundation.e.apps.data.fused.data.FusedApp import foundation.e.apps.data.fusedDownload.FusedDownloadRepository import foundation.e.apps.data.fusedDownload.FusedManagerRepository import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.install.updates.UpdatesNotifier +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus import foundation.e.apps.utils.getFormattedString import kotlinx.coroutines.flow.transformWhile import timber.log.Timber @@ -40,6 +51,7 @@ class AppInstallProcessor @Inject constructor( @ApplicationContext private val context: Context, private val fusedDownloadRepository: FusedDownloadRepository, private val fusedManagerRepository: FusedManagerRepository, + private val fusedAPIRepository: FusedAPIRepository, private val dataStoreManager: DataStoreManager ) { @@ -50,6 +62,145 @@ class AppInstallProcessor @Inject constructor( private const val DATE_FORMAT = "dd/MM/yyyy-HH:mm" } + /** + * creates [FusedDownload] from [FusedApp] and enqueues into WorkManager to run install process. + * @param fusedApp represents the app info which will be installed + * @param isAnUpdate indicates the app is requested for update or not + * + */ + suspend fun initAppInstall( + fusedApp: FusedApp, + isAnUpdate: Boolean = false + ) { + val fusedDownload = FusedDownload( + fusedApp._id, + fusedApp.origin, + fusedApp.status, + fusedApp.name, + fusedApp.package_name, + mutableListOf(), + mutableMapOf(), + fusedApp.status, + fusedApp.type, + fusedApp.icon_image_path, + fusedApp.latest_version_code, + fusedApp.offer_type, + fusedApp.isFree, + fusedApp.originalSize + ) + + if (fusedDownload.type == Type.PWA) { + fusedDownload.downloadURLList = mutableListOf(fusedApp.url) + } + + enqueueFusedDownload(fusedDownload, isAnUpdate) + } + + /** + * Enqueues [FusedDownload] into WorkManager to run app install process. Before enqueuing, + * It validates some corner cases + * @param fusedDownload represents the app downloading and installing related info, example- Installing Status, + * Url of the APK,OBB files are needed to be downloaded and installed etc. + * @param isAnUpdate indicates the app is requested for update or not + */ + suspend fun enqueueFusedDownload( + fusedDownload: FusedDownload, + isAnUpdate: Boolean = false + ) { + try { + val authData = dataStoreManager.getAuthData() + if (!fusedDownload.isFree && authData.isAnonymous) { + EventBus.invokeEvent(AppEvent.ErrorMessageEvent(R.string.paid_app_anonymous_message)) + return + } + + if (fusedDownload.type != Type.PWA && !updateDownloadUrls(fusedDownload)) return + + val downloadAdded = fusedManagerRepository.addDownload(fusedDownload) + if (!downloadAdded) { + Timber.i("Update adding ABORTED! status: $downloadAdded") + return + } + + if (!isNetworkAvailable()) { + fusedManagerRepository.installationIssue(fusedDownload) + EventBus.invokeEvent(AppEvent.NoInternetEvent(false)) + return + } + + if (!isStorageAvailable(fusedDownload)) { + Timber.d("Storage is not available for: ${fusedDownload.name} size: ${fusedDownload.appSize}") + fusedManagerRepository.installationIssue(fusedDownload) + EventBus.invokeEvent(AppEvent.ErrorMessageEvent(R.string.not_enough_storage)) + return + } + + fusedManagerRepository.updateAwaiting(fusedDownload) + InstallWorkManager.enqueueWork(fusedDownload, isAnUpdate) + } catch (e: Exception) { + Timber.e(e) + } + } + + // returns TRUE if updating urls is successful, otherwise false. + private suspend fun updateDownloadUrls(fusedDownload: FusedDownload): Boolean { + try { + updateFusedDownloadWithAppDownloadLink(fusedDownload) + } catch (e: ApiException.AppNotPurchased) { + fusedManagerRepository.addFusedDownloadPurchaseNeeded(fusedDownload) + EventBus.invokeEvent(AppEvent.AppPurchaseEvent(fusedDownload)) + return false + } catch (e: Exception) { + Timber.e(e) + EventBus.invokeEvent( + AppEvent.UpdateEvent( + ResultSupreme.WorkError( + ResultStatus.UNKNOWN, + fusedDownload + ) + ) + ) + return false + } + return true + } + + private suspend fun updateFusedDownloadWithAppDownloadLink( + fusedDownload: FusedDownload + ) { + fusedAPIRepository.updateFusedDownloadWithDownloadingInfo( + fusedDownload.origin, + fusedDownload + ) + } + + private fun isStorageAvailable(fusedDownload: FusedDownload): Boolean { + val availableSpace = calculateAvailableDiskSpace() + return availableSpace > fusedDownload.appSize + (500 * (1000 * 1000)) + } + + private fun calculateAvailableDiskSpace(): Long { + val path = Environment.getDataDirectory().absolutePath + val statFs = StatFs(path) + return statFs.availableBytes + } + + private fun isNetworkAvailable(): Boolean { + val connectivityManager = + context.getSystemService(ConnectivityManager::class.java) + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + ?: return false + + if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && + capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + ) { + return true + } + + return false + } + suspend fun processInstall( fusedDownloadId: String, isItUpdateWork: Boolean, @@ -207,16 +358,20 @@ class AppInstallProcessor @Inject constructor( when (fusedDownload.status) { Status.AWAITING, Status.DOWNLOADING -> { } + Status.DOWNLOADED -> { fusedManagerRepository.updateDownloadStatus(fusedDownload, Status.INSTALLING) } + Status.INSTALLING -> { Timber.i("===> doWork: Installing ${fusedDownload.name} ${fusedDownload.status}") } + Status.INSTALLED, Status.INSTALLATION_ISSUE -> { Timber.i("===> doWork: Installed/Failed: ${fusedDownload.name} ${fusedDownload.status}") finishInstallation(fusedDownload) } + else -> { Timber.wtf( TAG, diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt index a2426b41f..5d302687e 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt @@ -18,12 +18,8 @@ package foundation.e.apps.ui -import android.app.usage.StorageStatsManager import android.os.Build import android.os.Bundle -import android.os.Environment -import android.os.StatFs -import android.os.storage.StorageManager import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Lifecycle @@ -43,14 +39,12 @@ import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R -import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.install.updates.UpdatesNotifier -import foundation.e.apps.install.workmanager.InstallWorkManager import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.purchase.AppPurchaseFragmentDirections import foundation.e.apps.ui.settings.SettingsFragment @@ -62,9 +56,6 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch -import timber.log.Timber -import java.io.File -import java.util.UUID @AndroidEntryPoint class MainActivity : AppCompatActivity() { @@ -173,19 +164,8 @@ class MainActivity : AppCompatActivity() { viewModel.createNotificationChannels() } - // Observe and handle downloads - viewModel.downloadList.observe(this) { list -> - list.forEach { - if (it.status == Status.QUEUED) { - handleFusedDownloadQueued(it, viewModel) - } - } - } - viewModel.purchaseAppLiveData.observe(this) { - val action = - AppPurchaseFragmentDirections.actionGlobalAppPurchaseFragment(it.packageName) - findNavController(R.id.fragment).navigate(action) + goToAppPurchaseFragment(it) } viewModel.errorMessage.observe(this) { @@ -230,10 +210,54 @@ class MainActivity : AppCompatActivity() { launch { observeSignatureMissMatchError() } + + launch { + observerErrorEvent() + } + + launch { + observeAppPurchaseFragment() + } + + launch { + observeNoInternetEvent() + } } } } + private suspend fun observeNoInternetEvent() { + EventBus.events.filter { appEvent -> + appEvent is AppEvent.NoInternetEvent + }.collectLatest { + if (!(it.data as Boolean)) { + showNoInternet() + } + } + } + + private suspend fun observeAppPurchaseFragment() { + EventBus.events.filter { appEvent -> + appEvent is AppEvent.AppPurchaseEvent + }.collectLatest { + goToAppPurchaseFragment(it.data as FusedDownload) + } + } + + private fun goToAppPurchaseFragment(it: FusedDownload) { + val action = + AppPurchaseFragmentDirections.actionGlobalAppPurchaseFragment(it.packageName) + findNavController(R.id.fragment).navigate(action) + } + + private suspend fun observerErrorEvent() { + EventBus.events.filter { appEvent -> + appEvent is AppEvent.ErrorMessageEvent + }.collectLatest { + showSnackbarMessage(getString(it.data as Int)) + } + } + private suspend fun observeSignatureMissMatchError() { EventBus.events.filter { appEvent -> appEvent is AppEvent.SignatureMissMatchError @@ -284,35 +308,13 @@ class MainActivity : AppCompatActivity() { } } - private fun handleFusedDownloadQueued( - it: FusedDownload, - viewModel: MainActivityViewModel - ) { - lifecycleScope.launch { - if (!isStorageAvailable(it)) { - showSnackbarMessage(getString(R.string.not_enough_storage)) - viewModel.updateUnAvailable(it) - return@launch - } - if (viewModel.internetConnection.value == false) { - showNoInternet() - viewModel.updateUnAvailable(it) - return@launch - } - viewModel.updateAwaiting(it) - InstallWorkManager.enqueueWork(it) - Timber.d("===> onCreate: AWAITING ${it.name}") - } - } - private fun startInstallationOfPurchasedApp( viewModel: MainActivityViewModel, - it: String + packageName: String ) { lifecycleScope.launch { - val fusedDownload = viewModel.updateAwaitingForPurchasedApp(it) + val fusedDownload = viewModel.updateAwaitingForPurchasedApp(packageName) if (fusedDownload != null) { - InstallWorkManager.enqueueWork(fusedDownload) ApplicationDialogFragment( title = getString(R.string.purchase_complete), message = getString(R.string.download_automatically_message), @@ -336,38 +338,4 @@ class MainActivity : AppCompatActivity() { binding.noInternet.visibility = View.VISIBLE binding.fragment.visibility = View.GONE } - - // TODO: move storage availability code to FileManager Class - private fun isStorageAvailable(fusedDownload: FusedDownload): Boolean { - val availableSpace = calculateAvailableDiskSpace() - return availableSpace > fusedDownload.appSize + (500 * (1000 * 1000)) - } - - private fun calculateAvailableDiskSpace(): Long { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val storageManager = getSystemService(STORAGE_SERVICE) as StorageManager - val statsManager = getSystemService(STORAGE_STATS_SERVICE) as StorageStatsManager - val uuid = storageManager.primaryStorageVolume.uuid - try { - if (uuid != null) { - statsManager.getFreeBytes(UUID.fromString(uuid)) - } else { - statsManager.getFreeBytes(StorageManager.UUID_DEFAULT) - } - } catch (e: Exception) { - Timber.e("calculateAvailableDiskSpace: ${e.stackTraceToString()}") - getAvailableInternalMemorySize() - } - } else { - getAvailableInternalMemorySize() - } - } - - private fun getAvailableInternalMemorySize(): Long { - val path: File = Environment.getDataDirectory() - val stat = StatFs(path.path) - val blockSize = stat.blockSizeLong - val availableBlocks = stat.availableBlocksLong - return availableBlocks * blockSize - } } diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt index 97b9a1a88..8c81f0003 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivityViewModel.kt @@ -34,14 +34,10 @@ import androidx.lifecycle.asLiveData import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.exceptions.ApiException import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.R import foundation.e.apps.data.blockedApps.BlockedAppRepository import foundation.e.apps.data.ecloud.EcloudRepository -import foundation.e.apps.data.enums.Origin -import foundation.e.apps.data.enums.Status -import foundation.e.apps.data.enums.Type import foundation.e.apps.data.enums.User import foundation.e.apps.data.enums.isInitialized import foundation.e.apps.data.enums.isUnFiltered @@ -52,6 +48,7 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.preference.DataStoreModule import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule +import foundation.e.apps.install.workmanager.AppInstallProcessor import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow @@ -67,6 +64,7 @@ class MainActivityViewModel @Inject constructor( private val pwaManagerModule: PWAManagerModule, private val ecloudRepository: EcloudRepository, private val blockedAppRepository: BlockedAppRepository, + private val appInstallProcessor: AppInstallProcessor ) : ViewModel() { val tocStatus: LiveData = dataStoreModule.tocStatus.asLiveData() @@ -185,98 +183,16 @@ class MainActivityViewModel @Inject constructor( } fun getApplication(app: FusedApp) { - if (shouldShowPaidAppsSnackBar(app)) { - return - } - viewModelScope.launch { - val fusedDownload: FusedDownload - try { - fusedDownload = FusedDownload( - app._id, - app.origin, - app.status, - app.name, - app.package_name, - mutableListOf(), - mutableMapOf(), - app.status, - app.type, - app.icon_image_path, - app.latest_version_code, - app.offer_type, - app.isFree, - app.originalSize - ) - updateFusedDownloadWithAppDownloadLink(app, fusedDownload) - } catch (e: Exception) { - if (e is ApiException.AppNotPurchased) { - handleAppNotPurchased(app) - return@launch - } - _errorMessage.value = e - return@launch - } - - if (fusedDownload.status == Status.INSTALLATION_ISSUE) { - fusedManagerRepository.clearInstallationIssue(fusedDownload) - } - fusedManagerRepository.addDownload(fusedDownload) + appInstallProcessor.initAppInstall(app) } } - private fun handleAppNotPurchased( - app: FusedApp - ) { - val fusedDownload = FusedDownload( - app._id, - app.origin, - Status.PURCHASE_NEEDED, - app.name, - app.package_name, - mutableListOf(), - mutableMapOf(), - app.status, - app.type, - app.icon_image_path, - app.latest_version_code, - app.offer_type, - app.isFree, - app.originalSize - ) - viewModelScope.launch { - fusedManagerRepository.addFusedDownloadPurchaseNeeded(fusedDownload) - _purchaseAppLiveData.postValue(fusedDownload) - } - } - - suspend fun updateAwaiting(fusedDownload: FusedDownload) { - fusedManagerRepository.updateAwaiting(fusedDownload) - } - - suspend fun updateUnAvailable(fusedDownload: FusedDownload) { - fusedManagerRepository.updateUnavailable(fusedDownload) - } - suspend fun updateAwaitingForPurchasedApp(packageName: String): FusedDownload? { val fusedDownload = fusedManagerRepository.getFusedDownload(packageName = packageName) gPlayAuthData.let { if (!it.isAnonymous) { - try { - fusedAPIRepository.updateFusedDownloadWithDownloadingInfo( - it, - Origin.GPLAY, - fusedDownload - ) - } catch (e: ApiException.AppNotPurchased) { - e.printStackTrace() - return null - } catch (e: Exception) { - e.printStackTrace() - _errorMessage.value = e - return null - } - updateAwaiting(fusedDownload) + appInstallProcessor.enqueueFusedDownload(fusedDownload) return fusedDownload } } @@ -296,25 +212,6 @@ class MainActivityViewModel @Inject constructor( } } - private suspend fun updateFusedDownloadWithAppDownloadLink( - app: FusedApp, - fusedDownload: FusedDownload - ) { - val downloadList = mutableListOf() - gPlayAuthData.let { - if (app.type == Type.PWA) { - downloadList.add(app.url) - fusedDownload.downloadURLList = downloadList - } else { - fusedAPIRepository.updateFusedDownloadWithDownloadingInfo( - it, - app.origin, - fusedDownload - ) - } - } - } - fun setupConnectivityManager(context: Context) { connectivityManager = context.getSystemService(ConnectivityManager::class.java) as ConnectivityManager @@ -370,7 +267,8 @@ class MainActivityViewModel @Inject constructor( // protected to avoid SyntheticAccessor protected fun ProducerScope.sendInternetStatus(connectivityManager: ConnectivityManager) { - val capabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) + val capabilities = + connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) val hasInternet = capabilities != null && diff --git a/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsRVAdapter.kt index f5e2cacb5..9fefd43d0 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/model/ApplicationScreenshotsRVAdapter.kt @@ -58,9 +58,6 @@ class ApplicationScreenshotsRVAdapter( Origin.GPLAY -> { imageView.load(oldList[position]) } - Origin.GITLAB -> { - TODO("YET TO BE IMPLEMENTED") - } } imageView.setOnClickListener { val action = diff --git a/app/src/main/java/foundation/e/apps/ui/application/model/ScreenshotRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/application/model/ScreenshotRVAdapter.kt index b5533935d..01fe6c50b 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/model/ScreenshotRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/model/ScreenshotRVAdapter.kt @@ -66,9 +66,8 @@ class ScreenshotRVAdapter(private val list: List, private val origin: Or placeholder(circularProgressDrawable) } } - Origin.GITLAB -> { - TODO("YET TO BE IMPLEMENTED") - } + + else -> {} } } diff --git a/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt b/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt index d979763ed..1074bbdbb 100644 --- a/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt +++ b/app/src/main/java/foundation/e/apps/utils/eventBus/AppEvent.kt @@ -22,10 +22,14 @@ package foundation.e.apps.utils.eventBus import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.ResultStatus +import foundation.e.apps.data.fusedDownload.models.FusedDownload sealed class AppEvent(val data: Any) { class SignatureMissMatchError(packageName: String) : AppEvent(packageName) class UpdateEvent(result: ResultSupreme.WorkError) : AppEvent(result) class InvalidAuthEvent(authName: String) : AppEvent(authName) + class ErrorMessageEvent(stringResourceId: Int) : AppEvent(stringResourceId) + class AppPurchaseEvent(fusedDownload: FusedDownload) : AppEvent(fusedDownload) + class NoInternetEvent(isInternetAvailable: Boolean) : AppEvent(isInternetAvailable) } -- GitLab