First attempt
This commit is contained in:
parent
a9d04b7384
commit
e0c829d887
4 changed files with 51 additions and 31 deletions
|
|
@ -29,30 +29,29 @@ class SyncNotificationWorkManagerRequest(
|
||||||
private val workerDataConverter: WorkerDataConverter,
|
private val workerDataConverter: WorkerDataConverter,
|
||||||
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
|
private val buildVersionSdkIntProvider: BuildVersionSdkIntProvider,
|
||||||
) : WorkManagerRequest {
|
) : WorkManagerRequest {
|
||||||
override fun build(): Result<WorkRequest> {
|
override fun build(): List<Result<WorkRequest>> {
|
||||||
if (notificationEventRequests.isEmpty()) {
|
if (notificationEventRequests.isEmpty()) {
|
||||||
return Result.failure(InvalidParameterException("notificationEventRequests cannot be empty"))
|
return listOf(Result.failure(InvalidParameterException("notificationEventRequests cannot be empty")))
|
||||||
}
|
|
||||||
val data = workerDataConverter.serialize(notificationEventRequests).getOrElse {
|
|
||||||
return Result.failure(it)
|
|
||||||
}
|
}
|
||||||
Timber.d("Scheduling ${notificationEventRequests.size} notification requests with WorkManager for $sessionId")
|
Timber.d("Scheduling ${notificationEventRequests.size} notification requests with WorkManager for $sessionId")
|
||||||
return Result.success(
|
return workerDataConverter.serialize(notificationEventRequests).map {
|
||||||
OneTimeWorkRequestBuilder<FetchNotificationsWorker>()
|
it.map { data ->
|
||||||
.setInputData(data)
|
OneTimeWorkRequestBuilder<FetchNotificationsWorker>()
|
||||||
.apply {
|
.setInputData(data)
|
||||||
// Expedited workers aren't needed on Android 12 or lower:
|
.apply {
|
||||||
// They force displaying a foreground sync notification for no good reason, since they sync almost immediately anyway
|
// Expedited workers aren't needed on Android 12 or lower:
|
||||||
// See https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started/define-work#backwards-compat
|
// They force displaying a foreground sync notification for no good reason, since they sync almost immediately anyway
|
||||||
if (buildVersionSdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
|
// See https://developer.android.com/develop/background-work/background-tasks/persistent/getting-started/define-work#backwards-compat
|
||||||
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
if (buildVersionSdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
|
||||||
|
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.setTraceTag(workManagerTag(sessionId, WorkManagerRequestType.NOTIFICATION_SYNC))
|
||||||
.setTraceTag(workManagerTag(sessionId, WorkManagerRequestType.NOTIFICATION_SYNC))
|
// TODO investigate using this instead of the resolver queue
|
||||||
// TODO investigate using this instead of the resolver queue
|
// .setInputMerger()
|
||||||
// .setInputMerger()
|
.build()
|
||||||
.build()
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import androidx.work.Data
|
||||||
import androidx.work.workDataOf
|
import androidx.work.workDataOf
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.libraries.androidutils.json.JsonProvider
|
import io.element.android.libraries.androidutils.json.JsonProvider
|
||||||
|
import io.element.android.libraries.core.extensions.mapCatchingExceptions
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
import io.element.android.libraries.matrix.api.core.EventId
|
import io.element.android.libraries.matrix.api.core.EventId
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
|
@ -23,12 +24,29 @@ import timber.log.Timber
|
||||||
class WorkerDataConverter(
|
class WorkerDataConverter(
|
||||||
private val json: JsonProvider,
|
private val json: JsonProvider,
|
||||||
) {
|
) {
|
||||||
fun serialize(notificationEventRequests: List<NotificationEventRequest>): Result<Data> {
|
fun serialize(notificationEventRequests: List<NotificationEventRequest>): List<Result<Data>> {
|
||||||
|
return serializeRequests(notificationEventRequests)
|
||||||
|
.fold(
|
||||||
|
onSuccess = {
|
||||||
|
listOf(Result.success(it))
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
// Perform serialization on sublists, workDataOf may have failed because of size limit
|
||||||
|
Timber.w(it, "Failed to serialize ${notificationEventRequests.size} notification requests, trying with chunks of $CHUNK_SIZE.")
|
||||||
|
notificationEventRequests.chunked(CHUNK_SIZE).map { chunk ->
|
||||||
|
serializeRequests(chunk)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun serializeRequests(notificationEventRequests: List<NotificationEventRequest>): Result<Data> {
|
||||||
return runCatchingExceptions { json().encodeToString(notificationEventRequests.map { it.toData() }) }
|
return runCatchingExceptions { json().encodeToString(notificationEventRequests.map { it.toData() }) }
|
||||||
.onFailure {
|
.onFailure {
|
||||||
Timber.e(it, "Failed to serialize notification requests")
|
Timber.e(it, "Failed to serialize notification requests")
|
||||||
}
|
}
|
||||||
.map { str ->
|
.mapCatchingExceptions { str ->
|
||||||
|
// Note: workDataOf can fail if the data is too large
|
||||||
workDataOf(REQUESTS_KEY to str)
|
workDataOf(REQUESTS_KEY to str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -51,6 +69,7 @@ class WorkerDataConverter(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val REQUESTS_KEY = "requests"
|
private const val REQUESTS_KEY = "requests"
|
||||||
|
private const val CHUNK_SIZE = 20
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,5 @@ package io.element.android.libraries.workmanager.api
|
||||||
import androidx.work.WorkRequest
|
import androidx.work.WorkRequest
|
||||||
|
|
||||||
interface WorkManagerRequest {
|
interface WorkManagerRequest {
|
||||||
fun build(): Result<WorkRequest>
|
fun build(): List<Result<WorkRequest>>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,14 +27,16 @@ class DefaultWorkManagerScheduler(
|
||||||
private val workManager by lazy { WorkManager.getInstance(context) }
|
private val workManager by lazy { WorkManager.getInstance(context) }
|
||||||
|
|
||||||
override fun submit(workManagerRequest: WorkManagerRequest) {
|
override fun submit(workManagerRequest: WorkManagerRequest) {
|
||||||
workManagerRequest.build().fold(
|
workManagerRequest.build().forEach {
|
||||||
onSuccess = {
|
it.fold(
|
||||||
workManager.enqueue(it)
|
onSuccess = { workRequest ->
|
||||||
},
|
workManager.enqueue(workRequest)
|
||||||
onFailure = {
|
},
|
||||||
Timber.e(it, "Failed to build WorkManager request $workManagerRequest")
|
onFailure = {
|
||||||
}
|
Timber.e(it, "Failed to build WorkManager request $workManagerRequest")
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancel(sessionId: SessionId) {
|
override fun cancel(sessionId: SessionId) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue