/* * Copyright (C) 2010-2016 Stichting Akvo (Akvo Foundation) * * This file is part of Akvo Flow. * * Akvo Flow is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Akvo Flow is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Akvo Flow. If not, see <http://www.gnu.org/licenses/>. */ package org.akvo.flow.ui.fragment; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.content.LocalBroadcastManager; 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.SubMenu; import android.view.View; import android.view.ViewGroup; import org.akvo.flow.R; import org.akvo.flow.activity.SurveyActivity; import org.akvo.flow.data.database.SurveyDbAdapter; import org.akvo.flow.domain.SurveyGroup; import org.akvo.flow.util.ConstantUtil; import java.lang.ref.WeakReference; import java.util.Map; import java.util.WeakHashMap; public class DatapointsFragment extends Fragment { private static final String TAG = DatapointsFragment.class.getSimpleName(); private static final int POSITION_LIST = 0; private static final int POSITION_MAP = 1; private static final String STATS_DIALOG_FRAGMENT_TAG = "stats"; /** * BroadcastReceiver to notify of records synchronisation. This should be * fired from SurveyedLocalesSyncService. */ private final BroadcastReceiver mSurveyedLocalesSyncReceiver = new DataPointSyncBroadcastReceiver( this); private SurveyDbAdapter mDatabase; private TabsAdapter mTabsAdapter; private ViewPager mPager; private SurveyGroup mSurveyGroup; @Nullable private DatapointFragmentListener listener; private String[] tabNames; public DatapointsFragment() { } public static DatapointsFragment newInstance(SurveyGroup surveyGroup) { DatapointsFragment fragment = new DatapointsFragment(); Bundle args = new Bundle(); args.putSerializable(SurveyActivity.EXTRA_SURVEY_GROUP, surveyGroup); fragment.setArguments(args); return fragment; } @Override public void onAttach(Activity activity) { super.onAttach(activity); if (!(activity instanceof DatapointFragmentListener)) { throw new IllegalArgumentException("Activity must implement DatapointFragmentListener"); } this.listener = (DatapointFragmentListener) activity; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mSurveyGroup = (SurveyGroup) getArguments() .getSerializable(SurveyActivity.EXTRA_SURVEY_GROUP); tabNames = getResources().getStringArray(R.array.records_activity_tabs); setHasOptionsMenu(true); setRetainInstance(true); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (mDatabase == null) { mDatabase = new SurveyDbAdapter(getActivity()); mDatabase.open(); } } @Override public void onDetach() { super.onDetach(); this.listener = null; } @Override public void onDestroy() { super.onDestroy(); mDatabase.close(); } @Override public void onResume() { super.onResume(); // Delete empty Records, if any // TODO: For a more efficient cleanup, attempt to wipe ONLY the latest Record, // TODO: providing the id to RecordActivity, and reading it back on onActivityResult(...) mDatabase.deleteEmptyRecords(); LocalBroadcastManager.getInstance(getActivity()) .registerReceiver(mSurveyedLocalesSyncReceiver, new IntentFilter(ConstantUtil.ACTION_LOCALE_SYNC)); } @Override public void onPause() { super.onPause(); LocalBroadcastManager.getInstance(getActivity()) .unregisterReceiver(mSurveyedLocalesSyncReceiver); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.datapoints_fragment, container, false); mPager = (ViewPager) v.findViewById(R.id.pager); TabLayout tabs = (TabLayout) v.findViewById(R.id.tabs); mTabsAdapter = new TabsAdapter(getFragmentManager(), tabNames, mSurveyGroup); mPager.setAdapter(mTabsAdapter); tabs.setupWithViewPager(mPager); return v; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { if (mSurveyGroup != null) { inflater.inflate(R.menu.datapoints_fragment, menu); SubMenu subMenu = menu.findItem(R.id.more_submenu).getSubMenu(); if (!mSurveyGroup.isMonitored()) { subMenu.removeItem(R.id.sync_records); } // "Order By" is only available for the ListFragment, not the MapFragment. // The navigation components maintain 2 different indexes: Tab index and Pager index. // The system seems to always update the tab index first, prior to the onCreateOptionsMenu // call (either selecting the Tab or swiping the Pager). For this reason, we need to check // the Tab index, not the Pager one, which turns out to be buggy in some Android versions. // TODO: If this approach is still unreliable, we'll need to invalidate the menu twice. if (mPager != null && mPager.getCurrentItem() == POSITION_MAP) { //TODO: maybe instead of removing we should use custom menu for each fragment subMenu.removeItem(R.id.order_by); } } super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.new_datapoint: if (listener != null) { String newLocaleId = mDatabase.createSurveyedLocale(mSurveyGroup.getId()); listener.onRecordSelected(newLocaleId); } return true; case R.id.search: if (listener != null) { return listener.onSearchTap(); } case R.id.sync_records: if (listener != null && mSurveyGroup != null) { listener.onSyncRecordsTap(mSurveyGroup.getId()); } return true; case R.id.stats: StatsDialogFragment dialogFragment = StatsDialogFragment .newInstance(mSurveyGroup.getId()); dialogFragment.show(getFragmentManager(), STATS_DIALOG_FRAGMENT_TAG); return true; default: return super.onOptionsItemSelected(item); } } private static class TabsAdapter extends FragmentPagerAdapter { private final String[] tabs; private SurveyGroup surveyGroup; private final Map<Integer, Fragment> fragmentsRef = new WeakHashMap<>(2); public TabsAdapter(FragmentManager fm, String[] tabs, SurveyGroup surveyGroup) { super(fm); this.tabs = tabs; this.surveyGroup = surveyGroup; } @Override public int getCount() { return tabs.length; } public void refreshFragments(SurveyGroup newSurveyGroup) { this.surveyGroup = newSurveyGroup; SurveyedLocaleListFragment listFragment = (SurveyedLocaleListFragment) fragmentsRef .get(POSITION_LIST); MapFragment mapFragment = (MapFragment) fragmentsRef.get(POSITION_MAP); if (listFragment != null) { listFragment.refresh(surveyGroup); } if (mapFragment != null) { mapFragment.refresh(surveyGroup); } } @Override public Object instantiateItem(ViewGroup container, int position) { if (position == POSITION_LIST) { SurveyedLocaleListFragment surveyedLocaleListFragment = (SurveyedLocaleListFragment) super .instantiateItem(container, position); fragmentsRef.put(POSITION_LIST, surveyedLocaleListFragment); return surveyedLocaleListFragment; } else { MapFragment mapFragment = (MapFragment) super.instantiateItem(container, position); fragmentsRef.put(POSITION_MAP, mapFragment); return mapFragment; } } @Override public Fragment getItem(int position) { if (position == POSITION_LIST) { return SurveyedLocaleListFragment.newInstance(surveyGroup); } // Map mode return MapFragment.newInstance(surveyGroup, null); } @Override public CharSequence getPageTitle(int position) { return tabs[position]; } } public void refresh(SurveyGroup surveyGroup) { mSurveyGroup = surveyGroup; refreshView(); } private void refreshView() { if (mTabsAdapter != null) { mTabsAdapter.refreshFragments(mSurveyGroup); } if (listener != null) { listener.refreshMenu(); } } private static class DataPointSyncBroadcastReceiver extends BroadcastReceiver { private final WeakReference<DatapointsFragment> fragmentWeakReference; private DataPointSyncBroadcastReceiver(DatapointsFragment fragment) { this.fragmentWeakReference = new WeakReference<>(fragment); } @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "New Records have been synchronised. Refreshing fragments..."); DatapointsFragment datapointsFragment = fragmentWeakReference.get(); if (datapointsFragment != null) { datapointsFragment.refreshView(); } } } public interface DatapointFragmentListener { void refreshMenu(); void onRecordSelected(String recordId); boolean onSearchTap(); void onSyncRecordsTap(long surveyGroupId); } }