1 /*
2  * Copyright 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.paging.rxjava3
18 
19 import androidx.paging.ExperimentalPagingApi
20 import androidx.paging.LoadState
21 import androidx.paging.LoadType
22 import androidx.paging.PagingData
23 import androidx.paging.PagingSource
24 import androidx.paging.PagingState
25 import androidx.paging.RemoteMediator
26 import androidx.paging.RemoteMediator.InitializeAction.LAUNCH_INITIAL_REFRESH
27 import androidx.paging.RemoteMediator.MediatorResult
28 import io.reactivex.rxjava3.core.Single
29 import kotlinx.coroutines.rx3.await
30 
31 /** RxJava3 compatibility wrapper around [RemoteMediator]'s suspending APIs. */
32 @ExperimentalPagingApi
33 abstract class RxRemoteMediator<Key : Any, Value : Any> : RemoteMediator<Key, Value>() {
34     /**
35      * Implement this method to load additional remote data, which will then be stored for the
36      * [PagingSource] to access. These loads take one of two forms:
37      *     * type == [LoadType.PREPEND] / [LoadType.APPEND] The [PagingSource] has loaded a
38      *       'boundary' page, with a `null` adjacent key. This means this method should load
39      *       additional remote data to append / prepend as appropriate, and store it locally.
40      * * type == [LoadType.REFRESH] The app (or [initialize]) has requested a remote refresh of
41      *   data. This means the method should generally load remote data, and **replace** all local
42      *   data.
43      *
44      * The runtime of this method defines loading state behavior in boundary conditions, which
45      * affects e.g., [LoadState] callbacks registered to [androidx.paging.PagingDataAdapter].
46      *
47      * NOTE: A [PagingSource.load] request which is fulfilled by a page that hits a boundary
48      * condition in either direction will trigger this callback with [LoadType.PREPEND] or
49      * [LoadType.APPEND] or both. [LoadType.REFRESH] occurs as a result of [initialize].
50      *
51      * @param loadType [LoadType] of the boundary condition which triggered this callback.
52      *     * [LoadType.PREPEND] indicates a boundary condition at the front of the list.
53      *     * [LoadType.APPEND] indicates a boundary condition at the end of the list.
54      *     * [LoadType.REFRESH] indicates this callback was triggered as the result of a requested
55      *       refresh - either driven by the UI, or by [initialize].
56      *
57      * @param state A copy of the state including the list of pages currently held in memory of the
58      *   currently presented [PagingData] at the time of starting the load. E.g. for load(loadType =
59      *   END), you can use the page or item at the end as input for what to load from the network.
60      * @return [MediatorResult] signifying what [LoadState] to be passed to the UI, and whether
61      *   there's more data available.
62      */
loadSinglenull63     abstract fun loadSingle(
64         loadType: LoadType,
65         state: PagingState<Key, Value>
66     ): Single<MediatorResult>
67 
68     /**
69      * Callback fired during initialization of a [PagingData] stream, before initial load.
70      *
71      * This function runs to completion before any loading is performed.
72      *
73      * @return [InitializeAction] indicating the action to take after initialization:
74      *     * [LAUNCH_INITIAL_REFRESH] to immediately dispatch a [load] asynchronously with load type
75      *       [LoadType.REFRESH], to update paginated content when the stream is initialized. Note:
76      *       This also prevents [RemoteMediator] from triggering [PREPEND] or [APPEND] until
77      *       [REFRESH] succeeds.
78      *     * [SKIP_INITIAL_REFRESH][InitializeAction.SKIP_INITIAL_REFRESH] to wait for a refresh
79      *       request from the UI before dispatching a [load] with load type [LoadType.REFRESH].
80      */
81     open fun initializeSingle(): Single<InitializeAction> = Single.just(LAUNCH_INITIAL_REFRESH)
82 
83     final override suspend fun load(
84         loadType: LoadType,
85         state: PagingState<Key, Value>
86     ): MediatorResult {
87         return loadSingle(loadType, state).await()
88     }
89 
initializenull90     final override suspend fun initialize(): InitializeAction {
91         return initializeSingle().await()
92     }
93 }
94