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