/* * Copyright 2014 Mostafa Gazar <eng.mostafa.gazar@gmail.com> * * 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.meg7.soas.ui.fragment.retained; import android.app.Activity; import android.app.Fragment; import android.os.Bundle; import com.android.volley.Cache; import com.android.volley.Response; import com.android.volley.VolleyError; import com.meg7.soas.SoasApplication; import com.meg7.soas.data.Photos; import com.meg7.soas.http.HttpConstants; import com.meg7.soas.http.request.GsonGetRequest; import com.meg7.soas.http.util.UrlBuilder; import org.apache.http.HttpStatus; /** * This Fragment manages a single background task and retains * itself across configuration changes. * * @author Mostafa Gazar <eng.mostafa.gazar@gmail.com> */ public class PhotosListTaskFragment extends Fragment { public static final String TAG = PhotosListTaskFragment.class.getSimpleName(); public static enum Task { REFRESH, REQUEST_NEXT } private int mLastRequestedPhotosStatusCode; private Photos mLastRequestedPhotos; private int mNextItemToLoad = 0; private static final int PAGE_SIZE = 14; /** * Callback interface through which the fragment will report the * task's progress and results back to the Activity. */ public static interface TaskCallbacks { void onResponse(Task task, int statusCode, Photos photos); } private TaskCallbacks mCallbacks; /** * This method will only be called once when the retained * Fragment is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Retain this fragment across configuration changes. setRetainInstance(true); // First initial request. refresh(); } public void refresh() { mLastRequestedPhotos = null; mNextItemToLoad = 0; String url = getRequestUrl(); Cache cache = SoasApplication.getRequestQueue(getActivity()).getCache(); Cache.Entry entry = cache.get(url); if (entry != null) { // Invalidate cache and reload. SoasApplication.getRequestQueue(getActivity()).getCache().invalidate(url, true); } GsonGetRequest<Photos> mainRequest = new GsonGetRequest<Photos> (url, Photos.class, new Response.Listener<Photos>() { @Override public void onResponse(Photos photos) { populateResponse(Task.REFRESH, photos); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { populateResponse(Task.REFRESH, error); } } ); mainRequest.setShouldCache(true); SoasApplication.addToRequestQueue(getActivity(), mainRequest, TAG); } public void requestNextPage() { // Create and execute the background task. String url = getRequestUrl(); Cache cache = SoasApplication.getRequestQueue(getActivity()).getCache(); Cache.Entry entry = cache.get(url); if (entry != null) { // Soft invalidate cache because we forced it earlier. // TODO :: Comment out if cache was based on server response cache headers. SoasApplication.getRequestQueue(getActivity()).getCache().invalidate(url, false); } GsonGetRequest<Photos> mainRequest = new GsonGetRequest<Photos> (url, Photos.class, new Response.Listener<Photos>() { @Override public void onResponse(Photos photos) { populateResponse(Task.REQUEST_NEXT, photos); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { populateResponse(Task.REQUEST_NEXT, error); } } ); mainRequest.setShouldCache(true); SoasApplication.addToRequestQueue(getActivity(), mainRequest, TAG); } private String getRequestUrl() { return UrlBuilder.appendGetParams(HttpConstants.PHOTOS_URL, new Photos.RequestParamsBuilder(mNextItemToLoad, mNextItemToLoad + PAGE_SIZE).build()); } private void populateResponse(Task task, Photos photos) { mLastRequestedPhotosStatusCode = HttpStatus.SC_OK; mLastRequestedPhotos = photos; mNextItemToLoad += PAGE_SIZE + 1; mCallbacks.onResponse(task, mLastRequestedPhotosStatusCode, mLastRequestedPhotos); } private void populateResponse(Task task, VolleyError error) { if (error != null && error.networkResponse != null) { mLastRequestedPhotosStatusCode = error.networkResponse.statusCode; } else { mLastRequestedPhotosStatusCode = -1; } mLastRequestedPhotos = null; mCallbacks.onResponse(task, mLastRequestedPhotosStatusCode, mLastRequestedPhotos); } /** * Hold a reference to the parent Activity so we can report the * task's current progress and results. The Android framework * will pass us a reference to the newly created Activity after * each configuration change. */ @Override public void onAttach(Activity activity) { super.onAttach(activity); // Activities containing this fragment must implement its callbacks. if (!(activity instanceof TaskCallbacks)) { throw new IllegalStateException("Activity must implement fragment's callbacks."); } mCallbacks = (TaskCallbacks) activity; } /** * Set the callback to null so we don't accidentally leak the * Activity instance. */ @Override public void onDetach() { super.onDetach(); mCallbacks = null; } @Override public void onDestroy() { super.onDestroy(); // Cancel any running Http requests associated with this fragment TAG. SoasApplication.cancelPendingRequests(TAG); } public int getLastRequestedPhotosStatusCode() { return mLastRequestedPhotosStatusCode; } public Photos getLastRequestedPhotos() { return mLastRequestedPhotos; } }