data: work: Add required doc-comments for all workers

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-07-09 12:36:58 +07:00
parent dc51987f08
commit b7f2eaa7e3
4 changed files with 104 additions and 0 deletions

View File

@@ -18,6 +18,9 @@ import java.util.concurrent.TimeUnit.MINUTES
import kotlin.time.DurationUnit
import kotlin.time.toDuration
/**
* A periodic worker to automatically clear the old downloads cache periodically.
*/
@HiltWorker
class CacheWorker @AssistedInject constructor(
@Assisted private val appContext: Context,
@@ -28,6 +31,11 @@ class CacheWorker @AssistedInject constructor(
private const val TAG = "CleanCacheWorker"
private const val CLEAN_CACHE_WORKER = "CLEAN_CACHE_WORKER"
/**
* Schedules the automated cache cleanup
* @param context Current [Context]
* @see [CacheWorker]
*/
fun scheduleAutomatedCacheCleanup(context: Context) {
val periodicWorkRequest = PeriodicWorkRequestBuilder<CacheWorker>(
repeatInterval = 1,
@@ -42,6 +50,9 @@ class CacheWorker @AssistedInject constructor(
}
}
/**
* Duration to cache files, defaults to 6 hours
*/
private val cacheDuration = 6.toDuration(DurationUnit.HOURS)
override suspend fun doWork(): Result {
@@ -73,6 +84,9 @@ class CacheWorker @AssistedInject constructor(
return Result.success()
}
/**
* Deletes the file if it's older than $[cacheDuration]
*/
private fun File.deleteIfOld() {
val elapsedTime = Calendar.getInstance().timeInMillis - this.lastModified()
if (elapsedTime.toDuration(DurationUnit.HOURS) > cacheDuration) {

View File

@@ -56,6 +56,11 @@ import javax.net.ssl.HttpsURLConnection
import kotlin.properties.Delegates
import com.aurora.gplayapi.data.models.File as GPlayFile
/**
* An expedited long-running worker to download and trigger installation for given apps.
*
* Avoid using this worker directly and prefer using [DownloadWorkerUtil] instead.
*/
@HiltWorker
class DownloadWorker @AssistedInject constructor(
private val downloadDao: DownloadDao,
@@ -202,6 +207,13 @@ class DownloadWorker @AssistedInject constructor(
}
}
/**
* Purchases the app to get the download URL of the required files
* @param packageName The packageName of the app
* @param versionCode Required version of the app
* @param offerType Offer type of the app (free/paid)
* @return A list of purchased files
*/
private fun purchase(packageName: String, versionCode: Int, offerType: Int): List<GPlayFile> {
// Android 9.0+ supports key rotation, so purchase with latest certificate's hash
return if (isPAndAbove() && download.isInstalled) {
@@ -235,6 +247,12 @@ class DownloadWorker @AssistedInject constructor(
return downloadList
}
/**
* Downloads the file from the given request.
* Failed downloads aren't removed and persists as long as [CacheWorker] doesn't cleans them.
* @param request A [Request] to download
* @return A [Result] indicating whether the file was downloaded or not.
*/
private suspend fun downloadFile(request: Request): Result {
return withContext(Dispatchers.IO) {
val algorithm = if (request.sha256.isBlank()) Algorithm.SHA1 else Algorithm.SHA256
@@ -278,6 +296,10 @@ class DownloadWorker @AssistedInject constructor(
}
}
/**
* Updates the progress data of the download in the local database and notifies user.
* @param downloadInfo An instance of [DownloadInfo]
*/
private suspend fun onProgress(downloadInfo: DownloadInfo) {
if (!isStopped && !download.isFinished) {
downloadedBytes += downloadInfo.bytesCopied
@@ -325,6 +347,11 @@ class DownloadWorker @AssistedInject constructor(
}
}
/**
* Notifies the user of the current status of the download.
* @param status Current [DownloadStatus]
* @param dID ID of the notification, defaults to hashCode of the download's packageName
*/
private suspend fun notifyStatus(status: DownloadStatus, dID: Int = -1) {
// Update status in database
download.downloadStatus = status
@@ -346,6 +373,13 @@ class DownloadWorker @AssistedInject constructor(
notificationManager.notify(notificationID, notification)
}
/**
* Validates whether given file has the expected SHA hash sum.
* @param file [File] to check
* @param expectedSha Expected SHA hash sum
* @param algorithm [Algorithm] of the SHA
* @return A boolean whether the given file has the expected SHA or not.
*/
@OptIn(ExperimentalStdlibApi::class)
private suspend fun validSha(file: File, expectedSha: String, algorithm: Algorithm): Boolean {
return withContext(Dispatchers.IO) {
@@ -362,6 +396,10 @@ class DownloadWorker @AssistedInject constructor(
}
}
/**
* Gets the current [Proxy] configuration.
* @return An instance of [Proxy] if configured by the user, null otherwise
*/
private fun getProxy(): Proxy? {
val proxyEnabled = Preferences.getBoolean(appContext, PREFERENCE_PROXY_ENABLED)
val proxyInfoString = Preferences.getString(appContext, PREFERENCE_PROXY_INFO)

View File

@@ -35,6 +35,11 @@ import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit.HOURS
import java.util.concurrent.TimeUnit.MINUTES
/**
* A periodic worker to check for updates for installed apps based on
* filters and the auto-updates mode selected by the user. The repeat interval
* is configurable by the user, defaulting to 3 hours with a flex time of 30 minutes.
*/
@HiltWorker
class UpdateWorker @AssistedInject constructor(
private val downloadWorkerUtil: DownloadWorkerUtil,
@@ -48,17 +53,32 @@ class UpdateWorker @AssistedInject constructor(
private const val TAG = "UpdateWorker"
private const val UPDATE_WORKER = "UPDATE_WORKER"
/**
* Cancels the automated updates check
* @param context Current [Context]
* @see [UpdateWorker]
*/
fun cancelAutomatedCheck(context: Context) {
Log.i(TAG, "Cancelling periodic app updates!")
WorkManager.getInstance(context).cancelUniqueWork(UPDATE_WORKER)
}
/**
* Schedules the automated updates check
* @param context Current [Context]
* @see [UpdateWorker]
*/
fun scheduleAutomatedCheck(context: Context) {
Log.i(TAG,"Scheduling periodic app updates!")
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(UPDATE_WORKER, KEEP, buildUpdateWork(context))
}
/**
* Updates the automated updates check to reconsider the new user preferences
* @param context Current [Context]
* @see [UpdateWorker]
*/
fun updateAutomatedCheck(context: Context) {
Log.i(TAG,"Updating periodic app updates!")
WorkManager.getInstance(context).updateWork(buildUpdateWork(context))
@@ -165,6 +185,9 @@ class UpdateWorker @AssistedInject constructor(
return Result.success()
}
/**
* Checks if the given package can be auto-updated or not
*/
private fun canAutoUpdate(packageName: String): Boolean {
return when (AppInstaller.getCurrentInstaller(appContext)) {
Installer.SESSION -> isSAndAbove() && CertUtil.isAuroraStoreApp(appContext, packageName)

View File

@@ -23,6 +23,9 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/**
* Helper class to work with the [DownloadWorker].
*/
class DownloadWorkerUtil @Inject constructor(
@ApplicationContext private val context: Context,
private val downloadDao: DownloadDao,
@@ -44,6 +47,9 @@ class DownloadWorkerUtil @Inject constructor(
private val TAG = DownloadWorkerUtil::class.java.simpleName
/**
* Removes failed download from the queue and starts observing for newly enqueued apps.
*/
fun init() {
AuroraApp.scope.launch {
cancelFailedDownloads(downloadDao.downloads().firstOrNull() ?: emptyList())
@@ -72,10 +78,18 @@ class DownloadWorkerUtil @Inject constructor(
}
}
/**
* Enqueues an app for download & install
* @param app [App] to download
*/
suspend fun enqueueApp(app: App) {
downloadDao.insert(Download.fromApp(app))
}
/**
* Cancels the download for the given package
* @param packageName Name of the package to cancel download
*/
suspend fun cancelDownload(packageName: String) {
Log.i(TAG, "Cancelling download for $packageName")
WorkManager.getInstance(context).cancelAllWorkByTag("$PACKAGE_NAME:$packageName")
@@ -84,6 +98,11 @@ class DownloadWorkerUtil @Inject constructor(
?.let { downloadDao.updateStatus(packageName, DownloadStatus.CANCELLED) }
}
/**
* Clears the entry & downloaded files for the given package
* @param packageName Name of the package of the app
* @param versionCode Version of the package
*/
suspend fun clearDownload(packageName: String, versionCode: Int) {
Log.i(TAG, "Clearing downloads for $packageName ($versionCode)")
downloadDao.delete(packageName)
@@ -91,6 +110,9 @@ class DownloadWorkerUtil @Inject constructor(
.deleteRecursively()
}
/**
* Clears all the downloads and their downloaded files
*/
suspend fun clearAllDownloads() {
Log.i(TAG, "Clearing all downloads!")
downloadDao.deleteAll()
@@ -98,12 +120,19 @@ class DownloadWorkerUtil @Inject constructor(
PathUtil.getOldDownloadDirectories(context).forEach { it.deleteRecursively() }
}
/**
* Clears finished downloads and their downloaded files
*/
suspend fun clearFinishedDownloads() {
downloadsList.value.filter { it.isFinished }.forEach {
clearDownload(it.packageName, it.versionCode)
}
}
/**
* Cancels all the ongoing and queued downloads
* @param updatesOnly Whether to cancel only updates, defaults to false
*/
suspend fun cancelAll(updatesOnly: Boolean = false) {
// Cancel all enqueued downloads first to avoid triggering re-download
downloadsList.value.filter { it.downloadStatus == DownloadStatus.QUEUED }