UpdateWorker: Merge self-updates logic for non-fdroid builds

Treat Aurora Store as a normal app for non-fdroid builds and check
for its updates based on auto-update preference.

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-03-01 20:21:37 +05:30
parent 3b9efeed11
commit 2f5d39106a
38 changed files with 121 additions and 257 deletions

View File

@@ -43,6 +43,7 @@ object Constants {
const val NOTIFICATION_CHANNEL_UPDATES = "NOTIFICATION_CHANNEL_UPDATES"
const val NOTIFICATION_CHANNEL_UPDATER_SERVICE = "NOTIFICATION_CHANNEL_UPDATER_SERVICE"
const val GITLAB_URL = "https://gitlab.com/AuroraOSS/AuroraStore"
const val URL_DISPENSER = "https://auroraoss.com/api/auth"
const val PLAY_QUERY_URL = "https://play.google.com/store/search?q="

View File

@@ -34,6 +34,7 @@ data class SelfUpdate(
@SerializedName("version_code") var versionCode: Int = 0,
@SerializedName("aurora_build") var auroraBuild: String = String(),
@SerializedName("fdroid_build") var fdroidBuild: String = String(),
@SerializedName("updated_on") var updatedOn: String = String(),
val changelog: String = String(),
val size: Long = 0L
) {
@@ -56,7 +57,9 @@ data class SelfUpdate(
versionName = selfUpdate.versionName,
changes = selfUpdate.changelog,
size = selfUpdate.size,
updatedOn = selfUpdate.updatedOn,
displayName = context.getString(R.string.app_name),
developerName = "Rahul Kumar Patel",
iconArtwork = Artwork(url = "$BASE_URL/$icon"),
fileList = mutableListOf(
File(
@@ -64,7 +67,9 @@ data class SelfUpdate(
url = downloadURL,
size = selfUpdate.size
)
)
),
isFree = true,
isInstalled = true
)
}
}

View File

@@ -1,115 +0,0 @@
package com.aurora.store.data.work
import android.content.Context
import android.util.Log
import androidx.hilt.work.HiltWorker
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy.KEEP
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.aurora.Constants
import com.aurora.extensions.isMAndAbove
import com.aurora.store.BuildConfig
import com.aurora.store.data.model.SelfUpdate
import com.aurora.store.data.network.HttpClient
import com.aurora.store.util.CertUtil
import com.aurora.store.util.DownloadWorkerUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_SELF_UPDATE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import com.google.gson.Gson
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import java.util.concurrent.TimeUnit.HOURS
import java.util.concurrent.TimeUnit.MINUTES
@HiltWorker
class SelfUpdateWorker @AssistedInject constructor(
private val downloadWorkerUtil: DownloadWorkerUtil,
private val gson: Gson,
@Assisted private val appContext: Context,
@Assisted workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
companion object {
private const val TAG = "SelfUpdateWorker"
private const val SELF_UPDATE_WORKER = "SELF_UPDATE_WORKER"
fun cancelAutomatedCheck(context: Context) {
Log.i(TAG, "Cancelling periodic self-updates!")
WorkManager.getInstance(context).cancelUniqueWork(SELF_UPDATE_WORKER)
}
fun scheduleAutomatedCheck(context: Context) {
Log.i(TAG,"Scheduling periodic self-updates!")
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(SELF_UPDATE_WORKER, KEEP, buildUpdateWork(context))
}
fun updateAutomatedCheck(context: Context) {
Log.i(TAG,"Updating periodic self-updates!")
WorkManager.getInstance(context).updateWork(buildUpdateWork(context))
}
private fun buildUpdateWork(context: Context): PeriodicWorkRequest {
val updateCheckInterval = Preferences.getInteger(
context,
PREFERENCE_UPDATES_CHECK_INTERVAL,
3
).toLong()
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.setRequiresBatteryNotLow(true)
if (isMAndAbove()) constraints.setRequiresDeviceIdle(true)
return PeriodicWorkRequestBuilder<UpdateWorker>(
repeatInterval = updateCheckInterval,
repeatIntervalTimeUnit = HOURS,
flexTimeInterval = 30,
flexTimeIntervalUnit = MINUTES
).setConstraints(constraints.build()).build()
}
}
private val TAG = SelfUpdateWorker::class.java.simpleName
private val RELEASE = "release"
override suspend fun doWork(): Result {
@Suppress("KotlinConstantConditions") // False-positive for build type always not being release
if (BuildConfig.BUILD_TYPE != RELEASE && Preferences.getBoolean(appContext, PREFERENCE_SELF_UPDATE)) {
Log.e(TAG, "Either build type is not release or self-update check is disabled")
return Result.failure()
}
try {
val response = HttpClient.getPreferredClient(appContext)
.get(Constants.UPDATE_URL, mapOf())
val selfUpdate = gson.fromJson(String(response.responseBytes), SelfUpdate::class.java)
if (selfUpdate.versionCode > BuildConfig.VERSION_CODE) {
if (CertUtil.isFDroidApp(appContext, BuildConfig.APPLICATION_ID)) {
if (selfUpdate.fdroidBuild.isNotEmpty()) {
downloadWorkerUtil.enqueueApp(SelfUpdate.toApp(selfUpdate, appContext))
}
} else if (selfUpdate.auroraBuild.isNotEmpty()) {
downloadWorkerUtil.enqueueApp(SelfUpdate.toApp(selfUpdate, appContext))
} else {
Log.e(TAG, "Update file is missing!")
return Result.failure()
}
}
} catch (exception: Exception) {
Log.e(TAG, "Failed to check Aurora Store updates", exception)
return Result.failure()
}
Log.i(TAG, "No updates found")
return Result.success()
}
}

