package io.github.droidkaigi.confsched.fragment; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.ViewPager; import android.util.Log; 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 org.parceler.Parcels; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.inject.Inject; import io.github.droidkaigi.confsched.R; import io.github.droidkaigi.confsched.activity.ActivityNavigator; import io.github.droidkaigi.confsched.activity.SearchActivity; import io.github.droidkaigi.confsched.api.DroidKaigiClient; import io.github.droidkaigi.confsched.dao.SessionDao; import io.github.droidkaigi.confsched.databinding.FragmentSessionsBinding; import io.github.droidkaigi.confsched.model.MainContentStateBrokerProvider; import io.github.droidkaigi.confsched.model.Page; import io.github.droidkaigi.confsched.model.Session; import io.github.droidkaigi.confsched.util.DateUtil; import io.github.droidkaigi.confsched.util.LocaleUtil; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; import rx.subscriptions.CompositeSubscription; public class SessionsFragment extends BaseFragment implements StackedPageListener { public static final String TAG = SessionsFragment.class.getSimpleName(); private static final String ARG_SHOULD_REFRESH = "should_refresh"; private static final int REQ_SEARCH = 2; @Inject DroidKaigiClient client; @Inject SessionDao dao; @Inject CompositeSubscription compositeSubscription; @Inject ActivityNavigator activityNavigator; @Inject MainContentStateBrokerProvider brokerProvider; private SessionsPagerAdapter adapter; private FragmentSessionsBinding binding; private boolean shouldRefresh; private OnChangeSessionListener onChangeSessionListener = session -> { /*no op*/ }; public static SessionsFragment newInstance() { return newInstance(false); } public static SessionsFragment newInstance(boolean shouldRefresh) { SessionsFragment fragment = new SessionsFragment(); Bundle args = new Bundle(); args.putBoolean(ARG_SHOULD_REFRESH, shouldRefresh); fragment.setArguments(args); return fragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentSessionsBinding.inflate(inflater, container, false); setHasOptionsMenu(true); initEmptyView(); compositeSubscription.add(loadData()); compositeSubscription.add(fetchAndSave()); return binding.getRoot(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { this.shouldRefresh = getArguments().getBoolean(ARG_SHOULD_REFRESH, false); } } @Override public void onAttach(Context context) { super.onAttach(context); getComponent().inject(this); if (context instanceof OnChangeSessionListener) { onChangeSessionListener = (OnChangeSessionListener) context; } } private void initEmptyView() { binding.emptyViewButton.setOnClickListener(v -> brokerProvider.get().set(Page.ALL_SESSIONS)); } private Subscription fetchAndSave() { return client.getSessions(LocaleUtil.getCurrentLanguageId(getActivity())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( dao::updateAllAsync, throwable -> Log.e(TAG, "Failed to fetchAndSave.", throwable) ); } protected Subscription loadData() { showLoadingView(); Observable<List<Session>> cachedSessions = dao.findAll(); return cachedSessions.flatMap(sessions -> { if (shouldRefresh || sessions.isEmpty()) { return client.getSessions(LocaleUtil.getCurrentLanguageId(getActivity())) .doOnNext(dao::updateAllAsync); } else { return Observable.just(sessions); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( this::onLoadDataSuccess, this::onLoadDataFailure ); } private void onLoadDataSuccess(List<Session> sessions) { Log.i(TAG, "Sessions Load succeeded."); // TODO This is temporary bug fix. https://github.com/konifar/droidkaigi2016/issues/264 if (shouldRefresh) { sessions = dao.findAll().toBlocking().single(); } groupByDateSessions(sessions); } private void onLoadDataFailure(Throwable throwable) { Log.e(TAG, "Sessions Load failed", throwable); Snackbar.make(binding.containerMain, R.string.sessions_network_error, Snackbar.LENGTH_LONG).show(); } protected void showEmptyView() { binding.emptyView.setVisibility(View.VISIBLE); } protected void hideEmptyView() { binding.emptyView.setVisibility(View.GONE); } protected void showLoadingView() { binding.progressBarContainer.setVisibility(View.VISIBLE); } protected void hideLoadingView() { binding.progressBarContainer.setVisibility(View.GONE); } protected void groupByDateSessions(List<Session> sessions) { Map<String, List<Session>> sessionsByDate = new TreeMap<>(); for (Session session : sessions) { String key = DateUtil.getMonthDate(session.stime, getActivity()); if (sessionsByDate.containsKey(key)) { sessionsByDate.get(key).add(session); } else { List<Session> list = new ArrayList<>(); list.add(session); sessionsByDate.put(key, list); } } adapter = new SessionsPagerAdapter(getFragmentManager()); for (Map.Entry<String, List<Session>> e : sessionsByDate.entrySet()) { addFragment(e.getKey(), e.getValue()); } binding.viewPager.setAdapter(adapter); binding.tabLayout.setupWithViewPager(binding.viewPager); binding.tabLayout.setOnTabSelectedListener(new CustomViewPagerOnTabSelectedListener(binding.viewPager)); hideLoadingView(); if (sessions.isEmpty()) { showEmptyView(); } else { hideEmptyView(); } } protected SessionsTabFragment createTabFragment(List<Session> sessions) { return SessionsTabFragment.newInstance(sessions); } private void addFragment(String title, List<Session> sessions) { SessionsTabFragment fragment = createTabFragment(sessions); adapter.add(title, fragment); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { menuInflater.inflate(R.menu.menu_sessions, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.item_search: activityNavigator.showSearch(this, REQ_SEARCH); break; } return super.onOptionsItemSelected(item); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode != REQ_SEARCH) { return; } if (resultCode != Activity.RESULT_OK) { return; } List<Session> statusChangedSession = Parcels .unwrap(data.getParcelableExtra(SearchActivity.RESULT_STATUS_CHANGED_SESSIONS)); if (statusChangedSession == null || statusChangedSession.isEmpty()) { return; } onChangeSessionListener.onChangeSession(statusChangedSession); compositeSubscription.add(loadData()); } @Override public void onTop() { compositeSubscription.add(loadData()); } public interface OnChangeSessionListener { void onChangeSession(List<Session> sessions); } private class SessionsPagerAdapter extends FragmentStatePagerAdapter { private List<SessionsTabFragment> fragments = new ArrayList<>(); private List<String> titles = new ArrayList<>(); public SessionsPagerAdapter(FragmentManager fm) { super(fm); } @Override @Nullable public Fragment getItem(int position) { if (position >= 0 && position < fragments.size()) { return fragments.get(position); } return null; } @Override public int getCount() { return fragments.size(); } @Override public CharSequence getPageTitle(int position) { return titles.get(position); } public void add(String title, SessionsTabFragment fragment) { fragments.add(fragment); titles.add(title); notifyDataSetChanged(); } } private class CustomViewPagerOnTabSelectedListener extends TabLayout.ViewPagerOnTabSelectedListener { public CustomViewPagerOnTabSelectedListener(ViewPager viewPager) { super(viewPager); } @Override public void onTabReselected(TabLayout.Tab tab) { super.onTabReselected(tab); SessionsTabFragment fragment = (SessionsTabFragment) adapter.getItem(tab.getPosition()); if (fragment != null) { fragment.scrollUpToTop(); } } } }