Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement recurring tasks using AndroidX Work #4881

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4544011
Implement talk page cleanup using a worker
Isira-Seneviratne Aug 20, 2024
0f5e017
Convert remaining tasks to workers
Isira-Seneviratne Aug 20, 2024
552d2b5
Use Constraints constructor directly
Isira-Seneviratne Aug 20, 2024
ac984f9
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Aug 20, 2024
7e19557
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Aug 21, 2024
e2f6f7f
Merge branch 'refs/heads/main' into Recurring-tasks-WorkManager
Isira-Seneviratne Aug 22, 2024
8f83ddb
Rm unused constant
Isira-Seneviratne Aug 22, 2024
8e37884
Revert DAO changes for now
Isira-Seneviratne Aug 22, 2024
64c113f
Restore comment
Isira-Seneviratne Aug 22, 2024
3e610df
Merge branch 'refs/heads/main' into Recurring-tasks-WorkManager
Isira-Seneviratne Aug 22, 2024
ebedafa
Merge branch 'refs/heads/main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 8, 2024
029c873
Avoid running recurring tasks when battery is low
Isira-Seneviratne Sep 8, 2024
8e919b8
Merge branch 'refs/heads/main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 9, 2024
8d5d33a
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 10, 2024
e2fcaf6
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 14, 2024
bb1e38e
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 19, 2024
de11cd9
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 20, 2024
4d1443c
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Sep 29, 2024
b117289
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Oct 5, 2024
0bdd535
Merge branch 'refs/heads/main' into Recurring-tasks-WorkManager
Isira-Seneviratne Oct 13, 2024
7dbfa5d
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Oct 18, 2024
2a565ed
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Oct 20, 2024
aed02eb
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Nov 1, 2024
77c1652
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Nov 19, 2024
d719f0c
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Dec 5, 2024
4e02273
Merge branch 'main' into Recurring-tasks-WorkManager
Isira-Seneviratne Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/main/java/org/wikipedia/activity/BaseActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ abstract class BaseActivity : AppCompatActivity(), ConnectionStateMonitor.Callba
}

// Conditionally execute all recurring tasks
RecurringTasksExecutor().run()
RecurringTasksExecutor.schedule()
if (Prefs.isReadingListsFirstTimeSync && AccountUtil.isLoggedIn) {
Prefs.isReadingListsFirstTimeSync = false
Prefs.isReadingListSyncEnabled = true
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.wikipedia.alphaupdater

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.Request
import okio.IOException
import org.wikipedia.dataclient.okhttp.OkHttpConnectionFactory.client
import org.wikipedia.notifications.NotificationCategory
import org.wikipedia.settings.PrefsIoUtil

class AlphaUpdateWorker(
appContext: Context,
params: WorkerParameters
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
val hashString = withContext(Dispatchers.IO) {
val request = Request.Builder().url(ALPHA_BUILD_DATA_URL).build()
try {
client.newCall(request).execute().body!!.use { it.string() }
} catch (e: IOException) {
// It's ok, we can do nothing.
null
}
}
if (hashString == null) {
return Result.failure()
}
if (PrefsIoUtil.getString(PREFERENCE_KEY_ALPHA_COMMIT, "") != hashString) {
showNotification()
}
PrefsIoUtil.setString(PREFERENCE_KEY_ALPHA_COMMIT, hashString)
return Result.success()
}

private fun showNotification() {
if (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
return
}
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(ALPHA_BUILD_APK_URL))
val pendingIntent = PendingIntentCompat.getActivity(applicationContext, 0, intent, 0, false)

val notificationManagerCompat = NotificationManagerCompat.from(applicationContext)
val notificationCategory = NotificationCategory.ALPHA_BUILD_CHECKER

val notificationBuilder = NotificationCompat.Builder(applicationContext, notificationCategory.id)
.setContentTitle(applicationContext.getString(notificationCategory.title))
.setContentText(applicationContext.getString(notificationCategory.description))
.setContentIntent(pendingIntent)
.setAutoCancel(true)
notificationBuilder.setSmallIcon(notificationCategory.iconResId)
notificationManagerCompat.notify(1, notificationBuilder.build())
}