View File

@@ -2,6 +2,7 @@ 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
@@ -13,21 +14,25 @@ import androidx.work.PeriodicWorkRequest
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import com.aurora.Constants
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.store.data.providers.AuthProvider
import com.aurora.store.data.providers.BlacklistProvider
import com.aurora.store.util.CertUtil
import com.aurora.store.util.DownloadWorkerUtil
import com.aurora.store.util.Log
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
import com.google.gson.Gson
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers
@@ -37,30 +42,69 @@ import java.util.concurrent.TimeUnit.MINUTES
@HiltWorker
class UpdateWorker @AssistedInject constructor(
val downloadWorkerUtil: DownloadWorkerUtil,
private val downloadWorkerUtil: DownloadWorkerUtil,
private val gson: Gson,
@Assisted private val appContext: Context,
@Assisted workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
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("Cancelling periodic app updates!")
Log.i(TAG, "Cancelling periodic app updates!")
WorkManager.getInstance(context).cancelUniqueWork(UPDATE_WORKER)
}
fun scheduleAutomatedCheck(context: Context) {
Log.i("Scheduling periodic app updates!")
Log.i(TAG,"Scheduling periodic app updates!")
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(UPDATE_WORKER, KEEP, buildUpdateWork(context))
}
fun updateAutomatedCheck(context: Context) {
Log.i("Updating periodic app updates!")
Log.i(TAG,"Updating periodic app updates!")
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,
@@ -83,6 +127,7 @@ class UpdateWorker @AssistedInject constructor(
}
}
private val updatesList = mutableListOf<App>()
private val notificationID = 100
private val workerID = 101
private val gapps: MutableSet<String> = hashSetOf(
@@ -108,18 +153,23 @@ class UpdateWorker @AssistedInject constructor(
// Exit if auto-updates is turned off in settings
if (autoUpdatesMode == 0) {
Log.i("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("AuthData is not valid, retrying later!")
Log.i(TAG,"AuthData is not valid, retrying later!")
return@withContext Result.retry()
}
Log.i("Checking for app updates")
Log.i(TAG,"Checking for app updates")
val appDetailsHelper = AppDetailsHelper(authData)
.using(HttpClient.getPreferredClient(appContext))
@@ -144,7 +194,7 @@ class UpdateWorker @AssistedInject constructor(
val appList = appDetailsHelper.getAppByPackageName(filtersPackages)
.filter { it.displayName.isNotEmpty() }
val updatesList = appList.filter {
val appUpdatesList = appList.filter {
val packageInfo = packageInfoMap[it.packageName]
if (packageInfo != null) {
it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo)
@@ -159,10 +209,11 @@ class UpdateWorker @AssistedInject constructor(
)
}
}
updatesList.addAll(appUpdatesList)
if (updatesList.isNotEmpty()) {
if (autoUpdatesMode == 1) {
Log.i("Found updates, notifying!")
Log.i(TAG,"Found updates, notifying!")
val notifyManager =
appContext.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
@@ -171,13 +222,13 @@ class UpdateWorker @AssistedInject constructor(
NotificationUtil.getUpdateNotification(appContext, updatesList)
)
} else {
Log.i("Found updates, updating!")
Log.i(TAG,"Found updates, updating!")
updatesList.forEach { downloadWorkerUtil.enqueueApp(it) }
}
return@withContext Result.success()
}
Log.i("No updates found!")
Log.i(TAG,"No updates found!")
return@withContext Result.success()
}
}

View File

@@ -67,8 +67,6 @@ object Preferences {
const val PREFERENCE_UNIQUE_GROUP_IDS = "PREFERENCE_UNIQUE_GROUP_IDS"
const val PREFERENCE_SELF_UPDATE = "PREFERENCE_SELF_UPDATE"
private var prefs: SharedPreferences? = null
fun getPrefs(context: Context): SharedPreferences {

View File

@@ -25,6 +25,7 @@ import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.aurora.Constants
import com.aurora.Constants.GITLAB_URL
import com.aurora.extensions.browse
import com.aurora.store.MobileNavigationDirections
import com.aurora.store.R
@@ -51,8 +52,6 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
lateinit var downloadWorkerUtil: DownloadWorkerUtil
lateinit var downloadList: List<Download>
private val AURORA_STORE_URL = "https://gitlab.com/AuroraOSS/AuroraStore"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -116,7 +115,7 @@ class DownloadFragment : BaseFragment(R.layout.fragment_download) {
.download(it)
.click { _ ->
if (it.packageName == Constants.APP_ID) {
requireContext().browse(AURORA_STORE_URL)
requireContext().browse(GITLAB_URL)
} else {
openDetailsFragment(it.packageName)
}

View File

@@ -28,12 +28,9 @@ import androidx.navigation.fragment.findNavController
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.aurora.extensions.isSAndAbove
import com.aurora.store.BuildConfig
import com.aurora.store.R
import com.aurora.store.data.work.SelfUpdateWorker
import com.aurora.store.data.work.UpdateWorker
import com.aurora.store.databinding.FragmentOnboardingBinding
import com.aurora.store.util.CertUtil
import com.aurora.store.util.PathUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
@@ -52,7 +49,6 @@ import com.aurora.store.util.Preferences.PREFERENCE_INTRO
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_ENABLED
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_INFO
import com.aurora.store.util.Preferences.PREFERENCE_PROXY_URL
import com.aurora.store.util.Preferences.PREFERENCE_SELF_UPDATE
import com.aurora.store.util.Preferences.PREFERENCE_SIMILAR
import com.aurora.store.util.Preferences.PREFERENCE_THEME_ACCENT
import com.aurora.store.util.Preferences.PREFERENCE_THEME_TYPE
@@ -63,7 +59,6 @@ import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.aurora.store.util.save
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import kotlin.properties.Delegates
@AndroidEntryPoint
class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
@@ -71,7 +66,6 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
private var _binding: FragmentOnboardingBinding? = null
private val binding get() = _binding!!
private var shouldSelfUpdate by Delegates.notNull<Boolean>()
private var lastPosition = 0
internal class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
@@ -97,8 +91,6 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentOnboardingBinding.bind(view)
shouldSelfUpdate = CertUtil.isFDroidApp(requireContext(), BuildConfig.APPLICATION_ID)
val isDefaultPrefLoaded = Preferences.getBoolean(requireContext(), PREFERENCE_DEFAULT)
if (!isDefaultPrefLoaded) {
save(PREFERENCE_DEFAULT, true)
@@ -148,7 +140,6 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
binding.btnForward.setOnClickListener {
save(PREFERENCE_INTRO, true)
UpdateWorker.scheduleAutomatedCheck(requireContext())
if (shouldSelfUpdate) SelfUpdateWorker.scheduleAutomatedCheck(requireContext())
findNavController().navigate(
OnboardingFragmentDirections.actionOnboardingFragmentToSplashFragment()
)
@@ -196,6 +187,5 @@ class OnboardingFragment : Fragment(R.layout.fragment_onboarding) {
save(PREFERENCE_UPDATES_EXTENDED, false)
save(PREFERENCE_UPDATES_AUTO, 2)
save(PREFERENCE_UPDATES_CHECK_INTERVAL, 3)
save(PREFERENCE_SELF_UPDATE, shouldSelfUpdate)
}
}

View File

@@ -27,16 +27,10 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SeekBarPreference
import androidx.preference.SwitchPreferenceCompat
import com.aurora.store.BuildConfig
import com.aurora.store.R
import com.aurora.store.data.work.SelfUpdateWorker
import com.aurora.store.data.work.UpdateWorker
import com.aurora.store.util.CertUtil
import com.aurora.store.util.Preferences.PREFERENCE_SELF_UPDATE
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import com.aurora.store.util.save
import com.aurora.store.view.custom.preference.ListPreferenceMaterialDialogFragmentCompat
import dagger.hilt.android.AndroidEntryPoint
@@ -46,22 +40,6 @@ class UpdatesPreference : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preferences_updates, rootKey)
findPreference<SwitchPreferenceCompat>(PREFERENCE_SELF_UPDATE)?.let {
if (CertUtil.isFDroidApp(requireContext(), BuildConfig.APPLICATION_ID)) {
it.isVisible = false
}
it.setOnPreferenceChangeListener { _, newValue ->
if (newValue.toString().toBoolean()) {
SelfUpdateWorker.scheduleAutomatedCheck(requireContext())
} else {
SelfUpdateWorker.cancelAutomatedCheck(requireContext())
}
save(PREFERENCE_SELF_UPDATE, newValue.toString().toBoolean())
true
}
}
findPreference<ListPreference>(PREFERENCE_UPDATES_AUTO)
?.setOnPreferenceChangeListener { _, newValue ->
when (newValue.toString().toInt()) {
@@ -74,7 +52,6 @@ class UpdatesPreference : PreferenceFragmentCompat() {
findPreference<SeekBarPreference>(PREFERENCE_UPDATES_CHECK_INTERVAL)
?.setOnPreferenceChangeListener { _, _ ->
UpdateWorker.updateAutomatedCheck(requireContext())
SelfUpdateWorker.updateAutomatedCheck(requireContext())
true
}
}

View File

@@ -30,12 +30,13 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.aurora.Constants
import com.aurora.extensions.browse
import com.aurora.extensions.isRAndAbove
import com.aurora.extensions.toast
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R
import com.aurora.store.data.event.BusEvent
import com.aurora.store.data.model.DownloadStatus
import com.aurora.store.data.room.download.Download
import com.aurora.store.databinding.FragmentUpdatesBinding
import com.aurora.store.util.PathUtil
@@ -178,7 +179,13 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) {
.id(app.packageName)
.app(app)
.download(download)
.click { _ -> openDetailsFragment(app.packageName, app) }
.click { _ ->
if (app.packageName == Constants.APP_ID) {
requireContext().browse(Constants.GITLAB_URL)
} else {
openDetailsFragment(app.packageName, app)
}
}
.longClick { _ ->
openAppMenuSheet(app)
false

View File

@@ -24,6 +24,8 @@ 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.DownloadWorkerUtil
import com.aurora.store.util.Preferences
@@ -66,8 +68,14 @@ class UpdatesViewModel @Inject constructor(
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) {
_updates.emit(
nApps.addAll(
apps.filter { app ->
app.certificateSetList.any {
it.certificateSet in CertUtil.getEncodedCertificateHashes(
@@ -77,8 +85,9 @@ class UpdatesViewModel @Inject constructor(
}
}
)
_updates.emit(nApps)
} else {
_updates.emit(apps)
_updates.emit(nApps)
}
}
} catch (exception: Exception) {