package ua.kpi.ecampus.util.pagination; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import ua.kpi.ecampus.ui.adapter.PagingRecyclerAdapter; import ua.kpi.ecampus.util.BackgroundExecutor; import java.util.ArrayList; import java.util.Collections; import java.util.List; import rx.Observable; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import rx.subscriptions.Subscriptions; /** * Created by Administrator on 21.03.2016. */ public class PaginationTool<T> { // for first start of items loading then on RecyclerView there are not // items and no scrolling private static final int EMPTY_LIST_ITEMS_COUNT = 0; // default limit for requests private static final int DEFAULT_LIMIT = 100; // default max attempts to retry loading request private static final int MAX_ATTEMPTS_TO_RETRY_LOADING = 3; private RecyclerView recyclerView; private PagingListener<T> pagingListener; private int limit; private int emptyListCount; private int retryCount; private boolean emptyListCountPlusToOffset; private PaginationTool() { } public Observable<T> getPagingObservable() { int startNumberOfRetryAttempt = 0; return getScrollObservable(recyclerView, limit, emptyListCount) .subscribeOn(AndroidSchedulers.mainThread()) .distinctUntilChanged() .observeOn(Schedulers.from(BackgroundExecutor .getSafeBackgroundExecutor())) .switchMap(lastId -> getPagingObservable(pagingListener, pagingListener.onNextPage(lastId), startNumberOfRetryAttempt, lastId, retryCount)); } private Observable<Integer> getScrollObservable(RecyclerView recyclerView, int limit, int emptyListCount) { return Observable.create(subscriber -> { final RecyclerView.OnScrollListener sl = new RecyclerView .OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (!subscriber.isUnsubscribed()) { int position = getLastVisibleItemPosition(recyclerView); int updatePosition = recyclerView.getAdapter() .getItemCount() - 1 - (limit / 2); if (position >= updatePosition) { //int offset = emptyListCountPlusToOffset ? // recyclerView.getAdapter().getItemCount() : // recyclerView.getAdapter().getItemCount() - // emptyListCount; int lastId = getLastId(recyclerView); subscriber.onNext(lastId); } } } }; recyclerView.addOnScrollListener(sl); subscriber.add(Subscriptions.create(() -> recyclerView .removeOnScrollListener(sl))); if (recyclerView.getAdapter().getItemCount() == emptyListCount) { //int offset = emptyListCountPlusToOffset ? recyclerView // .getAdapter().getItemCount() : recyclerView.getAdapter() // .getItemCount() - emptyListCount; int lastId = getLastId(recyclerView); subscriber.onNext(lastId); } }); } private int getLastId(RecyclerView recyclerView) { PagingRecyclerAdapter adapter = (PagingRecyclerAdapter) recyclerView .getAdapter(); int lastId; if (adapter.getItemCount() == 0) { lastId = -1; } else { lastId = Integer.valueOf(adapter.getLastItem().getId()); } return lastId; } private int getLastVisibleItemPosition(RecyclerView recyclerView) { Class recyclerViewLMClass = recyclerView.getLayoutManager().getClass(); if (recyclerViewLMClass == LinearLayoutManager.class || LinearLayoutManager.class.isAssignableFrom (recyclerViewLMClass)) { LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); return linearLayoutManager.findLastVisibleItemPosition(); } else if (recyclerViewLMClass == StaggeredGridLayoutManager.class || StaggeredGridLayoutManager.class.isAssignableFrom (recyclerViewLMClass)) { StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) recyclerView .getLayoutManager(); int[] into = staggeredGridLayoutManager .findLastVisibleItemPositions(null); List<Integer> intoList = new ArrayList<>(); for (int i : into) { intoList.add(i); } return Collections.max(intoList); } throw new PagingException("Unknown LayoutManager class: " + recyclerViewLMClass.toString()); } private Observable getPagingObservable(PagingListener<T> listener, Observable<T> observable, int numberOfAttemptToRetry, int lastId, int retryCount) { return observable.onErrorResumeNext(throwable -> { // retry to load new data portion if error occurred if (numberOfAttemptToRetry < retryCount) { int attemptToRetryInc = numberOfAttemptToRetry + 1; return getPagingObservable(listener, listener.onNextPage (lastId), attemptToRetryInc, lastId, retryCount); } else { return Observable.error(new PagingException("Exception while " + "downloading has occurred. Check the url is valid, " + "internet connection is available etc.")); } }); } public static <T> Builder<T> buildPagingObservable(RecyclerView recyclerView, PagingListener<T> pagingListener) { return new Builder<>(recyclerView, pagingListener); } public static class Builder<T> { private RecyclerView recyclerView; private PagingListener<T> pagingListener; private int limit = DEFAULT_LIMIT; private int emptyListCount = EMPTY_LIST_ITEMS_COUNT; private int retryCount = MAX_ATTEMPTS_TO_RETRY_LOADING; private boolean emptyListCountPlusToOffset = false; private Builder(RecyclerView recyclerView, PagingListener<T> pagingListener) { if (recyclerView == null) { throw new PagingException("null recyclerView"); } if (recyclerView.getAdapter() == null) { throw new PagingException("null recyclerView adapter"); } if (pagingListener == null) { throw new PagingException("null pagingListener"); } this.recyclerView = recyclerView; this.pagingListener = pagingListener; } public Builder<T> setLimit(int limit) { if (limit <= 0) { throw new PagingException("limit must be greater then 0"); } this.limit = limit; return this; } public Builder<T> setEmptyListCount(int emptyListCount) { if (emptyListCount < 0) { throw new PagingException("emptyListCount must be not less " + "then 0"); } this.emptyListCount = emptyListCount; return this; } public Builder<T> setRetryCount(int retryCount) { if (retryCount < 0) { throw new PagingException("retryCount must be not less then 0"); } this.retryCount = retryCount; return this; } public Builder<T> setEmptyListCountPlusToOffset(boolean emptyListCountPlusToOffset) { this.emptyListCountPlusToOffset = emptyListCountPlusToOffset; return this; } public PaginationTool<T> build() { PaginationTool<T> paginationTool = new PaginationTool<>(); paginationTool.recyclerView = this.recyclerView; paginationTool.pagingListener = pagingListener; paginationTool.limit = limit; paginationTool.emptyListCount = emptyListCount; paginationTool.retryCount = retryCount; paginationTool.emptyListCountPlusToOffset = emptyListCountPlusToOffset; return paginationTool; } } }