1 /* 2 * Copyright (C) 2017 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 androidx.arch.core.executor.ArchTaskExecutor 20 import androidx.lifecycle.LiveData 21 import java.util.concurrent.Executor 22 import kotlinx.coroutines.CoroutineScope 23 import kotlinx.coroutines.DelicateCoroutinesApi 24 import kotlinx.coroutines.GlobalScope 25 import kotlinx.coroutines.asCoroutineDispatcher 26 27 /** 28 * Builder for `LiveData<PagedList>` for Java users, given a [androidx.paging.DataSource.Factory] 29 * and a [androidx.paging.PagedList.Config]. 30 * 31 * The required parameters are in the constructor, so you can simply construct and build, or 32 * optionally enable extra features (such as initial load key, or BoundaryCallback). 33 * 34 * @param Key Type of input valued used to load data from the [DataSource]. Must be [Int] if you're 35 * using [PositionalDataSource]. 36 * @param Value Item type being presented. 37 * @see toLiveData 38 */ 39 @Deprecated("PagedList is deprecated and has been replaced by PagingData") 40 class LivePagedListBuilder<Key : Any, Value : Any> { 41 private val pagingSourceFactory: (() -> PagingSource<Key, Value>)? 42 private val dataSourceFactory: DataSource.Factory<Key, Value>? 43 44 @Suppress("DEPRECATION") private val config: PagedList.Config 45 @OptIn(DelicateCoroutinesApi::class) private var coroutineScope: CoroutineScope = GlobalScope 46 private var initialLoadKey: Key? = null 47 48 @Suppress("DEPRECATION") private var boundaryCallback: PagedList.BoundaryCallback<Value>? = null 49 private var fetchDispatcher = ArchTaskExecutor.getIOThreadExecutor().asCoroutineDispatcher() 50 51 /** 52 * Creates a [LivePagedListBuilder] with required parameters. 53 * 54 * @param dataSourceFactory [DataSource] factory providing DataSource generations. 55 * @param config Paging configuration. 56 */ 57 @Deprecated( 58 message = "PagedList is deprecated and has been replaced by PagingData", 59 replaceWith = 60 ReplaceWith( 61 """Pager( 62 PagingConfig( 63 config.pageSize, 64 config.prefetchDistance, 65 config.enablePlaceholders, 66 config.initialLoadSizeHint, 67 config.maxSize 68 ), 69 initialLoadKey, 70 dataSourceFactory.asPagingSourceFactory(Dispatchers.IO) 71 ).liveData""", 72 "androidx.paging.Pager", 73 "androidx.paging.PagingConfig", 74 "androidx.paging.liveData", 75 "kotlinx.coroutines.Dispatchers" 76 ) 77 ) 78 constructor( 79 dataSourceFactory: DataSource.Factory<Key, Value>, 80 @Suppress("DEPRECATION") config: PagedList.Config 81 ) { 82 this.pagingSourceFactory = null 83 this.dataSourceFactory = dataSourceFactory 84 this.config = config 85 } 86 87 /** 88 * Creates a [LivePagedListBuilder] with required parameters. 89 * 90 * This method is a convenience for: 91 * ``` 92 * LivePagedListBuilder(dataSourceFactory, 93 * new PagedList.Config.Builder().setPageSize(pageSize).build()) 94 * ``` 95 * 96 * @param dataSourceFactory [DataSource.Factory] providing DataSource generations. 97 * @param pageSize Size of pages to load. 98 */ 99 @Suppress("DEPRECATION") 100 @Deprecated( 101 message = "PagedList is deprecated and has been replaced by PagingData", 102 replaceWith = 103 ReplaceWith( 104 """Pager( 105 PagingConfig(pageSize), 106 initialLoadKey, 107 dataSourceFactory.asPagingSourceFactory(Dispatchers.IO) 108 ).liveData""", 109 "androidx.paging.Pager", 110 "androidx.paging.PagingConfig", 111 "androidx.paging.liveData", 112 "kotlinx.coroutines.Dispatchers" 113 ) 114 ) 115 constructor( 116 dataSourceFactory: DataSource.Factory<Key, Value>, 117 pageSize: Int 118 ) : this(dataSourceFactory, PagedList.Config.Builder().setPageSize(pageSize).build()) 119 120 /** 121 * Creates a [LivePagedListBuilder] with required parameters. 122 * 123 * @param pagingSourceFactory [PagingSource] factory providing [PagingSource] generations. 124 * 125 * The returned [PagingSource] should invalidate itself if the snapshot is no longer valid. If a 126 * [PagingSource] becomes invalid, the only way to query more data is to create a new 127 * [PagingSource] by invoking the supplied [pagingSourceFactory]. 128 * 129 * [pagingSourceFactory] will invoked to construct a new [PagedList] and [PagingSource] when the 130 * current [PagingSource] is invalidated, and pass the new [PagedList] through the 131 * `LiveData<PagedList>` to observers. 132 * 133 * @param config Paging configuration. 134 */ 135 @Deprecated( 136 message = "PagedList is deprecated and has been replaced by PagingData", 137 replaceWith = 138 ReplaceWith( 139 """Pager( 140 PagingConfig( 141 config.pageSize, 142 config.prefetchDistance, 143 config.enablePlaceholders, 144 config.initialLoadSizeHint, 145 config.maxSize 146 ), 147 initialLoadKey, 148 this 149 ).liveData""", 150 "androidx.paging.Pager", 151 "androidx.paging.PagingConfig", 152 "androidx.paging.liveData" 153 ) 154 ) 155 constructor( 156 pagingSourceFactory: () -> PagingSource<Key, Value>, 157 @Suppress("DEPRECATION") config: PagedList.Config 158 ) { 159 this.pagingSourceFactory = pagingSourceFactory 160 this.dataSourceFactory = null 161 this.config = config 162 } 163 164 /** 165 * Creates a [LivePagedListBuilder] with required parameters. 166 * 167 * This method is a convenience for: 168 * ``` 169 * LivePagedListBuilder(pagingSourceFactory, 170 * new PagedList.Config.Builder().setPageSize(pageSize).build()) 171 * ``` 172 * 173 * @param pagingSourceFactory [PagingSource] factory providing [PagingSource] generations. 174 * 175 * The returned [PagingSource] should invalidate itself if the snapshot is no longer valid. If a 176 * [PagingSource] becomes invalid, the only way to query more data is to create a new 177 * [PagingSource] by invoking the supplied [pagingSourceFactory]. 178 * 179 * [pagingSourceFactory] will invoked to construct a new [PagedList] and [PagingSource] when the 180 * current [PagingSource] is invalidated, and pass the new [PagedList] through the 181 * `LiveData<PagedList>` to observers. 182 * 183 * @param pageSize Size of pages to load. 184 */ 185 @Suppress("DEPRECATION") 186 @Deprecated( 187 message = "PagedList is deprecated and has been replaced by PagingData", 188 replaceWith = 189 ReplaceWith( 190 """Pager( 191 PagingConfig(pageSize), 192 initialLoadKey, 193 this 194 ).liveData""", 195 "androidx.paging.Pager", 196 "androidx.paging.PagingConfig", 197 "androidx.paging.liveData" 198 ) 199 ) 200 constructor( 201 pagingSourceFactory: () -> PagingSource<Key, Value>, 202 pageSize: Int 203 ) : this(pagingSourceFactory, PagedList.Config.Builder().setPageSize(pageSize).build()) 204 205 /** 206 * Set the [CoroutineScope] that page loads should be launched within. The set [coroutineScope] 207 * allows a [PagingSource] to cancel running load operations when the results are no longer 208 * needed - for example, when the containing activity is destroyed. 209 * 210 * Defaults to [GlobalScope]. 211 * 212 * @param coroutineScope 213 * @return this 214 */ 215 @Suppress("unused") // Public API setCoroutineScopenull216 fun setCoroutineScope(coroutineScope: CoroutineScope) = 217 this.apply { this.coroutineScope = coroutineScope } 218 219 /** 220 * First loading key passed to the first PagedList/DataSource. 221 * 222 * When a new PagedList/DataSource pair is created after the first, it acquires a load key from 223 * the previous generation so that data is loaded around the position already being observed. 224 * 225 * @param key Initial load key passed to the first PagedList/DataSource. 226 * @return this 227 */ <lambda>null228 fun setInitialLoadKey(key: Key?) = this.apply { initialLoadKey = key } 229 230 /** 231 * Sets a [androidx.paging.PagedList.BoundaryCallback] on each PagedList created, typically used 232 * to load additional data from network when paging from local storage. 233 * 234 * Pass a [PagedList.BoundaryCallback] to listen to when the PagedList runs out of data to load. 235 * If this method is not called, or `null` is passed, you will not be notified when each 236 * [PagingSource] runs out of data to provide to its [PagedList]. 237 * 238 * If you are paging from a DataSource.Factory backed by local storage, you can set a 239 * BoundaryCallback to know when there is no more information to page from local storage. This 240 * is useful to page from the network when local storage is a cache of network data. 241 * 242 * Note that when using a BoundaryCallback with a `LiveData<PagedList>`, method calls on the 243 * callback may be dispatched multiple times - one for each PagedList/DataSource pair. If 244 * loading network data from a BoundaryCallback, you should prevent multiple dispatches of the 245 * same method from triggering multiple simultaneous network loads. 246 * 247 * @param boundaryCallback The boundary callback for listening to PagedList load state. 248 * @return this 249 */ setBoundaryCallbacknull250 fun setBoundaryCallback( 251 @Suppress("DEPRECATION") boundaryCallback: PagedList.BoundaryCallback<Value>? 252 ) = this.apply { this.boundaryCallback = boundaryCallback } 253 254 /** 255 * Sets [Executor] used for background fetching of [PagedList]s, and the pages within. 256 * 257 * The library will wrap this as a [kotlinx.coroutines.CoroutineDispatcher]. 258 * 259 * If not set, defaults to a 260 * [ExecutorCoroutineDispatcher][kotlinx.coroutines.ExecutorCoroutineDispatcher] backed by 261 * [ArchTaskExecutor.getIOThreadExecutor]. 262 * 263 * @param fetchExecutor [Executor] for fetching data from [PagingSource]s. 264 * @return this 265 */ setFetchExecutornull266 fun setFetchExecutor(fetchExecutor: Executor) = 267 this.apply { this.fetchDispatcher = fetchExecutor.asCoroutineDispatcher() } 268 269 /** 270 * Constructs the `LiveData<PagedList>`. 271 * 272 * No work (such as loading) is done immediately, the creation of the first [PagedList] is 273 * deferred until the [LiveData] is observed. 274 * 275 * @return The [LiveData] of [PagedList]s 276 */ 277 @Suppress("DEPRECATION") buildnull278 fun build(): LiveData<PagedList<Value>> { 279 val pagingSourceFactory = 280 pagingSourceFactory ?: dataSourceFactory?.asPagingSourceFactory(fetchDispatcher) 281 282 check(pagingSourceFactory != null) { 283 "LivePagedList cannot be built without a PagingSourceFactory or DataSource.Factory" 284 } 285 286 return LivePagedList( 287 coroutineScope, 288 initialLoadKey, 289 config, 290 boundaryCallback, 291 pagingSourceFactory, 292 ArchTaskExecutor.getMainThreadExecutor().asCoroutineDispatcher(), 293 fetchDispatcher 294 ) 295 } 296 } 297