/* * Copyright (C) 2014 SCVNGR, Inc. d/b/a LevelUp * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.scvngr.levelup.core.net.request.factory; import android.content.Context; import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.scvngr.levelup.core.annotation.LevelUpApi; import com.scvngr.levelup.core.annotation.LevelUpApi.Contract; import com.scvngr.levelup.core.net.AbstractRequest; import com.scvngr.levelup.core.net.AccessTokenRetriever; /** * <p> * A request factory that can page through requests. This is used with endpoints that support the * Link header, as noted in {@link com.scvngr.levelup.core.util.LinkHeaderParser}. This will save the * next page URL in a shared preference so that it can be resumed from that point forward. * </p> * * <p> * To use, subclass and implement methods to retrieve the first page ({@link #getFirstPageRequest()} * ) and to construct a request for an arbitrary page from a URL ({@link #getPageRequest(Uri)}). * Once a page has been loaded successfully, {@link #setNextPage(Uri)} should be called with the URL * contained in the Link header. * </p> */ @LevelUpApi(contract = Contract.INTERNAL) public abstract class AbstractPagingRequestFactory extends AbstractRequestFactory { @NonNull private final PageCacheRetriever mPageCacheRetriever; @NonNull private final String mSavedPageKey; /** * @param context the context. * @param retriever an access token retriever. * @param pageCacheRetriever a page cache retriever. * @param savedPageKey a key that's unique for the endpoint that this request factory is for. * This should be full namespaced to ensure its uniqueness. */ public AbstractPagingRequestFactory(@NonNull final Context context, @NonNull final AccessTokenRetriever retriever, @NonNull final PageCacheRetriever pageCacheRetriever, @NonNull final String savedPageKey) { super(context, retriever); mSavedPageKey = savedPageKey; mPageCacheRetriever = pageCacheRetriever; } /** * Subclasses must implement this to construct a new request for the first page. This is usually * just a request to the endpoint. * * @return a request for the first page. */ @NonNull public abstract AbstractRequest getFirstPageRequest(); /** * <p> * Gets the next page. If no pages have been loaded before, this returns the result of * {@link #getFirstPageRequest()}, otherwise it loads a request for the next page with * {@link #getPageRequest(Uri)}. * </p> * * <p> * This must not be called from the UI thread. * </p> * * @return a request for the next page that should be loaded. */ @NonNull public final AbstractRequest getNextPageRequest() { AbstractRequest response; final Uri nextPage = mPageCacheRetriever.getNextPageUrl(mSavedPageKey); if (null == nextPage) { response = getFirstPageRequest(); } else { response = getPageRequest(nextPage); if (null == response) { response = getFirstPageRequest(); } } return response; } /** * Subclasses must implement this to construct a new request for the given next page. * * @param page the URL of the next page that should be loaded. * @return a request for the next page or null if there is an error. */ @Nullable public abstract AbstractRequest getPageRequest(@NonNull final Uri page); /** * <p> * Sets the next page. This is the URL returned in the Link header for each page that has been * successfully loaded into cache. * </p> * * <p> * This may be called from the UI thread. * </p> * * @param page the URL as returned by the Link header of the response or null to clear. */ public final void setNextPage(@Nullable final Uri page) { mPageCacheRetriever.setNextPage(mSavedPageKey, page); } /** * @return the page cache retriever. */ @NonNull protected PageCacheRetriever getPageCacheRetriever() { return mPageCacheRetriever; } /** * Implement this to support persisting the URL that points to the next page of information. */ public interface PageCacheRetriever { /** * <p> * Retrieves the next page for the given key. * </p> * * <p> * Classes implementing this method must retrieve the URI associated with the provided key. * This will not be called from the UI thread. * </p> * * @return the next page of content that should be loaded or {@code null} if none have been * set yet for this key. * @param pageKey The key under which the page URI will be stored. This must be unique in * the context of the application and be fully namespaced. */ @Nullable Uri getNextPageUrl(@NonNull final String pageKey); /** * <p> * Sets the next page. This is the URL returned in the Link header for each page that has * been successfully loaded into cache. * </p> * * <p> * Classes implementing this method must persist the provided URI so that it may be * retrieved by the given key. This may be called from the UI thread. * </p> * * @param pageKey The key under which the page URI will be stored. This must be unique in the * context of the application and be fully namespaced. * @param page the URL as returned by the Link header of the response or null to clear. */ void setNextPage(@NonNull final String pageKey, @Nullable final Uri page); } }