Simplify push notification flow by using locally stored values for pending pushes (#6258)
* Create `PushRequest` in push history DB: this will be used to store requests for push notifications, either pending or completed ones. * Rename `WorkManagerRequest` to `WorkManagerRequestBuilder`: make its `build` method return a list of `WorkManagerRequestWrapper`, which can be used to enqueue normal or unique workers. * Rename `PerformDatabaseVacuumRequestBuilder` and adapt it to the new API. * Adjust other components using `WorkManagerRequest`. * Replace `SyncNotificationWorkManagerRequestBuilder` with `SyncPendingNotificationsRequestBuilder` and `FetchNotificationsWorker` with `FetchPendingNotificationsWorker`: this new pair of request builder and worker allow enqueuing requests for a session id and, once the worker runs, retrieve all the pending request data and use it to fetch the associated events. This simplifies quite a bit how this data had to be passed or grouped, since it's no longer necessary to do so * Add new methods to `PushHistoryService` to modify the `PushDatabase`: - insertOrUpdatePushRequest - insertOrUpdatePushRequests - getPendingPushRequests - removeOldPushRequests * Make `PushHandler` just handle incoming pushes: those will be inserted into the pending push request table in DB, then handled by the new worker. Once the process finished, a new `NotificationResultProcessor` will handle the results and what needs to be done with them (call ringing, displaying notifications, etc.) * Add `requestType` optional parameter to `WorkManagerScheduler.cancel` so we can decide to only cancel some kinds of requests. * Add migration to remove existing work manager requests for fetching notifications, since the previous worker class no longer exists.
This commit is contained in:
parent
a8c66381f2
commit
721add707c
48 changed files with 1715 additions and 1892 deletions
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package io.element.android.libraries.workmanager.impl
|
||||
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import dev.zacsweers.metro.AppScope
|
||||
|
|
@ -16,9 +17,10 @@ import dev.zacsweers.metro.SingleIn
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionListener
|
||||
import io.element.android.libraries.sessionstorage.api.observer.SessionObserver
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestBuilder
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerScheduler
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerWorkerType
|
||||
import io.element.android.libraries.workmanager.api.workManagerTag
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -41,13 +43,22 @@ class DefaultWorkManagerScheduler(
|
|||
})
|
||||
}
|
||||
|
||||
override fun submit(workManagerRequest: WorkManagerRequest) {
|
||||
workManagerRequest.build().fold(
|
||||
onSuccess = { workRequests ->
|
||||
workManager.enqueue(workRequests)
|
||||
override suspend fun submit(workManagerRequestBuilder: WorkManagerRequestBuilder) {
|
||||
workManagerRequestBuilder.build().fold(
|
||||
onSuccess = { wrappers ->
|
||||
for (wrapper in wrappers) {
|
||||
when (wrapper.type) {
|
||||
WorkManagerWorkerType.Default -> workManager.enqueue(wrapper.request)
|
||||
is WorkManagerWorkerType.Unique -> {
|
||||
val type = wrapper.type as WorkManagerWorkerType.Unique
|
||||
val requests = wrapper.request as OneTimeWorkRequest
|
||||
workManager.enqueueUniqueWork(type.name, type.policy, requests)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to build WorkManager request $workManagerRequest")
|
||||
Timber.e(it, "Failed to build WorkManager request $workManagerRequestBuilder")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -64,10 +75,15 @@ class DefaultWorkManagerScheduler(
|
|||
}
|
||||
}
|
||||
|
||||
override fun cancel(sessionId: SessionId) {
|
||||
override fun cancel(sessionId: SessionId, requestType: WorkManagerRequestType?) {
|
||||
Timber.d("Cancelling work for sessionId: $sessionId")
|
||||
for (requestType in WorkManagerRequestType.entries) {
|
||||
|
||||
if (requestType != null) {
|
||||
workManager.cancelAllWorkByTag(workManagerTag(sessionId, requestType))
|
||||
} else {
|
||||
for (requestType in WorkManagerRequestType.entries) {
|
||||
workManager.cancelAllWorkByTag(workManagerTag(sessionId, requestType))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,12 +7,17 @@
|
|||
|
||||
package io.element.android.libraries.workmanager.impl
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkRequest
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.sessionstorage.test.observer.FakeSessionObserver
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequest
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestBuilder
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestType
|
||||
import io.element.android.libraries.workmanager.api.WorkManagerRequestWrapper
|
||||
import io.element.android.libraries.workmanager.api.workManagerTag
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
|
|
@ -55,7 +60,7 @@ class DefaultWorkManagerSchedulerTest {
|
|||
sessionObserver = FakeSessionObserver(),
|
||||
)
|
||||
|
||||
scheduler.submit(FakeWorkManagerRequest())
|
||||
scheduler.submit(FakeWorkManagerRequestBuilder())
|
||||
|
||||
verify { workManager.enqueue(any<List<WorkRequest>>()) }
|
||||
}
|
||||
|
|
@ -69,7 +74,7 @@ class DefaultWorkManagerSchedulerTest {
|
|||
sessionObserver = FakeSessionObserver(),
|
||||
)
|
||||
|
||||
scheduler.submit(FakeWorkManagerRequest(result = Result.failure(IllegalStateException("Test error"))))
|
||||
scheduler.submit(FakeWorkManagerRequestBuilder(result = Result.failure(IllegalStateException("Test error"))))
|
||||
|
||||
verify(exactly = 0) { workManager.enqueue(any<List<WorkRequest>>()) }
|
||||
}
|
||||
|
|
@ -88,7 +93,7 @@ class DefaultWorkManagerSchedulerTest {
|
|||
val mockSessionA = mockk<WorkRequest> {
|
||||
every { tags } returns setOf(tagToRemove)
|
||||
}
|
||||
scheduler.submit(FakeWorkManagerRequest(result = Result.success(listOf(mockSessionA))))
|
||||
scheduler.submit(FakeWorkManagerRequestBuilder(result = Result.success(listOf(WorkManagerRequestWrapper(mockSessionA)))))
|
||||
|
||||
scheduler.cancel(sessionId)
|
||||
|
||||
|
|
@ -96,10 +101,16 @@ class DefaultWorkManagerSchedulerTest {
|
|||
}
|
||||
}
|
||||
|
||||
private class FakeWorkManagerRequest(
|
||||
private val result: Result<List<WorkRequest>> = Result.success(listOf()),
|
||||
) : WorkManagerRequest {
|
||||
override fun build(): Result<List<WorkRequest>> {
|
||||
private val workRequest = OneTimeWorkRequest.Builder(FakeWorker::class.java).build()
|
||||
|
||||
private class FakeWorkManagerRequestBuilder(
|
||||
private val result: Result<List<WorkManagerRequestWrapper>> = Result.success(listOf(WorkManagerRequestWrapper(workRequest))),
|
||||
) : WorkManagerRequestBuilder {
|
||||
override suspend fun build(): Result<List<WorkManagerRequestWrapper>> {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
internal class FakeWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
override fun doWork(): Result = Result.success()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue