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
18 
19 import kotlin.jvm.JvmOverloads
20 import kotlinx.coroutines.flow.Flow
21 
22 /**
23  * Primary entry point into Paging; constructor for a reactive stream of [PagingData]. The same
24  * Pager instance should be reused within an instance of ViewModel. For example in your ViewModel:
25  * ```
26  * // create a Pager instance and store to a variable
27  * val pager = Pager(
28  *      ...
29  *      )
30  *      .flow
31  *      .cachedIn(viewModelScope)
32  * ```
33  *
34  * Each [PagingData] represents a snapshot of the backing paginated data. Updates to the backing
35  * dataset should be represented by a new instance of [PagingData].
36  *
37  * [PagingSource.invalidate] and calls to [AsyncPagingDataDiffer.refresh] or
38  * [PagingDataAdapter.refresh] will notify [Pager] that the backing dataset has been updated and a
39  * new [PagingData] / [PagingSource] pair will be generated to represent an updated snapshot.
40  *
41  * [PagingData] can be transformed to alter data as it loads, and presented in a `RecyclerView` via
42  * `AsyncPagingDataDiffer` or `PagingDataAdapter`.
43  *
44  * LiveData support is available as an extension property provided by the
45  * `androidx.paging:paging-runtime` artifact.
46  *
47  * RxJava support is available as extension properties provided by the
48  * `androidx.paging:paging-rxjava2` artifact.
49  */
50 public class Pager<Key : Any, Value : Any>
51 // Experimental usage is propagated to public API via constructor argument.
52 @ExperimentalPagingApi
53 constructor(
54     config: PagingConfig,
55     initialKey: Key? = null,
56     remoteMediator: RemoteMediator<Key, Value>?,
57     pagingSourceFactory: () -> PagingSource<Key, Value>
58 ) {
59     // Experimental usage is internal, so opt-in is allowed here.
60     @JvmOverloads
61     @OptIn(ExperimentalPagingApi::class)
62     public constructor(
63         config: PagingConfig,
64         initialKey: Key? = null,
65         pagingSourceFactory: () -> PagingSource<Key, Value>
66     ) : this(config, initialKey, null, pagingSourceFactory)
67 
68     /**
69      * A cold [Flow] of [PagingData], which emits new instances of [PagingData] once they become
70      * invalidated by [PagingSource.invalidate] or calls to [AsyncPagingDataDiffer.refresh] or
71      * [PagingDataAdapter.refresh].
72      *
73      * To consume this stream as a LiveData or in Rx, you may use the extensions available in the
74      * paging-runtime or paging-rxjava* artifacts.
75      *
76      * NOTE: Instances of [PagingData] emitted by this [Flow] are not re-usable and cannot be
77      * submitted multiple times. This is especially relevant for transforms such as
78      * [Flow.combine][kotlinx.coroutines.flow.combine], which would replay the latest value
79      * downstream. To ensure you get a new instance of [PagingData] for each downstream observer,
80      * you should use the [cachedIn] operator which multicasts the [Flow] in a way that returns a
81      * new instance of [PagingData] with cached data pre-loaded.
82      */
83     @OptIn(androidx.paging.ExperimentalPagingApi::class)
84     public val flow: Flow<PagingData<Value>> =
85         PageFetcher(
86                 pagingSourceFactory =
87                     if (pagingSourceFactory is SuspendingPagingSourceFactory<Key, Value>) {
88                         pagingSourceFactory::create
89                     } else {
90                         // cannot pass it as is since it is not a suspend function. Hence, we wrap
91                         // it in {}
92                         // which means we are calling the original factory inside a suspend function
<lambda>null93                         { pagingSourceFactory() }
94                     },
95                 initialKey = initialKey,
96                 config = config,
97                 remoteMediator = remoteMediator
98             )
99             .flow
100 }
101