From 13a88afa09dc21fbf70057bd49919dcb356e9a9b Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Tue, 19 Mar 2024 18:36:46 +0530 Subject: [PATCH] AppUtil: Move installed and updatable app logic to separate class Signed-off-by: Aayush Gupta --- .../java/com/aurora/extensions/AuthData.kt | 20 +++ .../aurora/store/data/work/UpdateWorker.kt | 153 +++--------------- .../java/com/aurora/store/util/AppUtil.kt | 109 +++++++++++++ .../store/viewmodel/all/InstalledViewModel.kt | 4 +- .../store/viewmodel/all/UpdatesViewModel.kt | 47 +----- 5 files changed, 160 insertions(+), 173 deletions(-) create mode 100644 app/src/main/java/com/aurora/extensions/AuthData.kt create mode 100644 app/src/main/java/com/aurora/store/util/AppUtil.kt diff --git a/app/src/main/java/com/aurora/extensions/AuthData.kt b/app/src/main/java/com/aurora/extensions/AuthData.kt new file mode 100644 index 000000000..192d900a6 --- /dev/null +++ b/app/src/main/java/com/aurora/extensions/AuthData.kt @@ -0,0 +1,20 @@ +package com.aurora.extensions + +import android.content.Context +import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.helpers.AuthValidator +import com.aurora.store.data.network.HttpClient +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +suspend fun AuthData.isValid(context: Context): Boolean { + return withContext(Dispatchers.IO) { + try { + AuthValidator(this@isValid) + .using(HttpClient.getPreferredClient(context)) + .isValid() + } catch (e: Exception) { + false + } + } +} diff --git a/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt b/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt index daf7a3076..3bec8f69f 100644 --- a/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt +++ b/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt @@ -3,7 +3,6 @@ package com.aurora.store.data.work import android.app.NotificationManager import android.content.Context import android.util.Log -import androidx.core.content.pm.PackageInfoCompat import androidx.hilt.work.HiltWorker import androidx.work.Constraints import androidx.work.CoroutineWorker @@ -14,22 +13,13 @@ import androidx.work.PeriodicWorkRequest import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerParameters -import com.aurora.Constants import com.aurora.extensions.isIgnoringBatteryOptimizations import com.aurora.extensions.isMAndAbove -import com.aurora.gplayapi.data.models.App -import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.helpers.AppDetailsHelper -import com.aurora.gplayapi.helpers.AuthValidator -import com.aurora.store.BuildConfig -import com.aurora.store.data.model.SelfUpdate -import com.aurora.store.data.network.HttpClient +import com.aurora.extensions.isValid import com.aurora.store.data.providers.AuthProvider -import com.aurora.store.data.providers.BlacklistProvider -import com.aurora.store.util.CertUtil +import com.aurora.store.util.AppUtil import com.aurora.store.util.DownloadWorkerUtil import com.aurora.store.util.NotificationUtil -import com.aurora.store.util.PackageUtil import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL @@ -53,7 +43,6 @@ class UpdateWorker @AssistedInject constructor( companion object { private const val TAG = "UpdateWorker" private const val UPDATE_WORKER = "UPDATE_WORKER" - private const val RELEASE = "release" fun cancelAutomatedCheck(context: Context) { Log.i(TAG, "Cancelling periodic app updates!") @@ -71,42 +60,6 @@ class UpdateWorker @AssistedInject constructor( WorkManager.getInstance(context).updateWork(buildUpdateWork(context)) } - suspend fun getSelfUpdate(context: Context, gson: Gson): App? { - return withContext(Dispatchers.IO) { - @Suppress("KotlinConstantConditions") // False-positive for build type always not being release - if (BuildConfig.BUILD_TYPE != RELEASE) { - Log.i(TAG, "Self-updates are not available for this build!") - return@withContext null - } - - try { - val response = HttpClient.getPreferredClient(context) - .get(Constants.UPDATE_URL, mapOf()) - val selfUpdate = - gson.fromJson(String(response.responseBytes), SelfUpdate::class.java) - - if (selfUpdate.versionCode > BuildConfig.VERSION_CODE) { - if (CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID)) { - if (selfUpdate.fdroidBuild.isNotEmpty()) { - return@withContext SelfUpdate.toApp(selfUpdate, context) - } - } else if (selfUpdate.auroraBuild.isNotEmpty()) { - return@withContext SelfUpdate.toApp(selfUpdate, context) - } else { - Log.e(TAG, "Update file is missing!") - return@withContext null - } - } - } catch (exception: Exception) { - Log.e(TAG, "Failed to check self-updates", exception) - return@withContext null - } - - Log.i(TAG, "No self-updates found!") - return@withContext null - } - } - private fun buildUpdateWork(context: Context): PeriodicWorkRequest { val updateCheckInterval = Preferences.getInteger( context, @@ -129,105 +82,45 @@ class UpdateWorker @AssistedInject constructor( } } - private val updatesList = mutableListOf() private val notificationID = 100 private val workerID = 101 - private val gapps: MutableSet = hashSetOf( - "com.chrome.beta", - "com.chrome.canary", - "com.chrome.dev", - "com.android.chrome", - "com.niksoftware.snapseed", - "com.google.toontastic" - ) - - private val authData = AuthProvider.with(appContext) - .getAuthData() - private val blackList = BlacklistProvider.with(appContext) - .getBlackList() - - override suspend fun getForegroundInfo(): ForegroundInfo { - return ForegroundInfo(workerID, NotificationUtil.getOngoingUpdateNotification(appContext)) - } override suspend fun doWork(): Result { + Log.i(TAG, "Checking for app updates") + val autoUpdatesMode = Preferences.getInteger(appContext, PREFERENCE_UPDATES_AUTO, 3) - val notifyManager = appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notifyManager = + appContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Exit if auto-updates is turned off in settings if (autoUpdatesMode == 0) { - Log.i(TAG,"Auto-updates is disabled, bailing out!") + Log.i(TAG, "Auto-updates is disabled, bailing out!") return Result.failure() } - // Check for Aurora Store updates first - if (!CertUtil.isFDroidApp(appContext, BuildConfig.APPLICATION_ID)) { - getSelfUpdate(appContext, gson)?.let { updatesList.add(it) } - } - withContext(Dispatchers.IO) { - - if (!isValid(authData)) { - Log.i(TAG,"AuthData is not valid, retrying later!") + if (!AuthProvider.with(appContext).getAuthData().isValid(appContext)) { + Log.i(TAG, "AuthData is not valid, retrying later!") return@withContext Result.retry() } - Log.i(TAG,"Checking for app updates") - - val appDetailsHelper = AppDetailsHelper(authData) - .using(HttpClient.getPreferredClient(appContext)) - - val isGoogleFilterEnabled = Preferences.getBoolean( - appContext, - Preferences.PREFERENCE_FILTER_GOOGLE - ) - val packageInfoMap = PackageUtil.getPackageInfoMap(appContext) - - packageInfoMap.keys.let { packages -> - /*Filter black list*/ - var filtersPackages = packages.filter { !blackList.contains(it) } - - /*Filter google apps*/ - if (isGoogleFilterEnabled) { - filtersPackages = filtersPackages - .filter { !it.startsWith("com.google") } - .filter { !gapps.contains(it) } - } - - val appList = appDetailsHelper.getAppByPackageName(filtersPackages) - .filter { it.displayName.isNotEmpty() } - - val appUpdatesList = appList.filter { - val packageInfo = packageInfoMap[it.packageName] - if (packageInfo != null) { - it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) - } else { - false - } - }.filter { app -> - app.certificateSetList.any { - it.certificateSet in CertUtil.getEncodedCertificateHashes( - appContext, - app.packageName - ) - } - } - updatesList.addAll(appUpdatesList) + try { + val updatesList = AppUtil.getUpdatableApps(appContext, gson, true) if (updatesList.isNotEmpty()) { if (autoUpdatesMode == 1) { - Log.i(TAG,"Found updates, notifying!") + Log.i(TAG, "Found updates, notifying!") notifyManager.notify( notificationID, NotificationUtil.getUpdateNotification(appContext, updatesList) ) } else { if (appContext.isIgnoringBatteryOptimizations()) { - Log.i(TAG,"Found updates, updating!") + Log.i(TAG, "Found updates, updating!") updatesList.forEach { downloadWorkerUtil.enqueueApp(it) } } else { // Fallback to notification if battery optimizations are enabled - Log.i(TAG,"Found updates, but battery optimizations are enabled!") + Log.i(TAG, "Found updates, but battery optimizations are enabled!") notifyManager.notify( notificationID, NotificationUtil.getUpdateNotification(appContext, updatesList) @@ -235,23 +128,19 @@ class UpdateWorker @AssistedInject constructor( appContext.save(PREFERENCE_UPDATES_AUTO, 1) } } - return@withContext Result.success() + } else { + Log.i(TAG, "No updates found!") } - - Log.i(TAG,"No updates found!") return@withContext Result.success() + } catch (exception: Exception) { + Log.e(TAG, "Failed to fetch updates", exception) + return@withContext Result.failure() } } return Result.success() } - private fun isValid(authData: AuthData): Boolean { - return try { - AuthValidator(authData) - .using(HttpClient.getPreferredClient(appContext)) - .isValid() - } catch (e: Exception) { - false - } + override suspend fun getForegroundInfo(): ForegroundInfo { + return ForegroundInfo(workerID, NotificationUtil.getOngoingUpdateNotification(appContext)) } } diff --git a/app/src/main/java/com/aurora/store/util/AppUtil.kt b/app/src/main/java/com/aurora/store/util/AppUtil.kt new file mode 100644 index 000000000..63fc251e7 --- /dev/null +++ b/app/src/main/java/com/aurora/store/util/AppUtil.kt @@ -0,0 +1,109 @@ +package com.aurora.store.util + +import android.content.Context +import android.content.pm.PackageInfo +import android.util.Log +import androidx.core.content.pm.PackageInfoCompat +import com.aurora.Constants +import com.aurora.gplayapi.data.models.App +import com.aurora.gplayapi.helpers.AppDetailsHelper +import com.aurora.store.BuildConfig +import com.aurora.store.data.model.SelfUpdate +import com.aurora.store.data.network.HttpClient +import com.aurora.store.data.providers.AuthProvider +import com.aurora.store.data.providers.BlacklistProvider +import com.google.gson.Gson +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +object AppUtil { + + private const val TAG = "AppUtil" + private const val RELEASE = "release" + + suspend fun getUpdatableApps(context: Context, gson: Gson, verifyCert: Boolean): List { + val packageInfoMap = PackageUtil.getPackageInfoMap(context) + val appUpdatesList = getFilteredInstalledApps(context, packageInfoMap).filter { + val packageInfo = packageInfoMap[it.packageName] + if (packageInfo != null) { + it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) + } else { + false + } + }.toMutableList() + + val verifiedUpdatesList = if (verifyCert) { + appUpdatesList.filter { app -> + app.certificateSetList.any { + it.certificateSet in CertUtil.getEncodedCertificateHashes( + context, app.packageName + ) + } + }.toMutableList() + } else { + appUpdatesList + } + + if (!CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID)) { + getSelfUpdate(context, gson)?.let { verifiedUpdatesList.add(it) } + } + + return verifiedUpdatesList + } + + suspend fun getFilteredInstalledApps( + context: Context, + packageInfoMap: MutableMap? = null + ): List { + return withContext(Dispatchers.IO) { + val authData = AuthProvider.with(context).getAuthData() + val blackList = BlacklistProvider.with(context).getBlackList() + val appDetailsHelper = + AppDetailsHelper(authData).using(HttpClient.getPreferredClient(context)) + + (packageInfoMap ?: PackageUtil.getPackageInfoMap(context)).keys.let { packages -> + val filtersPackages = packages.filter { !blackList.contains(it) } + + appDetailsHelper.getAppByPackageName(filtersPackages) + .filter { it.displayName.isNotEmpty() } + .map { it.isInstalled = true; it } + } + } + } + + private suspend fun getSelfUpdate(context: Context, gson: Gson): App? { + return withContext(Dispatchers.IO) { + @Suppress("KotlinConstantConditions") // False-positive for build type always not being release + if (BuildConfig.BUILD_TYPE != RELEASE) { + Log.i(TAG, "Self-updates are not available for this build!") + return@withContext null + } + + try { + val response = + HttpClient.getPreferredClient(context).get(Constants.UPDATE_URL, mapOf()) + val selfUpdate = + gson.fromJson(String(response.responseBytes), SelfUpdate::class.java) + + if (selfUpdate.versionCode > BuildConfig.VERSION_CODE) { + if (CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID)) { + if (selfUpdate.fdroidBuild.isNotEmpty()) { + return@withContext SelfUpdate.toApp(selfUpdate, context) + } + } else if (selfUpdate.auroraBuild.isNotEmpty()) { + return@withContext SelfUpdate.toApp(selfUpdate, context) + } else { + Log.e(TAG, "Update file is missing!") + return@withContext null + } + } + } catch (exception: Exception) { + Log.e(TAG, "Failed to check self-updates", exception) + return@withContext null + } + + Log.i(TAG, "No self-updates found!") + return@withContext null + } + } +} diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt index 9a1d059ae..dacfc7801 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/all/InstalledViewModel.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.viewModelScope import com.aurora.extensions.flushAndAdd import com.aurora.store.data.RequestState import com.aurora.store.data.event.BusEvent +import com.aurora.store.util.AppUtil import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.greenrobot.eventbus.EventBus @@ -35,14 +36,13 @@ class InstalledViewModel(application: Application) : BaseAppsViewModel(applicati init { EventBus.getDefault().register(this) - requestState = RequestState.Init } override fun observe() { viewModelScope.launch(Dispatchers.IO) { requestState = try { - appList.flushAndAdd(getFilteredApps()) + appList = AppUtil.getFilteredInstalledApps(getApplication()).toMutableList() liveData.postValue(appList.sortedBy { it.displayName.lowercase(Locale.getDefault()) }) RequestState.Complete } catch (e: Exception) { 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 06357a474..fab5f17b9 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 @@ -21,12 +21,9 @@ package com.aurora.store.viewmodel.all import android.app.Application import android.util.Log -import androidx.core.content.pm.PackageInfoCompat import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.App -import com.aurora.store.BuildConfig -import com.aurora.store.data.work.UpdateWorker -import com.aurora.store.util.CertUtil +import com.aurora.store.util.AppUtil import com.aurora.store.util.DownloadWorkerUtil import com.aurora.store.util.Preferences import dagger.hilt.android.lifecycle.HiltViewModel @@ -54,42 +51,14 @@ class UpdatesViewModel @Inject constructor( override fun observe() { viewModelScope.launch(Dispatchers.IO) { - val isExtendedUpdateEnabled = Preferences.getBoolean( - getApplication(), - Preferences.PREFERENCE_UPDATES_EXTENDED - ) - try { - 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 -> - val nApps = apps.toMutableList() - - if (!CertUtil.isFDroidApp(getApplication(), BuildConfig.APPLICATION_ID)) { - UpdateWorker.getSelfUpdate(getApplication(), gson)?.let { nApps.add(it) } - } - - if (!isExtendedUpdateEnabled) { - nApps.addAll( - apps.filter { app -> - app.certificateSetList.any { - it.certificateSet in CertUtil.getEncodedCertificateHashes( - getApplication(), - app.packageName - ) - } - } - ) - _updates.emit(nApps) - } else { - _updates.emit(nApps) - } - } + val isExtendedUpdateEnabled = Preferences.getBoolean( + getApplication(), Preferences.PREFERENCE_UPDATES_EXTENDED + ) + val updates = + AppUtil.getUpdatableApps(getApplication(), gson, !isExtendedUpdateEnabled) + .sortedBy { it.displayName.lowercase(Locale.getDefault()) } + _updates.emit(updates) } catch (exception: Exception) { Log.d(TAG, "Failed to get updates", exception) }