Snippets

John Paging Remote Mediator

Created by John De la cruz

private const val STARTING_PAGE_INDEX = 1

@ExperimentalPagingApi
class PagingRemoteMediator(
    private val userId: String,
    private val service: NetworkData,
    private val database: TimeSheetDatabase

) : RemoteMediator<Int, TimeSheet>() {


    override suspend fun initialize(): InitializeAction {
        return InitializeAction.LAUNCH_INITIAL_REFRESH
    }

    override suspend fun load(
        loadType: LoadType,
        state: PagingState<Int, TimeSheet>
    ): MediatorResult {
        val page : Int = when (loadType){

            LoadType.REFRESH ->{
                val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
                remoteKeys?.nextKey?.minus(1) ?: STARTING_PAGE_INDEX

            }
            LoadType.PREPEND ->{

                val remoteKeys = getRemoteKeyForFirstItem(state)
                // If remoteKeys is null, that means the refresh result is not in the database yet.
                // We can return Success with `endOfPaginationReached = false` because Paging
                // will call this method again if RemoteKeys becomes non-null.
                // If remoteKeys is NOT NULL but its prevKey is null, that means we've reached
                // the end of pagination for prepend.
                val prevKey = remoteKeys?.prevKey
                    ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
                prevKey
            }
            LoadType.APPEND -> {
                val remoteKeys = getRemoteKeyForLastItem(state)
                // If remoteKeys is null, that means the refresh result is not in the database yet.
                // We can return Success with `endOfPaginationReached = false` because Paging
                // will call this method again if RemoteKeys becomes non-null.
                // If remoteKeys is NOT NULL but its prevKey is null, that means we've reached
                // the end of pagination for append.
                val nextKey = remoteKeys?.nextKey
                    ?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
                nextKey
            }

        }

        try {
            val response = service.getTimeSheets(userId, page, state.config.pageSize)


            val endOfPaginationReached = response.nextPage == null

            database.withTransaction {

                if (loadType == LoadType.REFRESH) {
                    database.remoteKeysDao().clearRemoteKeys()
                    database.timeSheetsDao()
                }


                val prevKey = if (page == STARTING_PAGE_INDEX) null else page - 1
                val nextKey = if (endOfPaginationReached) null else response.nextPage
                val keys = response.data.map {
                    RemoteKeys(timeSheetId = it.id, prevKey = prevKey, nextKey = nextKey)
                }
                database.remoteKeysDao().insertAll(keys)
                database.timeSheetsDao().insertAll(response.data)

            }

            return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)

        }catch (e : HttpAccess.NoNetworkException){
            return MediatorResult.Error(e)

        }catch (e: HttpException){
            return MediatorResult.Error(e)

        }catch (e: IOException){
            return MediatorResult.Error(e)

        }
    }
    private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, TimeSheet>): RemoteKeys? {
        // Get the last page that was retrieved, that contained items.
        // From that last page, get the last item
        return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
            ?.let {
                // Get the remote keys of the last item retrieved
                database.remoteKeysDao().remoteKeysTimeSheetId(it.id)
            }
    }

    private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, TimeSheet>): RemoteKeys? {
        // Get the first page that was retrieved, that contained items.
        // From that first page, get the first item
        return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
            ?.let {
                // Get the remote keys of the first items retrieved
                database.remoteKeysDao().remoteKeysTimeSheetId(it.id)
            }
    }

    private suspend fun getRemoteKeyClosestToCurrentPosition(
        state: PagingState<Int, TimeSheet>
    ): RemoteKeys? {
        // The paging library is trying to load data after the anchor position
        // Get the item closest to the anchor position
        return state.anchorPosition?.let { position ->
            state.closestItemToPosition(position)?.id?.let {
                database.remoteKeysDao().remoteKeysTimeSheetId(it)
            }
        }
    }

}

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.