package com.tevinjeffrey.rutgersct.ui.course; import android.app.FragmentTransaction; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.transition.ChangeBounds; import android.transition.Fade; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.TextView; import com.google.gson.JsonParseException; import com.nispok.snackbar.Snackbar; import com.nispok.snackbar.SnackbarManager; import com.nispok.snackbar.enums.SnackbarType; import com.nispok.snackbar.listeners.ActionClickListener; import com.nispok.snackbar.listeners.ActionSwipeListener; import com.nispok.snackbar.listeners.EventListener; import com.tevinjeffrey.rutgersct.R; import com.tevinjeffrey.rutgersct.RutgersCTApp; import com.tevinjeffrey.rutgersct.ui.subject.SubjectView; import com.tevinjeffrey.rutgersct.ui.utils.ItemClickListener; import com.tevinjeffrey.rutgersct.rutgersapi.RetroRutgers; import com.tevinjeffrey.rutgersct.rutgersapi.model.Course; import com.tevinjeffrey.rutgersct.rutgersapi.model.Request; import com.tevinjeffrey.rutgersct.rutgersapi.model.Subject; import com.tevinjeffrey.rutgersct.ui.base.MVPFragment; import com.tevinjeffrey.rutgersct.ui.courseinfo.CourseInfoFragment; import com.tevinjeffrey.rutgersct.ui.trackedsections.TrackedSectionsView; import com.tevinjeffrey.rutgersct.utils.Utils; import com.tevinjeffrey.rutgersct.rutgersapi.exceptions.RutgersServerIOException; import org.apache.commons.lang3.text.WordUtils; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import icepick.Icicle; import timber.log.Timber; @SuppressWarnings("ClassWithTooManyMethods") public class CourseFragment extends MVPFragment implements CourseView, SwipeRefreshLayout.OnRefreshListener, ItemClickListener<Course, View> { private final String TAG = this.getClass().getSimpleName(); @Bind(R.id.toolbar) Toolbar mToolbar; @Bind(R.id.list_view) RecyclerView mRecyclerView; @Bind(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @Bind(R.id.error_view) ViewGroup mErrorView; @Icicle Request mRequest; @Icicle ArrayList<Course> mListDataset; @Icicle CourseViewState mViewState = new CourseViewState(); @Inject RetroRutgers mRetroRutgers; public CourseFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); mRequest = getArguments().getParcelable(TrackedSectionsView.REQUEST); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LayoutInflater themedInflator = inflater.cloneInContext(Utils.wrapContextTheme(getActivity(), R.style.RutgersCT)); final View rootView = themedInflator.inflate(R.layout.fragment_courses, container, false); ButterKnife.bind(this, rootView); return rootView; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); //Recreate presenter if necessary. if (mBasePresenter == null) { mBasePresenter = new CoursePresenterImpl(mRequest); RutgersCTApp.getObjectGraph(getParentActivity()).inject(mBasePresenter); } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mViewState.apply(this, savedInstanceState != null); //Attach view to presenter mBasePresenter.attachView(this); //Load data depending on if the view is currently refreshing if (mIsInitialLoad) { getPresenter().loadCourses(true); } else { //Silently refresh tracked sections if (!getPresenter().isLoading()) { getPresenter().loadCourses(false); } } } @Override public void onDestroyView() { super.onDestroyView(); ButterKnife.unbind(this); dismissSnackbar(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_refresh: onRefresh(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_fragment_main, menu); } @Override public void initToolbar() { setToolbarTitle(); setToolbar(mToolbar); } public void initSwipeLayout() { mSwipeRefreshLayout.setSize(SwipeRefreshLayout.DEFAULT); mSwipeRefreshLayout.setColorSchemeResources(R.color.red, R.color.green); mSwipeRefreshLayout.setOnRefreshListener(this); } public void initRecyclerView() { LinearLayoutManager layoutManager = new LinearLayoutManager(getParentActivity()); layoutManager.setOrientation(LinearLayoutManager.VERTICAL); layoutManager.setSmoothScrollbarEnabled(true); mRecyclerView.setLayoutManager(layoutManager); mRecyclerView.setHasFixedSize(true); if (mListDataset == null) { mListDataset = new ArrayList<>(10); } if (mRecyclerView.getAdapter() == null) { mRecyclerView.setAdapter(new CourseFragmentAdapter(mListDataset, this)); } } @Override public void showLoading(final boolean pullToRefresh) { mViewState.isRefreshing = pullToRefresh; mSwipeRefreshLayout.post(new Runnable() { @Override public void run() { if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setRefreshing(pullToRefresh); } } }); } @Override public void setData(List<Course> data) { mViewState.data = data; mListDataset.clear(); mListDataset.addAll(data); if (mRecyclerView.getAdapter() != null) { mRecyclerView.getAdapter().notifyDataSetChanged(); } } @Override public void showError(Throwable t) { String message; Resources resources = getContext().getResources(); if (t instanceof UnknownHostException) { message = resources.getString(R.string.no_internet); } else if (t instanceof JsonParseException || t instanceof RutgersServerIOException) { message = resources.getString(R.string.server_down); } else if (t instanceof SocketTimeoutException) { message = resources.getString(R.string.timed_out); } else { message = t.getMessage(); } //error message finalized, now save it. mViewState.errorMessage = message; //Show the error layout if there's nothing in the adpater to show. // Redirects the message that would usually be in the snackbar, to error layout. if (!adapterHasItems()) { showLayout(LayoutType.ERROR); TextView textViewMessage = ButterKnife.findById(mErrorView, R.id.text); textViewMessage.setText(message); } else { showSnackBar(message); } } @Override public void onItemClicked(Course course, View view) { Timber.i("Selected course: %s", course); startCourseInfoFragment(createArgs(mRequest, course)); } private boolean adapterHasItems() { return mRecyclerView.getAdapter().getItemCount() > 0; } public void showLayout(TrackedSectionsView.LayoutType type) { mViewState.layoutType = type; switch (type) { case ERROR: showRecyclerView(View.GONE); showErrorLayout(View.VISIBLE); //enableSwipeRefreshLayout(true); break; case LIST: showErrorLayout(View.GONE); showRecyclerView(View.VISIBLE); //enableSwipeRefreshLayout(true); break; default: throw new RuntimeException("Unknown type: " + type); } } private void showRecyclerView(int visibility) { if (mRecyclerView.getVisibility() != visibility) mRecyclerView.setVisibility(visibility); } private void showErrorLayout(int visibility) { if (mErrorView.getVisibility() != visibility) mErrorView.setVisibility(visibility); } private void showSnackBar(CharSequence message) { SnackbarManager.show( Snackbar.with(getParentActivity()) .type(SnackbarType.MULTI_LINE) .text(message) .actionLabel(R.string.retry)// text to display .actionListener(new ActionClickListener() { @Override public void onActionClicked(Snackbar snackbar) { onRefresh(); mViewState.snackBarShowing = false; } }) .swipeListener(new ActionSwipeListener() { @Override public void onSwipeToDismiss() { mViewState.snackBarShowing = false; } }) .actionColor(ContextCompat.getColor(getParentActivity(), android.R.color.white)) .color(ContextCompat.getColor(getParentActivity(), R.color.accent))// action button label color .duration(Snackbar.SnackbarDuration.LENGTH_INDEFINITE) .eventListener(new EventListener() { @Override public void onShow(Snackbar snackbar) { if (snackbar != null) { mViewState.snackBarShowing = true; } } @Override public void onShowByReplace(Snackbar snackbar) { } @Override public void onShown(Snackbar snackbar) { } @Override public void onDismiss(Snackbar snackbar) { } @Override public void onDismissByReplace(Snackbar snackbar) { } @Override public void onDismissed(Snackbar snackbar) { } }) , getParentActivity()); // activity where it is displayed } private void dismissSnackbar() { //It's only being dismissed to not leak the fragment if (SnackbarManager.getCurrentSnackbar() != null) { SnackbarManager.dismiss(); } } @OnClick(R.id.try_again) public void onTryAgainClick(View view) { onRefresh(); } @Override public void onRefresh() { getPresenter().loadCourses(true); } private void setToolbarTitle() { Subject selectedSubject = getArguments().getParcelable(SubjectView.SELECTED_SUBJECT); super.setToolbarTitle(mToolbar, (selectedSubject != null ? selectedSubject.getCode() : "") + " | " + WordUtils.capitalize(selectedSubject != null ? selectedSubject.getDescription().toLowerCase() : "")); } private CoursePresenter getPresenter() { return (CoursePresenter) mBasePresenter; } private void startCourseInfoFragment(Bundle b) { CourseInfoFragment courseInfoFragment = new CourseInfoFragment(); FragmentTransaction ft = getFragmentManager().beginTransaction(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ChangeBounds changeBoundsTransition = new ChangeBounds(); changeBoundsTransition.setInterpolator(new DecelerateInterpolator()); courseInfoFragment.setEnterTransition(new Fade(Fade.IN).setStartDelay(250)); courseInfoFragment.setReturnTransition(new Fade(Fade.OUT).setDuration(50)); courseInfoFragment.setAllowReturnTransitionOverlap(false); courseInfoFragment.setAllowEnterTransitionOverlap(false); courseInfoFragment.setSharedElementEnterTransition(changeBoundsTransition); courseInfoFragment.setSharedElementReturnTransition(changeBoundsTransition); ft.addSharedElement(mToolbar, getString(R.string.transition_name_tool_background)); } else { ft.setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit); } courseInfoFragment.setArguments(b); startFragment(this, courseInfoFragment, ft); } private Bundle createArgs(Request request, Course selectedCourse) { Bundle bundle = new Bundle(); selectedCourse.setRequest(request); bundle.putParcelable(SELECTED_COURSE, selectedCourse); return bundle; } @Override public String toString() { return TAG; } }