package com.kenny.openimgur.fragments;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.kenny.openimgur.R;
import com.kenny.openimgur.activities.SettingsActivity;
import com.kenny.openimgur.activities.ViewActivity;
import com.kenny.openimgur.api.ApiClient;
import com.kenny.openimgur.api.responses.GalleryResponse;
import com.kenny.openimgur.classes.FragmentListener;
import com.kenny.openimgur.classes.ImgurBaseObject;
import com.kenny.openimgur.classes.ImgurPhoto;
import com.kenny.openimgur.collections.SetUniqueList;
import com.kenny.openimgur.ui.adapters.GalleryAdapter;
import com.kenny.openimgur.util.ImageUtil;
import com.kenny.openimgur.util.LogUtil;
import com.kenny.openimgur.util.RequestCodes;
import com.kenny.openimgur.util.ViewUtils;
import com.kennyc.view.MultiStateView;
import com.nostra13.universalimageloader.core.ImageLoader;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.OnClick;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Base class for fragments that display images in a grid like style
* Created by Kenny Campagna on 12/13/2014.
*/
public abstract class BaseGridFragment extends BaseFragment implements Callback<GalleryResponse>, View.OnClickListener {
private static final String KEY_CURRENT_POSITION = "position";
private static final String KEY_ITEMS = "items";
private static final String KEY_CURRENT_PAGE = "page";
private static final String KEY_REQUEST_ID = "requestId";
private static final String KEY_HAS_MORE = "hasMore";
@BindView(R.id.multiView)
protected MultiStateView mMultiStateView;
@BindView(R.id.grid)
protected RecyclerView mGrid;
@BindView(R.id.refreshLayout)
protected SwipeRefreshLayout mRefreshLayout;
@Nullable
@BindView(R.id.loadingFooter)
View mLoadingFooter;
protected FragmentListener mListener;
protected boolean mIsLoading = false;
protected int mCurrentPage = 0;
protected String mRequestId = null;
private boolean mAllowNSFW;
protected boolean mHasMore = true;
protected GridLayoutManager mManager;
private GalleryAdapter mAdapter;
ImageLoader imageLoader;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof FragmentListener) mListener = (FragmentListener) activity;
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAllowNSFW = app.getPreferences().getBoolean(SettingsActivity.NSFW_KEY, false);
ViewUtils.setRecyclerViewGridDefaults(getActivity(), mGrid);
imageLoader = ImageUtil.getImageLoader(getActivity());
mGrid.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (mManager == null) {
mManager = (GridLayoutManager) recyclerView.getLayoutManager();
}
int visibleItemCount = mManager.getChildCount();
int totalItemCount = mManager.getItemCount();
int firstVisibleItemPosition = mManager.findFirstVisibleItemPosition();
// Load more items when hey get to the end of the list
if (mHasMore && totalItemCount > 0 && firstVisibleItemPosition + visibleItemCount >= totalItemCount && !mIsLoading) {
mIsLoading = true;
mCurrentPage++;
fetchGallery();
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
case RecyclerView.SCROLL_STATE_DRAGGING:
imageLoader.resume();
break;
case RecyclerView.SCROLL_STATE_SETTLING:
imageLoader.pause();
break;
}
}
});
mRefreshLayout.setColorSchemeColors(getResources().getColor(theme.accentColor));
int bgColor = theme.isDarkTheme ? R.color.bg_dark : R.color.bg_light;
mRefreshLayout.setProgressBackgroundColorSchemeColor(getResources().getColor(bgColor));
onRestoreSavedInstance(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
GalleryAdapter adapter = getAdapter();
if (adapter == null || adapter.isEmpty()) {
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING);
mIsLoading = true;
if (mListener != null)
mListener.onFragmentStateChange(FragmentListener.STATE_LOADING_STARTED);
fetchGallery();
}
}
protected void setAdapter(GalleryAdapter adapter) {
mAdapter = adapter;
mGrid.setAdapter(mAdapter);
mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
refresh();
}
});
}
protected GalleryAdapter getAdapter() {
return mAdapter;
}
@Override
public void onClick(View v) {
int adapterPosition = mGrid.getChildAdapterPosition(v);
if (adapterPosition != RecyclerView.NO_POSITION) {
ArrayList<ImgurBaseObject> items = getAdapter().getItems(adapterPosition);
int itemPosition = adapterPosition;
// Get the correct array index of the selected item
if (itemPosition > GalleryAdapter.MAX_ITEMS / 2) {
itemPosition = items.size() == GalleryAdapter.MAX_ITEMS
? GalleryAdapter.MAX_ITEMS / 2
: items.size() - (getAdapter().getItemCount() - itemPosition);
}
onItemSelected(v, itemPosition, items);
}
}
public void refresh() {
mHasMore = true;
mCurrentPage = 0;
mIsLoading = true;
if (getAdapter() != null) getAdapter().clear();
if (mListener != null)
mListener.onFragmentStateChange(FragmentListener.STATE_LOADING_STARTED);
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING);
fetchGallery();
}
@Nullable
@OnClick(R.id.errorButton)
public void onRetryClick() {
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_LOADING);
fetchGallery();
}
protected void onRestoreSavedInstance(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mCurrentPage = savedInstanceState.getInt(KEY_CURRENT_PAGE, 0);
mRequestId = savedInstanceState.getString(KEY_REQUEST_ID, null);
mHasMore = savedInstanceState.getBoolean(KEY_HAS_MORE, true);
if (savedInstanceState.containsKey(KEY_ITEMS)) {
ArrayList<ImgurBaseObject> items = savedInstanceState.getParcelableArrayList(KEY_ITEMS);
int currentPosition = savedInstanceState.getInt(KEY_CURRENT_POSITION, 0);
setAdapter(new GalleryAdapter(getActivity(), SetUniqueList.decorate(items), this, showPoints()));
mGrid.scrollToPosition(currentPosition);
if (mListener != null)
mListener.onFragmentStateChange(FragmentListener.STATE_LOADING_COMPLETE);
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT);
}
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_CURRENT_PAGE, mCurrentPage);
outState.putString(KEY_REQUEST_ID, mRequestId);
outState.putBoolean(KEY_HAS_MORE, mHasMore);
GalleryAdapter adapter = getAdapter();
if (adapter != null && !adapter.isEmpty()) {
// Saving the entire adapter can cause a crash in N
if (isApiLevel(Build.VERSION_CODES.N) && adapter.getItemCount() > GalleryAdapter.MAX_ITEMS) {
return;
}
outState.putParcelableArrayList(KEY_ITEMS, adapter.retainItems());
GridLayoutManager manager = (GridLayoutManager) mGrid.getLayoutManager();
outState.putInt(KEY_CURRENT_POSITION, manager.findFirstVisibleItemPosition());
}
}
@Override
public void onDestroyView() {
GalleryAdapter adapter = getAdapter();
if (adapter != null) adapter.onDestroy();
super.onDestroyView();
}
/**
* Callback for when an item is selected from the grid
*
* @param view The {@link View} selected
* @param position The position of the item in the list of items
* @param items The list of items that will be able to paged between
*/
protected void onItemSelected(View view, int position, ArrayList<ImgurBaseObject> items) {
startActivityForResult(ViewActivity.createIntent(getActivity(), items, position), RequestCodes.GALLERY_VIEW);
}
/**
* Configured the ApiClient to make the api request
*/
protected void fetchGallery() {
if (mLoadingFooter != null) mLoadingFooter.setVisibility(View.VISIBLE);
mIsLoading = true;
}
@Override
public void onResponse(Call<GalleryResponse> call, Response<GalleryResponse> response) {
if (!isAdded()) return;
if (response != null) {
GalleryResponse galleryResponse = response.body();
if (galleryResponse != null) {
galleryResponse.purgeNSFW(mAllowNSFW);
onApiResult(galleryResponse);
} else {
onApiFailure(ApiClient.getErrorCode(response.code()));
}
} else {
onApiFailure(R.string.error_generic);
}
}
@Override
public void onFailure(Call<GalleryResponse> call, Throwable t) {
LogUtil.e(TAG, "Error fetching gallery items", t);
if (!isAdded()) return;
onApiFailure(ApiClient.getErrorCode(t));
if (mLoadingFooter != null) mLoadingFooter.setVisibility(View.GONE);
}
protected void onApiResult(@NonNull GalleryResponse galleryResponse) {
if (!galleryResponse.data.isEmpty()) {
galleryResponse.purgeNSFW(mAllowNSFW);
if (getAdapter() == null) {
setAdapter(new GalleryAdapter(getActivity(), SetUniqueList.decorate(galleryResponse.data), this, showPoints()));
} else {
getAdapter().addItems(galleryResponse.data);
}
if (mMultiStateView != null)
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_CONTENT);
if (mCurrentPage == 0 && mListener != null) {
mListener.onFragmentStateChange(FragmentListener.STATE_LOADING_COMPLETE);
}
} else {
onEmptyResults();
}
mIsLoading = false;
if (mRefreshLayout != null) mRefreshLayout.setRefreshing(false);
if (mLoadingFooter != null) mLoadingFooter.setVisibility(View.GONE);
}
protected void onApiFailure(@StringRes int errorString) {
if (getAdapter() == null || getAdapter().isEmpty()) {
if (mListener != null) mListener.onFragmentStateChange(FragmentListener.STATE_ERROR);
ViewUtils.setErrorText(mMultiStateView, R.id.errorMessage, errorString);
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_ERROR);
}
mIsLoading = false;
if (mRefreshLayout != null) mRefreshLayout.setRefreshing(false);
if (mLoadingFooter != null) mLoadingFooter.setVisibility(View.GONE);
}
protected void onEmptyResults() {
if (mLoadingFooter != null) mLoadingFooter.setVisibility(View.GONE);
mHasMore = false;
if (mMultiStateView.getView(MultiStateView.VIEW_STATE_EMPTY) != null && (getAdapter() == null || getAdapter().isEmpty())) {
mMultiStateView.setViewState(MultiStateView.VIEW_STATE_EMPTY);
}
}
/**
* If the adapter should show the points on the images
*
* @return
*/
protected boolean showPoints() {
return true;
}
/**
* Returns the view to attach a {@link android.support.design.widget.Snackbar} to
*
* @return
*/
@NonNull
protected View getSnackbarView() {
return mListener != null ? mListener.getSnackbarView() : mMultiStateView;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RequestCodes.GALLERY_VIEW:
if (data != null && mAdapter != null) {
ImgurBaseObject obj = data.getParcelableExtra(ViewActivity.KEY_ENDING_ITEM);
if (obj != null) {
int adapterPosition = mAdapter.indexOf(obj);
if (adapterPosition >= 0) {
if (mManager == null) mManager = (GridLayoutManager) mGrid.getLayoutManager();
int visibleItemCount = mManager.getChildCount();
int firstVisibleItemPosition = mManager.findFirstVisibleItemPosition();
// Update the grid to the item they ended on
if (adapterPosition < firstVisibleItemPosition || adapterPosition > firstVisibleItemPosition + visibleItemCount) {
mGrid.scrollToPosition(adapterPosition);
}
}
}
}
break;
case RequestCodes.SETTINGS:
GalleryAdapter adapter = getAdapter();
if (adapter != null) {
SharedPreferences pref = app.getPreferences();
boolean nsfwThumb = pref.getBoolean(SettingsActivity.KEY_NSFW_THUMBNAILS, false);
mAllowNSFW = pref.getBoolean(SettingsActivity.NSFW_KEY, false);
adapter.setAllowNSFW(nsfwThumb);
adapter.setThumbnailQuality(getActivity(), pref.getString(SettingsActivity.KEY_THUMBNAIL_QUALITY, ImgurPhoto.THUMBNAIL_GALLERY));
}
break;
}
}
}