AppUtil: Move installed and updatable app logic to separate class
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
20
app/src/main/java/com/aurora/extensions/AuthData.kt
Normal file
20
app/src/main/java/com/aurora/extensions/AuthData.kt
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<App>()
|
||||
private val notificationID = 100
|
||||
private val workerID = 101
|
||||
private val gapps: MutableSet<String> = 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))
|
||||
}
|
||||
}
|
||||
|
||||
109
app/src/main/java/com/aurora/store/util/AppUtil.kt
Normal file
109
app/src/main/java/com/aurora/store/util/AppUtil.kt
Normal file
@@ -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<App> {
|
||||
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<String, PackageInfo>? = null
|
||||
): List<App> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user