package com.xda.one.ui;
import com.xda.one.R;
import com.xda.one.api.inteface.ThreadClient;
import com.xda.one.api.model.interfaces.UnifiedThread;
import com.xda.one.api.retrofit.RetrofitThreadClient;
import com.xda.one.model.augmented.AugmentedUnifiedThread;
import com.xda.one.model.augmented.container.AugmentedUnifiedThreadContainer;
import com.xda.one.ui.helper.ActionModeHelper;
import com.xda.one.ui.listener.InfiniteRecyclerLoadHelper;
import com.xda.one.ui.thread.DefaultThreadLoaderStrategy;
import com.xda.one.ui.thread.FirstThreadClickStrategy;
import com.xda.one.ui.thread.ParticipatedThreadLoaderStrategy;
import com.xda.one.ui.thread.SubscribedThreadLoaderStrategy;
import com.xda.one.ui.thread.ThreadActionModeHelper;
import com.xda.one.ui.thread.ThreadClickStrategy;
import com.xda.one.ui.thread.ThreadEventHelper;
import com.xda.one.ui.thread.ThreadLoaderStrategy;
import com.xda.one.ui.thread.UnreadThreadClickStrategy;
import com.xda.one.ui.widget.FloatingActionButton;
import com.xda.one.ui.widget.HierarchySpinnerAdapter;
import com.xda.one.ui.widget.XDARefreshLayout;
import com.xda.one.util.AccountUtils;
import com.xda.one.util.CompatUtils;
import com.xda.one.util.UIUtils;
import com.xda.one.util.Utils;
import android.app.Activity;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.XDALinerLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
public class ThreadFragment extends Fragment {
// Request codes
public static final int CREATE_THREAD_REQUEST_CODE = 101;
// Argument keys
public static final String FORUM_ID_ARGUMENT = "sub_forum_id";
private static final String FORUM_TITLE_ARGUMENT = "forum_title";
private static final String PARENT_FORUM_TITLE_ARGUMENT = "parent_forum_title";
private static final String ADD_EXTRA_DECOR_ARGUMENT = "add_extra_decor";
private static final String THREAD_LOAD_STRATEGY_ARGUMENT = "thread_load_strategy";
private static final String THREAD_CLICK_STRATEGY_ARGUMENT = "thread_click_strategy";
private static final String CURRENT_PAGE_LOADER_ARGUMENT = "current_page";
private static final String FORUM_HIERARCHY_ARGUMENT = "hierarchy";
// Saved state keys
private static final String THREADS_SAVED_STATE = "threads_saved_state";
private static final String PAGES_SAVED_STATE = "pages_saved_state";
// Callbacks
private final LoaderCallbacks mLoaderCallbacks = new LoaderCallbacks();
private Callback mCallback;
// Useful objects
private ThreadClient mThreadClient;
// Internal arguments
private boolean mAddExtraDecor;
private ThreadLoaderStrategy mThreadLoadStrategy;
private ThreadClickStrategy mThreadClickStrategy;
// External Arguments
private int mForumId;
private String mForumTitle;
private String mParentForumTitle;
private List<String> mHierarchy;
// Views
private XDARefreshLayout mRefreshLayout;
private RecyclerView mRecyclerView;
// View helpers
private ActionModeHelper mModeHelper;
private ThreadEventHelper mThreadEventHelper;
private InfiniteRecyclerLoadHelper mInfiniteScrollListener;
// Adapters
private ThreadAdapter mAdapter;
private HierarchySpinnerAdapter mSpinnerAdapter;
// Data
private int mTotalPages;
public static ThreadFragment createDefault(final int forumId, final String forumTitle,
final String parentForumTitle, final ArrayList<String> hierarchy) {
final Bundle bundle = new Bundle();
// Internal use
bundle.putBoolean(ADD_EXTRA_DECOR_ARGUMENT, true);
bundle.putParcelable(THREAD_LOAD_STRATEGY_ARGUMENT, new DefaultThreadLoaderStrategy());
bundle.putParcelable(THREAD_CLICK_STRATEGY_ARGUMENT, new FirstThreadClickStrategy());
// From external
bundle.putInt(FORUM_ID_ARGUMENT, forumId);
bundle.putString(FORUM_TITLE_ARGUMENT, forumTitle);
bundle.putString(PARENT_FORUM_TITLE_ARGUMENT, parentForumTitle);
bundle.putStringArrayList(FORUM_HIERARCHY_ARGUMENT, hierarchy);
final ThreadFragment threadFragment = new ThreadFragment();
threadFragment.setArguments(bundle);
return threadFragment;
}
public static ThreadFragment createSubscribed() {
final Bundle bundle = new Bundle();
// All are from internal
bundle.putBoolean(ADD_EXTRA_DECOR_ARGUMENT, false);
bundle.putParcelable(THREAD_LOAD_STRATEGY_ARGUMENT, new SubscribedThreadLoaderStrategy());
bundle.putParcelable(THREAD_CLICK_STRATEGY_ARGUMENT, new UnreadThreadClickStrategy());
bundle.putStringArrayList(FORUM_HIERARCHY_ARGUMENT, new ArrayList<String>());
final ThreadFragment threadFragment = new ThreadFragment();
threadFragment.setArguments(bundle);
return threadFragment;
}
public static ThreadFragment createParticipated() {
final Bundle bundle = new Bundle();
// All are from internal
bundle.putBoolean(ADD_EXTRA_DECOR_ARGUMENT, false);
bundle.putParcelable(THREAD_LOAD_STRATEGY_ARGUMENT, new ParticipatedThreadLoaderStrategy());
bundle.putParcelable(THREAD_CLICK_STRATEGY_ARGUMENT, new UnreadThreadClickStrategy());
bundle.putString(FORUM_TITLE_ARGUMENT, "Participated");
bundle.putStringArrayList(FORUM_HIERARCHY_ARGUMENT, new ArrayList<String>());
final ThreadFragment threadFragment = new ThreadFragment();
threadFragment.setArguments(bundle);
return threadFragment;
}
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
mCallback = (Callback) activity;
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mThreadClient = RetrofitThreadClient.getClient(getActivity());
// Retrieve the arguments from the given bundle
// Internal arguments
mAddExtraDecor = getArguments().getBoolean(ADD_EXTRA_DECOR_ARGUMENT, true);
mThreadLoadStrategy = getArguments().getParcelable(THREAD_LOAD_STRATEGY_ARGUMENT);
mThreadClickStrategy = getArguments().getParcelable(THREAD_CLICK_STRATEGY_ARGUMENT);
// From external
mForumId = getArguments().getInt(FORUM_ID_ARGUMENT, 0);
mForumTitle = getArguments().getString(FORUM_TITLE_ARGUMENT, null);
mParentForumTitle = getArguments().getString(PARENT_FORUM_TITLE_ARGUMENT, null);
mHierarchy = getArguments().getStringArrayList(FORUM_HIERARCHY_ARGUMENT);
final ThreadActionModeHelper helper =
new ThreadActionModeHelper(getActivity(), mThreadClient);
mModeHelper = new ActionModeHelper(getActivity(), helper,
new ThreadClickListener(), ActionModeHelper.SelectionMode.SINGLE);
mAdapter = new ThreadAdapter(getActivity(), mModeHelper, mModeHelper, mModeHelper);
mSpinnerAdapter = new HierarchySpinnerAdapter(getActivity(),
LayoutInflater.from(getActivity()), mHierarchy, getFragmentManager());
mThreadEventHelper = new ThreadEventHelper(getActivity(), mAdapter);
helper.setAdapter(mAdapter);
helper.setModeHelper(mModeHelper);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.thread_fragment, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
setHasOptionsMenu(true);
mThreadClient.getBus().register(mThreadEventHelper);
// Setup views
setupRefreshLayout(view);
setupRecyclerView(view);
setupAddThreadButton(view);
setupActionBar();
// Tell helpers about RecyclerView
mModeHelper.setRecyclerView(mRecyclerView);
if (mInfiniteScrollListener != null) {
mInfiniteScrollListener.updateRecyclerView(mRecyclerView);
}
// Start loading data if necessary
if (!mAdapter.isEmpty()) {
return;
}
if (savedInstanceState == null) {
loadTheFirstPage();
} else {
final List<AugmentedUnifiedThread> threads = savedInstanceState
.getParcelableArrayList(THREADS_SAVED_STATE);
if (Utils.isCollectionEmpty(threads)) {
// threads being empty implies a save state call came through when we were loading
// our data for the first time
loadTheFirstPage();
} else {
// This should give a non-zero integer
mTotalPages = savedInstanceState.getInt(PAGES_SAVED_STATE);
mInfiniteScrollListener = new InfiniteRecyclerLoadHelper(mRecyclerView,
new InfiniteLoadCallback(),
mTotalPages, null);
addDataToAdapter(threads);
}
}
}
private void setupRefreshLayout(final View view) {
mRefreshLayout = (XDARefreshLayout) view.findViewById(R.id.swipe_refresh_layout);
mRefreshLayout.setXDAColourScheme();
mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
UIUtils.updateEmptyViewState(getView(), mRecyclerView, mAdapter.isEmpty());
reloadTheFirstPage();
}
});
}
private void setupRecyclerView(final View view) {
mRecyclerView = (RecyclerView) view.findViewById(android.R.id.list);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new XDALinerLayoutManager(getActivity()));
ViewCompat.setOverScrollMode(mRecyclerView, ViewCompat.OVER_SCROLL_NEVER);
}
private void setupAddThreadButton(final View view) {
final FloatingActionButton button = (FloatingActionButton) view
.findViewById(R.id.thread_fragment_create_thread);
if (!mAddExtraDecor) {
button.setVisibility(View.GONE);
return;
}
button.setOnClickListener(new CreateThreadListener());
if (CompatUtils.hasLollipop()) {
final Drawable drawable = getResources().getDrawable(R.drawable.fab_background);
CompatUtils.setBackground(button, drawable);
} else {
final int color = getResources().getColor(R.color.fab_color);
button.setBackgroundColor(color);
}
}
private void setupActionBar() {
final ActionBar actionBar = UIUtils.getSupportActionBar(getActivity());
if (mForumTitle != null) {
actionBar.setTitle(mForumTitle.isEmpty() ? null : mForumTitle);
}
if (mParentForumTitle != null) {
actionBar.setSubtitle(mParentForumTitle.isEmpty() ? null : mParentForumTitle);
}
if (!mAddExtraDecor) {
return;
}
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setListNavigationCallbacks(mSpinnerAdapter, mSpinnerAdapter);
actionBar.setSelectedNavigationItem(mSpinnerAdapter.getCount() - 1);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mThreadClient.getBus().unregister(mThreadEventHelper);
}
@Override
public void onViewStateRestored(@Nullable final Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
mModeHelper.restoreInstanceState(savedInstanceState);
}
@Override
public void onSaveInstanceState(final Bundle state) {
super.onSaveInstanceState(state);
mModeHelper.saveInstanceState(state);
state.putParcelableArrayList(THREADS_SAVED_STATE, new ArrayList<>(mAdapter.getThreads()));
state.putInt(PAGES_SAVED_STATE, mTotalPages);
}
private void loadTheFirstPage() {
final Bundle bundle = new Bundle();
bundle.putInt(CURRENT_PAGE_LOADER_ARGUMENT, 1);
getLoaderManager().initLoader(0, bundle, mLoaderCallbacks);
}
private void reloadTheFirstPage() {
final Bundle bundle = new Bundle();
bundle.putInt(CURRENT_PAGE_LOADER_ARGUMENT, 1);
getLoaderManager().restartLoader(0, bundle, mLoaderCallbacks);
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == 1) {
UIUtils.updateEmptyViewState(getView(), mRecyclerView, true);
reloadTheFirstPage();
} else if (requestCode == 101) {
final UnifiedThread thread = data.getParcelableExtra("thread");
updateThread(thread);
}
}
}
private void updateThread(final UnifiedThread thread) {
final AugmentedUnifiedThread unifiedThread = new AugmentedUnifiedThread(thread,
getActivity());
final int position = mAdapter.indexOf(unifiedThread);
mAdapter.updateThread(position, unifiedThread);
}
private void addDataToAdapter(final List<AugmentedUnifiedThread> data) {
UIUtils.updateEmptyViewState(getView(), mRecyclerView, data == null ? 0 : data.size());
// Let's actually add the items now
mAdapter.addAll(data);
mRefreshLayout.setRefreshing(false);
}
public interface Callback {
public void login(final Runnable runnable);
}
private class InfiniteLoadCallback implements InfiniteRecyclerLoadHelper.Callback {
@Override
public void loadMoreData(final int page) {
final Bundle bundle = new Bundle();
bundle.putInt(CURRENT_PAGE_LOADER_ARGUMENT, page);
getLoaderManager().restartLoader(0, bundle, mLoaderCallbacks);
}
}
private class ThreadClickListener implements View.OnClickListener {
@Override
public void onClick(final View view) {
final int position = mRecyclerView.getChildPosition(view);
if (position == RecyclerView.NO_POSITION) {
return;
}
final AugmentedUnifiedThread thread = mAdapter.getThread(position);
mThreadClickStrategy.onClick(ThreadFragment.this, mHierarchy, thread);
}
}
private class CreateThreadListener implements View.OnClickListener {
@Override
public void onClick(final View v) {
if (AccountUtils.isAccountAvailable(getActivity())) {
createNewThread();
} else {
mCallback.login(new Runnable() {
@Override
public void run() {
createNewThread();
}
});
}
}
private void createNewThread() {
final DialogFragment fragment = CreateThreadFragment.createInstance(mForumId);
fragment.setTargetFragment(ThreadFragment.this, CREATE_THREAD_REQUEST_CODE);
fragment.show(getFragmentManager(), "createThread");
}
}
private class LoaderCallbacks
implements LoaderManager.LoaderCallbacks<AugmentedUnifiedThreadContainer> {
@Override
public Loader<AugmentedUnifiedThreadContainer> onCreateLoader(int id, Bundle bundle) {
return mThreadLoadStrategy.createLoader(getActivity(), mForumId,
bundle.getInt(CURRENT_PAGE_LOADER_ARGUMENT));
}
@Override
public void onLoadFinished(final Loader<AugmentedUnifiedThreadContainer> loader,
final AugmentedUnifiedThreadContainer data) {
if (data == null) {
// TODO - we need to tailor this to lack of connection/other network issue
addDataToAdapter(null);
return;
}
if (mInfiniteScrollListener != null && !mInfiniteScrollListener.isLoading()
&& !mRefreshLayout.isRefreshing()) {
// This may happen when we are coming back from posts fragment to threads. For some
// reason loadFinished gets called. However, we may have new data about the thread -
// don't disturb this data.
UIUtils.updateEmptyViewState(getView(), mRecyclerView, false);
mRecyclerView.setOnScrollListener(mInfiniteScrollListener);
return;
} else if (data.getCurrentPage() == 1 || mInfiniteScrollListener == null) {
mAdapter.clear();
mTotalPages = data.getTotalPages();
mInfiniteScrollListener = new InfiniteRecyclerLoadHelper(mRecyclerView,
new InfiniteLoadCallback(), mTotalPages, null);
}
mInfiniteScrollListener.onLoadFinished();
addDataToAdapter(data.getThreads());
if (!mInfiniteScrollListener.hasMoreData()) {
mAdapter.removeFooter();
}
}
@Override
public void onLoaderReset(Loader<AugmentedUnifiedThreadContainer> loader) {
}
}
}