Loading app/build.gradle +2 −2 Original line number Diff line number Diff line Loading @@ -90,8 +90,8 @@ android { buildTypes { debug { versionNameSuffix ".debug" applicationIdSuffix ".debug" // versionNameSuffix ".debug" // applicationIdSuffix ".debug" signingConfig signingConfigs.debugConfig proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } Loading app/src/main/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,15 @@ </intent-filter> </receiver> <receiver android:name=".receivers.DumpAppInstallStatusReceiver" android:enabled="true" android:exported="true" tools:ignore="ExportedReceiver"> <intent-filter> <action android:name="foundation.e.apps.action.APP_INSTALL_STATE"/> </intent-filter> </receiver> <!-- TODO: ExportedReceiver, suppressing because changes are needed in other apps --> <receiver android:name=".install.receiver.PWAPlayerStatusReceiver" tools:ignore="ExportedReceiver" Loading app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +6 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import androidx.work.ExistingPeriodicWorkPolicy import dagger.hilt.android.HiltAndroidApp import foundation.e.apps.data.Constants.TAG_APP_INSTALL_STATE import foundation.e.apps.data.Constants.TAG_AUTHDATA_DUMP import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference Loading Loading @@ -90,7 +91,11 @@ class AppLoungeApplication : Application(), Configuration.Provider { Telemetry.init(BuildConfig.SENTRY_DSN, this) plant(object : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (priority <= Log.WARN && tag != TAG_AUTHDATA_DUMP) { if (priority <= Log.WARN && !listOf( TAG_AUTHDATA_DUMP, TAG_APP_INSTALL_STATE ).contains(tag) ) { return } Log.println(priority, tag, message) Loading app/src/main/java/foundation/e/apps/data/Constants.kt +3 −0 Original line number Diff line number Diff line Loading @@ -9,4 +9,7 @@ object Constants { const val ACTION_AUTHDATA_DUMP = "foundation.e.apps.action.DUMP_GACCOUNT_INFO" const val TAG_AUTHDATA_DUMP = "AUTHDATA_DUMP" const val ACTION_DUMP_APP_INSTALL_STATE = "foundation.e.apps.action.APP_INSTALL_STATE" const val TAG_APP_INSTALL_STATE = "APP_INSTALL_STATE" } app/src/main/java/foundation/e/apps/install/download/DownloadManagerUtils.kt +50 −20 Original line number Diff line number Diff line Loading @@ -29,14 +29,16 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.install.notification.StorageNotificationManager import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import android.app.DownloadManager as PlatformDownloadManager import timber.log.Timber import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton @Singleton Loading @@ -45,12 +47,13 @@ class DownloadManagerUtils @Inject constructor( private val fusedManagerRepository: FusedManagerRepository, private val downloadManager: DownloadManager, private val storageNotificationManager: StorageNotificationManager, @Named("ioCoroutineScope") private val coroutineScope: CoroutineScope ) { private val mutex = Mutex() @DelicateCoroutinesApi fun cancelDownload(downloadId: Long) { GlobalScope.launch { coroutineScope.launch { val fusedDownload = fusedManagerRepository.getFusedDownload(downloadId) fusedManagerRepository.cancelDownload(fusedDownload) } Loading @@ -58,27 +61,25 @@ class DownloadManagerUtils @Inject constructor( @DelicateCoroutinesApi fun updateDownloadStatus(downloadId: Long) { GlobalScope.launch { coroutineScope.launch { mutex.withLock { delay(1500) // Waiting for downloadmanager to publish the progress of last bytes val fusedDownload = fusedManagerRepository.getFusedDownload(downloadId) if (fusedDownload.id.isNotEmpty()) { updateDownloadIdMap(fusedDownload, downloadId) val numberOfDownloadedItems = fusedDownload.downloadIdMap.values.filter { it }.size Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}") if (downloadManager.hasDownloadFailed(downloadId)) { handleDownloadFailed(fusedDownload, downloadId) Timber.e( "Download failed for ${fusedDownload.packageName}, " + "reason: " + "${downloadManager.getDownloadFailureReason(downloadId)}" "Download failed for ${fusedDownload.packageName}, " + "reason: " + "${ downloadManager.getDownloadFailureReason( downloadId ) }" ) return@launch } if (validateDownload(numberOfDownloadedItems, fusedDownload, downloadId)) { handleDownloadSuccess(fusedDownload) } validateDownload(fusedDownload, downloadId) } } } Loading @@ -87,9 +88,11 @@ class DownloadManagerUtils @Inject constructor( private suspend fun handleDownloadSuccess(fusedDownload: FusedDownload) { Timber.i("===> Download is completed for: ${fusedDownload.name}") fusedManagerRepository.moveOBBFileToOBBDirectory(fusedDownload) if (fusedDownload.status == Status.DOWNLOADING) { fusedDownload.status = Status.DOWNLOADED fusedManagerRepository.updateFusedDownload(fusedDownload) } } private suspend fun handleDownloadFailed(fusedDownload: FusedDownload, downloadId: Long) { fusedManagerRepository.installationIssue(fusedDownload) Loading @@ -103,20 +106,47 @@ class DownloadManagerUtils @Inject constructor( } private suspend fun validateDownload( numberOfDownloadedItems: Int, fusedDownload: FusedDownload, downloadId: Long ): Boolean { ) { val incompleteDownloadState = listOf( PlatformDownloadManager.STATUS_PENDING, PlatformDownloadManager.STATUS_RUNNING, PlatformDownloadManager.STATUS_PAUSED, ) val isDownloadSuccessful = downloadManager.isDownloadSuccessful(downloadId) if (isDownloadSuccessful.first) { updateDownloadIdMap(fusedDownload, downloadId) } val numberOfDownloadedItems = fusedDownload.downloadIdMap.values.filter { it }.size Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}") // if download status code is unknown (-1), consider installation is failed. if (isDownloadSuccessful.second == -1) { handleDownloadFailed(fusedDownload, downloadId) val areAllFilesDownloaded = areAllFilesDownloaded( numberOfDownloadedItems, fusedDownload ) if (isDownloadSuccessful.first && areAllFilesDownloaded && checkCleanApkSignatureOK(fusedDownload)) { handleDownloadSuccess(fusedDownload) return } return isDownloadSuccessful.first && areAllFilesDownloaded( numberOfDownloadedItems, fusedDownload ) && checkCleanApkSignatureOK(fusedDownload) if (incompleteDownloadState.contains(isDownloadSuccessful.second) || (isDownloadSuccessful.first && !areAllFilesDownloaded) ) { return } handleDownloadFailed(fusedDownload, downloadId) Timber.e( "Download failed for ${fusedDownload.packageName}: " + "Download Status: ${isDownloadSuccessful.second}" ) } private fun areAllFilesDownloaded( Loading Loading
app/build.gradle +2 −2 Original line number Diff line number Diff line Loading @@ -90,8 +90,8 @@ android { buildTypes { debug { versionNameSuffix ".debug" applicationIdSuffix ".debug" // versionNameSuffix ".debug" // applicationIdSuffix ".debug" signingConfig signingConfigs.debugConfig proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } Loading
app/src/main/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -83,6 +83,15 @@ </intent-filter> </receiver> <receiver android:name=".receivers.DumpAppInstallStatusReceiver" android:enabled="true" android:exported="true" tools:ignore="ExportedReceiver"> <intent-filter> <action android:name="foundation.e.apps.action.APP_INSTALL_STATE"/> </intent-filter> </receiver> <!-- TODO: ExportedReceiver, suppressing because changes are needed in other apps --> <receiver android:name=".install.receiver.PWAPlayerStatusReceiver" tools:ignore="ExportedReceiver" Loading
app/src/main/java/foundation/e/apps/AppLoungeApplication.kt +6 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration import androidx.work.ExistingPeriodicWorkPolicy import dagger.hilt.android.HiltAndroidApp import foundation.e.apps.data.Constants.TAG_APP_INSTALL_STATE import foundation.e.apps.data.Constants.TAG_AUTHDATA_DUMP import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference Loading Loading @@ -90,7 +91,11 @@ class AppLoungeApplication : Application(), Configuration.Provider { Telemetry.init(BuildConfig.SENTRY_DSN, this) plant(object : Timber.Tree() { override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (priority <= Log.WARN && tag != TAG_AUTHDATA_DUMP) { if (priority <= Log.WARN && !listOf( TAG_AUTHDATA_DUMP, TAG_APP_INSTALL_STATE ).contains(tag) ) { return } Log.println(priority, tag, message) Loading
app/src/main/java/foundation/e/apps/data/Constants.kt +3 −0 Original line number Diff line number Diff line Loading @@ -9,4 +9,7 @@ object Constants { const val ACTION_AUTHDATA_DUMP = "foundation.e.apps.action.DUMP_GACCOUNT_INFO" const val TAG_AUTHDATA_DUMP = "AUTHDATA_DUMP" const val ACTION_DUMP_APP_INSTALL_STATE = "foundation.e.apps.action.APP_INSTALL_STATE" const val TAG_APP_INSTALL_STATE = "APP_INSTALL_STATE" }
app/src/main/java/foundation/e/apps/install/download/DownloadManagerUtils.kt +50 −20 Original line number Diff line number Diff line Loading @@ -29,14 +29,16 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.install.notification.StorageNotificationManager import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import android.app.DownloadManager as PlatformDownloadManager import timber.log.Timber import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton @Singleton Loading @@ -45,12 +47,13 @@ class DownloadManagerUtils @Inject constructor( private val fusedManagerRepository: FusedManagerRepository, private val downloadManager: DownloadManager, private val storageNotificationManager: StorageNotificationManager, @Named("ioCoroutineScope") private val coroutineScope: CoroutineScope ) { private val mutex = Mutex() @DelicateCoroutinesApi fun cancelDownload(downloadId: Long) { GlobalScope.launch { coroutineScope.launch { val fusedDownload = fusedManagerRepository.getFusedDownload(downloadId) fusedManagerRepository.cancelDownload(fusedDownload) } Loading @@ -58,27 +61,25 @@ class DownloadManagerUtils @Inject constructor( @DelicateCoroutinesApi fun updateDownloadStatus(downloadId: Long) { GlobalScope.launch { coroutineScope.launch { mutex.withLock { delay(1500) // Waiting for downloadmanager to publish the progress of last bytes val fusedDownload = fusedManagerRepository.getFusedDownload(downloadId) if (fusedDownload.id.isNotEmpty()) { updateDownloadIdMap(fusedDownload, downloadId) val numberOfDownloadedItems = fusedDownload.downloadIdMap.values.filter { it }.size Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}") if (downloadManager.hasDownloadFailed(downloadId)) { handleDownloadFailed(fusedDownload, downloadId) Timber.e( "Download failed for ${fusedDownload.packageName}, " + "reason: " + "${downloadManager.getDownloadFailureReason(downloadId)}" "Download failed for ${fusedDownload.packageName}, " + "reason: " + "${ downloadManager.getDownloadFailureReason( downloadId ) }" ) return@launch } if (validateDownload(numberOfDownloadedItems, fusedDownload, downloadId)) { handleDownloadSuccess(fusedDownload) } validateDownload(fusedDownload, downloadId) } } } Loading @@ -87,9 +88,11 @@ class DownloadManagerUtils @Inject constructor( private suspend fun handleDownloadSuccess(fusedDownload: FusedDownload) { Timber.i("===> Download is completed for: ${fusedDownload.name}") fusedManagerRepository.moveOBBFileToOBBDirectory(fusedDownload) if (fusedDownload.status == Status.DOWNLOADING) { fusedDownload.status = Status.DOWNLOADED fusedManagerRepository.updateFusedDownload(fusedDownload) } } private suspend fun handleDownloadFailed(fusedDownload: FusedDownload, downloadId: Long) { fusedManagerRepository.installationIssue(fusedDownload) Loading @@ -103,20 +106,47 @@ class DownloadManagerUtils @Inject constructor( } private suspend fun validateDownload( numberOfDownloadedItems: Int, fusedDownload: FusedDownload, downloadId: Long ): Boolean { ) { val incompleteDownloadState = listOf( PlatformDownloadManager.STATUS_PENDING, PlatformDownloadManager.STATUS_RUNNING, PlatformDownloadManager.STATUS_PAUSED, ) val isDownloadSuccessful = downloadManager.isDownloadSuccessful(downloadId) if (isDownloadSuccessful.first) { updateDownloadIdMap(fusedDownload, downloadId) } val numberOfDownloadedItems = fusedDownload.downloadIdMap.values.filter { it }.size Timber.d("===> updateDownloadStatus: ${fusedDownload.name}: $downloadId: $numberOfDownloadedItems/${fusedDownload.downloadIdMap.size}") // if download status code is unknown (-1), consider installation is failed. if (isDownloadSuccessful.second == -1) { handleDownloadFailed(fusedDownload, downloadId) val areAllFilesDownloaded = areAllFilesDownloaded( numberOfDownloadedItems, fusedDownload ) if (isDownloadSuccessful.first && areAllFilesDownloaded && checkCleanApkSignatureOK(fusedDownload)) { handleDownloadSuccess(fusedDownload) return } return isDownloadSuccessful.first && areAllFilesDownloaded( numberOfDownloadedItems, fusedDownload ) && checkCleanApkSignatureOK(fusedDownload) if (incompleteDownloadState.contains(isDownloadSuccessful.second) || (isDownloadSuccessful.first && !areAllFilesDownloaded) ) { return } handleDownloadFailed(fusedDownload, downloadId) Timber.e( "Download failed for ${fusedDownload.packageName}: " + "Download Status: ${isDownloadSuccessful.second}" ) } private fun areAllFilesDownloaded( Loading