diff --git a/app/src/main/java/foundation/e/apps/data/DownloadManager.kt b/app/src/main/java/foundation/e/apps/data/DownloadManager.kt index dfcdc6cf762f7a9356062328e91460053c01e926..8a49caeb70058ca99f76ee23f7b76652d2f77e30 100644 --- a/app/src/main/java/foundation/e/apps/data/DownloadManager.kt +++ b/app/src/main/java/foundation/e/apps/data/DownloadManager.kt @@ -50,8 +50,11 @@ class DownloadManager @Inject constructor( ) { private val downloadsMaps = HashMap() - private val SDCARD_PATH = Environment.getExternalStorageDirectory().absolutePath - val EXTERNAL_STORAGE_TEMP_CACHE_DIR = "$SDCARD_PATH/Download/AppLounge/SplitInstallApks" + companion object { + private val SDCARD_PATH = Environment.getExternalStorageDirectory().absolutePath + val EXTERNAL_STORAGE_TEMP_CACHE_DIR = "$SDCARD_PATH/Download/AppLounge/SplitInstallApks" + private const val UNKNOWN_ERROR_OR_REASON = -1 + } fun downloadFileInCache( url: String, @@ -186,7 +189,8 @@ class DownloadManager @Inject constructor( } fun hasDownloadFailed(downloadId: Long): Boolean { - return getDownloadStatus(downloadId) == DownloadManager.STATUS_FAILED + return listOf(DownloadManager.STATUS_FAILED, UNKNOWN_ERROR_OR_REASON) + .contains(getDownloadStatus(downloadId)) } fun getSizeRequired(downloadId: Long): Long { @@ -218,8 +222,8 @@ class DownloadManager @Inject constructor( } private fun getDownloadStatus(downloadId: Long): Int { - var status = -1 - var reason = -1 + var status = UNKNOWN_ERROR_OR_REASON + var reason = UNKNOWN_ERROR_OR_REASON try { downloadManager.query(downloadManagerQuery.setFilterById(downloadId)) .use { cursor -> @@ -261,7 +265,7 @@ class DownloadManager @Inject constructor( } fun getDownloadFailureReason(downloadId: Long): Int { - var reason = -1 + var reason = UNKNOWN_ERROR_OR_REASON try { downloadManager.query(downloadManagerQuery.setFilterById(downloadId)) .use { cursor -> diff --git a/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt index ebb9d311158a3920f9161c53b06b3305a051758d..62876de4baf222ce94bed89ec59ab1871b6815ff 100644 --- a/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/apps/AppsApiImpl.kt @@ -114,11 +114,8 @@ class AppsApiImpl @Inject constructor( val applicationList = mutableListOf() for (packageName in packageNameList) { - val result = getCleanApkSearchResultByPackageName(packageName, applicationList) - status = result.getResultStatus() - - if (status != ResultStatus.OK) { - return Pair(applicationList, status) + getCleanApkSearchResultByPackageName(packageName).data?.run { + handleCleanApkSearch(applicationList) } } @@ -151,10 +148,11 @@ class AppsApiImpl @Inject constructor( authData: AuthData, applicationList: MutableList ) { - val filter = applicationDataManager.getAppFilterLevel(app.toApplication(context), authData) + val application = app.toApplication(context) + val filter = applicationDataManager.getAppFilterLevel(application, authData) if (filter.isUnFiltered()) { applicationList.add( - app.toApplication(context).apply { + application.apply { filterLevel = filter } ) @@ -163,14 +161,11 @@ class AppsApiImpl @Inject constructor( private suspend fun getCleanApkSearchResultByPackageName( packageName: String, - applicationList: MutableList ) = handleNetworkResult { appSources.cleanApkAppsRepo.getSearchResult( packageName, KEY_SEARCH_PACKAGE_NAME - ).body()?.run { - handleCleanApkSearch(applicationList) - } + ).body() } private suspend fun Search.handleCleanApkSearch( diff --git a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt index 1da8e13a55461574d2501095fad862533d55d547..7cc60ac310ec918f13b523df39f22d97e43146dc 100644 --- a/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/updates/UpdatesManagerImpl.kt @@ -30,9 +30,11 @@ import foundation.e.apps.data.enums.Status import foundation.e.apps.data.enums.isUnFiltered import foundation.e.apps.data.faultyApps.FaultyAppRepository import foundation.e.apps.data.fdroid.FdroidRepository +import foundation.e.apps.data.playstore.PlayStoreRepositoryImpl import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.search.SearchApi.Companion.APP_TYPE_ANY import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.install.pkg.AppLoungePackageManager import kotlinx.coroutines.Dispatchers @@ -196,8 +198,9 @@ class UpdatesManagerImpl @Inject constructor( * Aurora Store, Apk mirror, apps installed from browser, apps from ADB etc. */ private fun getAppsFromOtherStores(): List { + val gplayAndOpenSourceInstalledApps = getGPlayInstalledApps() + getOpenSourceInstalledApps() return userApplications.filter { - it.packageName !in (getGPlayInstalledApps() + getOpenSourceInstalledApps()) + it.packageName !in gplayAndOpenSourceInstalledApps }.map { it.packageName } } @@ -225,8 +228,8 @@ class UpdatesManagerImpl @Inject constructor( } /** - * Bulk info from gplay api is not providing correct geo restriction status of apps. - * So we get all individual app information asynchronously. + * Bulk info from gplay api [PlayStoreRepositoryImpl.getAppsDetails] is not providing correct + * geo restriction status of apps. So we get all individual app information asynchronously. * Example: in.startv.hotstar.dplus * Issue: https://gitlab.e.foundation/e/backlog/-/issues/7135 */ @@ -250,7 +253,7 @@ class UpdatesManagerImpl @Inject constructor( } val status = appsResults.find { it.second != ResultStatus.OK }?.second ?: ResultStatus.OK - val appsList = appsResults.map { it.first } + val appsList = appsResults.filter { it.first.package_name.isNotEmpty() }.map { it.first } return Pair(appsList, status) } @@ -273,32 +276,40 @@ class UpdatesManagerImpl @Inject constructor( private suspend fun getFDroidAppsAndSignatures(installedPackageNames: List): Map { val appsAndSignatures = hashMapOf() for (packageName in installedPackageNames) { + updateAppsWithPGPSignature(packageName, appsAndSignatures) + } + return appsAndSignatures + } + + private suspend fun updateAppsWithPGPSignature( + packageName: String, + appsAndSignatures: HashMap + ) { val cleanApkFusedApp = applicationRepository.getCleanapkAppDetails(packageName).first if (cleanApkFusedApp.package_name.isBlank()) { - continue + return } appsAndSignatures[packageName] = getPgpSignature(cleanApkFusedApp) - } - return appsAndSignatures } private suspend fun getPgpSignature(cleanApkApplication: Application): String { val installedVersionSignature = calculateSignatureVersion(cleanApkApplication) - val downloadInfo = + val downloadInfoResult = handleNetworkResult { applicationRepository .getOSSDownloadInfo(cleanApkApplication._id, installedVersionSignature) .body()?.download_data + } - val pgpSignature = downloadInfo?.signature ?: "" + val pgpSignature = downloadInfoResult.data?.signature ?: "" Timber.i( "Signature calculated for : ${cleanApkApplication.package_name}, " + - "signature version: $installedVersionSignature, " + - "is sig blank: ${pgpSignature.isBlank()}" + "signature version: $installedVersionSignature, " + + "is sig blank: ${pgpSignature.isBlank()}" ) - return downloadInfo?.signature ?: "" + return pgpSignature } /** @@ -314,8 +325,12 @@ class UpdatesManagerImpl @Inject constructor( val fDroidAppsAndSignatures = getFDroidAppsAndSignatures(installedPackageNames) val fDroidUpdatablePackageNames = fDroidAppsAndSignatures.filter { + if (it.value.isEmpty()) return@filter false + // For each installed app also present on F-droid, check signature of base APK. val baseApkPath = appLoungePackageManager.getBaseApkPath(it.key) + if (baseApkPath.isEmpty()) return@filter false + ApkSignatureManager.verifyFdroidSignature(context, baseApkPath, it.value, it.key) }.map { it.key } @@ -358,9 +373,11 @@ class UpdatesManagerImpl @Inject constructor( // Received list has build info of the latest version at the bottom. // We want it at the top. - val builds = fdroidRepository.getBuildVersionInfo(packageName)?.asReversed() ?: return "" + val builds = handleNetworkResult { + fdroidRepository.getBuildVersionInfo(packageName)?.asReversed() ?: listOf() + }.data - val matchingIndex = builds.find { + val matchingIndex = builds?.find { it.versionCode == installedVersionCode && it.versionName == installedVersionName }?.run { builds.indexOf(this) diff --git a/app/src/main/java/foundation/e/apps/install/pkg/AppLoungePackageManager.kt b/app/src/main/java/foundation/e/apps/install/pkg/AppLoungePackageManager.kt index 0dc514861388ed439dc32e89d0b1e828a6257b02..04314ee859f9a774c6998452c1d2ddb1c791120b 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/AppLoungePackageManager.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/AppLoungePackageManager.kt @@ -27,6 +27,7 @@ import android.content.pm.PackageInfo import android.content.pm.PackageInstaller.Session import android.content.pm.PackageInstaller.SessionParams import android.content.pm.PackageManager +import android.content.pm.PackageManager.NameNotFoundException import android.os.Build import androidx.core.content.pm.PackageInfoCompat import dagger.hilt.android.qualifiers.ApplicationContext @@ -39,6 +40,7 @@ import foundation.e.apps.data.fusedDownload.models.FusedDownload import kotlinx.coroutines.DelicateCoroutinesApi import timber.log.Timber import java.io.File +import java.lang.IllegalArgumentException import javax.inject.Inject import javax.inject.Singleton @@ -51,7 +53,9 @@ class AppLoungePackageManager @Inject constructor( const val ERROR_PACKAGE_INSTALL = "ERROR_PACKAGE_INSTALL" const val PACKAGE_NAME = "packageName" const val FAKE_STORE_PACKAGE_NAME = "com.android.vending" + private const val UNKNOWN_VALUE = "" } + private val packageManager = context.packageManager fun isInstalled(packageName: String): Boolean { @@ -69,15 +73,8 @@ class AppLoungePackageManager @Inject constructor( } private fun isUpdatable(packageName: String, versionCode: Int): Boolean { - return try { - val packageInfo = getPackageInfo(packageName) - packageInfo?.let { - return versionCode.toLong() > PackageInfoCompat.getLongVersionCode(it) - } - true - } catch (e: PackageManager.NameNotFoundException) { - false - } + val packageInfo = getPackageInfo(packageName) ?: return false + return versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) } fun getLaunchIntent(packageName: String): Intent? { @@ -85,7 +82,12 @@ class AppLoungePackageManager @Inject constructor( } private fun getPackageInfo(packageName: String): PackageInfo? { - return packageManager.getPackageInfo(packageName, 0) + return try { + packageManager.getPackageInfo(packageName, 0) + } catch (e: NameNotFoundException) { + Timber.e("getPackageInfo: ${e.localizedMessage}") + null + } } /** @@ -133,11 +135,19 @@ class AppLoungePackageManager @Inject constructor( } fun getInstallerName(packageName: String): String { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - val installerInfo = packageManager.getInstallSourceInfo(packageName) - installerInfo.originatingPackageName ?: installerInfo.installingPackageName ?: "" - } else { - packageManager.getInstallerPackageName(packageName) ?: "" + return try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val installerInfo = packageManager.getInstallSourceInfo(packageName) + installerInfo.originatingPackageName ?: installerInfo.installingPackageName ?: UNKNOWN_VALUE + } else { + packageManager.getInstallerPackageName(packageName) ?: UNKNOWN_VALUE + } + } catch (e: NameNotFoundException) { + Timber.e("getInstallerName -> $packageName : ${e.localizedMessage}") + UNKNOWN_VALUE + } catch (e: IllegalArgumentException) { + Timber.e("getInstallerName -> $packageName : ${e.localizedMessage}") + UNKNOWN_VALUE } } @@ -146,22 +156,22 @@ class AppLoungePackageManager @Inject constructor( */ fun getBaseApkPath(packageName: String): String { val packageInfo = getPackageInfo(packageName) - return packageInfo?.applicationInfo?.publicSourceDir ?: "" + return packageInfo?.applicationInfo?.publicSourceDir ?: UNKNOWN_VALUE } fun getVersionCode(packageName: String): String { val packageInfo = getPackageInfo(packageName) return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - packageInfo?.longVersionCode?.toString() ?: "" + packageInfo?.longVersionCode?.toString() ?: UNKNOWN_VALUE } else { @Suppress("DEPRECATION") - packageInfo?.versionCode?.toString() ?: "" + packageInfo?.versionCode?.toString() ?: UNKNOWN_VALUE } } fun getVersionName(packageName: String): String { val packageInfo = getPackageInfo(packageName) - return packageInfo?.versionName?.toString() ?: "" + return packageInfo?.versionName?.toString() ?: UNKNOWN_VALUE } /** @@ -194,7 +204,10 @@ class AppLoungePackageManager @Inject constructor( ) session.commit(servicePendingIntent.intentSender) } catch (e: Exception) { - Timber.e("Initiating Install Failed for $packageName exception: ${e.localizedMessage}", e) + Timber.e( + "Initiating Install Failed for $packageName exception: ${e.localizedMessage}", + e + ) val pendingIntent = PendingIntent.getBroadcast( context, sessionId, 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 68807788a25cd78c8efb7218b58e2f978813d574..ee181a81a655bce1ace25a6cb5f385021d8879ca 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 @@ -138,7 +138,7 @@ class UpdatesWorker @AssistedInject constructor( } if (resultStatus != ResultStatus.OK) { - manageRetry() + manageRetry(resultStatus.toString()) } else { /* * Show notification only if enabled. @@ -162,7 +162,7 @@ class UpdatesWorker @AssistedInject constructor( } } - private suspend fun manageRetry() { + private suspend fun manageRetry(extraMessage: String) { retryCount++ if (retryCount == 1) { EventBus.invokeEvent(AppEvent.UpdateEvent(ResultSupreme.WorkError(ResultStatus.RETRY))) @@ -172,7 +172,7 @@ class UpdatesWorker @AssistedInject constructor( delay(DELAY_FOR_RETRY) checkForUpdates() } else { - val message = "Update is aborted after trying for $MAX_RETRY_COUNT times!" + val message = "Update is aborted after trying for $MAX_RETRY_COUNT times! message: $extraMessage" Timber.e(message) EventBus.invokeEvent(AppEvent.UpdateEvent(ResultSupreme.WorkError(ResultStatus.UNKNOWN))) } 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 3ccd311a6533b54cae843c8f6b7fc61dcd6214aa..46dc5a2deac6845da9a0cc93a7443d1d08419651 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 @@ -32,6 +32,7 @@ import foundation.e.apps.data.application.data.Application 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.playstore.utils.GplayHttpRequestException import foundation.e.apps.data.preference.DataStoreManager import foundation.e.apps.install.notification.StorageNotificationManager import foundation.e.apps.install.updates.UpdatesNotifier @@ -155,23 +156,39 @@ class AppInstallProcessor @Inject constructor( fusedManagerRepository.addFusedDownloadPurchaseNeeded(fusedDownload) EventBus.invokeEvent(AppEvent.AppPurchaseEvent(fusedDownload)) return false - } catch (e: Exception) { - Timber.e( - "Updating download Urls failed for ${fusedDownload.packageName} exception: ${e.localizedMessage}", + } catch (e: GplayHttpRequestException) { + handleUpdateDownloadError( + fusedDownload, + "${fusedDownload.packageName} code: ${e.status} exception: ${e.localizedMessage}", e ) - EventBus.invokeEvent( - AppEvent.UpdateEvent( - ResultSupreme.WorkError( - ResultStatus.UNKNOWN, fusedDownload - ) - ) + return false + } catch (e: Exception) { + handleUpdateDownloadError( + fusedDownload, + "${fusedDownload.packageName} exception: ${e.localizedMessage}", + e ) return false } return true } + private suspend fun handleUpdateDownloadError( + fusedDownload: FusedDownload, + message: String, + e: Exception + ) { + Timber.e("Updating download Urls failed for $message", e) + EventBus.invokeEvent( + AppEvent.UpdateEvent( + ResultSupreme.WorkError( + ResultStatus.UNKNOWN, fusedDownload + ) + ) + ) + } + private suspend fun updateFusedDownloadWithAppDownloadLink( fusedDownload: FusedDownload ) { @@ -232,7 +249,9 @@ class AppInstallProcessor @Inject constructor( } private fun areFilesDownloadedButNotInstalled(fusedDownload: FusedDownload) = - fusedDownload.areFilesDownloaded() && (!fusedManagerRepository.isFusedDownloadInstalled(fusedDownload) || fusedDownload.status == Status.INSTALLING) + fusedDownload.areFilesDownloaded() && (!fusedManagerRepository.isFusedDownloadInstalled( + fusedDownload + ) || fusedDownload.status == Status.INSTALLING) private suspend fun checkUpdateWork( fusedDownload: FusedDownload?