companion object {
private const val PREFERENCE_KEY_ALPHA_COMMIT = "alpha_last_checked_commit"
private const val ALPHA_BUILD_APK_URL = "https://github.com/wikimedia/apps-android-wikipedia/releases/download/latest/app-alpha-universal-release.apk"
private const val ALPHA_BUILD_DATA_URL = "https://github.com/wikimedia/apps-android-wikipedia/releases/download/latest/rev-hash.txt"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,8 @@ class PollNotificationWorker(

companion object {
fun schedulePollNotificationJob(context: Context) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val workRequest = OneTimeWorkRequestBuilder<PollNotificationWorker>()
.setConstraints(constraints)
.setConstraints(Constraints(NetworkType.CONNECTED))
.build()
WorkManager.getInstance(context).enqueue(workRequest)
}
Expand Down
21 changes: 0 additions & 21 deletions app/src/main/java/org/wikipedia/recurring/DailyEventTask.kt

This file was deleted.

19 changes: 19 additions & 0 deletions app/src/main/java/org/wikipedia/recurring/DailyEventWorker.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.wikipedia.recurring

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import org.wikipedia.WikipediaApp
import org.wikipedia.analytics.eventplatform.DailyStatsEvent
import org.wikipedia.analytics.eventplatform.EventPlatformClient

class DailyEventWorker(
appContext: Context,
params: WorkerParameters
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
DailyStatsEvent.log(WikipediaApp.instance)
EventPlatformClient.refreshStreamConfigs()
return Result.success()
}
}
45 changes: 0 additions & 45 deletions app/src/main/java/org/wikipedia/recurring/RecurringTask.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,58 @@
package org.wikipedia.recurring

import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import org.wikipedia.R
import org.wikipedia.WikipediaApp
import org.wikipedia.alphaupdater.AlphaUpdateChecker
import org.wikipedia.settings.RemoteConfigRefreshTask
import org.wikipedia.alphaupdater.AlphaUpdateWorker
import org.wikipedia.settings.PrefsIoUtil
import org.wikipedia.settings.RemoteConfigRefreshWorker
import org.wikipedia.util.ReleaseUtil
import org.wikipedia.util.log.L

class RecurringTasksExecutor() {
fun run() {
val app = WikipediaApp.instance
MainScope().launch(CoroutineExceptionHandler { _, throwable ->
L.e(throwable)
}) {
RemoteConfigRefreshTask().runIfNecessary()
DailyEventTask(app).runIfNecessary()
TalkOfflineCleanupTask(app).runIfNecessary()
if (ReleaseUtil.isAlphaRelease) {
AlphaUpdateChecker(app).runIfNecessary()
}
import java.util.concurrent.TimeUnit

object RecurringTasksExecutor {
fun schedule() {
// Clean up now-unused task time values, as WorkManager handles scheduling
PrefsIoUtil.remove("alpha-update-checker")
PrefsIoUtil.remove("remote-config-refresher")
PrefsIoUtil.remove(R.string.preference_key_talk_offline_cleanup_task_name)
PrefsIoUtil.remove(R.string.preference_key_daily_event_time_task_name)

val networkConstraints = Constraints(
requiredNetworkType = NetworkType.CONNECTED,
requiresBatteryNotLow = true
)

val remoteConfigRefreshRequest = PeriodicWorkRequestBuilder<RemoteConfigRefreshWorker>(1, TimeUnit.DAYS)
.setConstraints(networkConstraints)
.build()

val dailyEventRequest = PeriodicWorkRequestBuilder<DailyEventWorker>(1, TimeUnit.DAYS)
.setConstraints(networkConstraints)
.build()

val offlineCleanupRequest = PeriodicWorkRequestBuilder<TalkOfflineCleanupWorker>(7, TimeUnit.DAYS)
.setConstraints(Constraints(requiresBatteryNotLow = true))
.build()

val tasks = mutableMapOf(
"REMOTE_CONFIG" to remoteConfigRefreshRequest,
"DAILY_EVENT" to dailyEventRequest,
"OFFLINE_CLEANUP" to offlineCleanupRequest
)

if (ReleaseUtil.isAlphaRelease) {
tasks["ALPHA_UPDATE"] = PeriodicWorkRequestBuilder<AlphaUpdateWorker>(1, TimeUnit.DAYS)
.setConstraints(networkConstraints)
.build()
}

tasks.forEach { (taskName, task) ->
WorkManager.getInstance(WikipediaApp.instance)
.enqueueUniquePeriodicWork(taskName, ExistingPeriodicWorkPolicy.KEEP, task)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
package org.wikipedia.recurring

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.wikipedia.R
import org.wikipedia.database.AppDatabase
import java.io.File
import java.util.Date
import java.util.concurrent.TimeUnit
import java.time.Instant
import java.time.temporal.ChronoUnit

class TalkOfflineCleanupTask(context: Context) : RecurringTask() {
override val name = context.getString(R.string.preference_key_talk_offline_cleanup_task_name)

override fun shouldRun(lastRun: Date): Boolean {
return millisSinceLastRun(lastRun) > TimeUnit.DAYS.toMillis(CLEANUP_MAX_AGE_DAYS)
}

override suspend fun run(lastRun: Date) {
class TalkOfflineCleanupWorker(
appContext: Context,
params: WorkerParameters
) : CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
withContext(Dispatchers.IO) {
AppDatabase.instance.offlineObjectDao()
.searchForOfflineObjects(CLEANUP_URL_SEARCH_KEY)
.filter {
(absoluteTime - File(it.path + ".0").lastModified()) > TimeUnit.DAYS.toMillis(CLEANUP_MAX_AGE_DAYS)
val lastModified = Instant.ofEpochMilli(File("${it.path}.0").lastModified())
ChronoUnit.DAYS.between(lastModified, Instant.now()) > CLEANUP_MAX_AGE_DAYS
}.forEach {
AppDatabase.instance.offlineObjectDao().deleteOfflineObject(it)
AppDatabase.instance.offlineObjectDao().deleteFilesForObject(it)
}
}
return Result.success()
}

companion object {
Expand Down
Loading
Loading