package com.orgzly.android.ui.fragments;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.ActionMode;
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.widget.AbsListView;
import android.widget.ListView;
import android.widget.ViewFlipper;
import com.orgzly.BuildConfig;
import com.orgzly.R;
import com.orgzly.android.provider.ProviderContract;
import com.orgzly.android.provider.clients.FiltersClient;
import com.orgzly.android.ui.Fab;
import com.orgzly.android.ui.FragmentListener;
import com.orgzly.android.ui.Loaders;
import com.orgzly.android.ui.util.ListViewUtils;
import com.orgzly.android.util.LogUtils;
import java.util.Set;
/**
* Displays and allows modifying saved filters.
*/
public class FiltersFragment extends ListFragment implements Fab, LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = FiltersFragment.class.getName();
/** Name used for {@link android.app.FragmentManager}. */
public static final String FRAGMENT_TAG = FiltersFragment.class.getName();
private SimpleCursorAdapter mListAdapter;
private FiltersFragmentListener mListener;
private boolean mIsViewCreated = false;
private ViewFlipper mViewFlipper;
private ActionMode mActionMode;
public static FiltersFragment getInstance() {
return new FiltersFragment();
}
/**
* Mandatory empty constructor for the fragment manager to instantiate the
* fragment (e.g. upon screen orientation changes).
*/
public FiltersFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupAdapter();
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
/* This makes sure that the container activity has implemented
* the callback interface. If not, it throws an exception
*/
try {
mListener = (FiltersFragmentListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(getActivity().toString() + " must implement " + FiltersFragmentListener.class);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_filters, container, false);
mViewFlipper = (ViewFlipper) view.findViewById(R.id.fragment_filters_flipper);
return view;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mIsViewCreated = true;
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
getListView().setMultiChoiceModeListener(new MyActionMode());
}
@Override
public void onDestroyView() {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG);
super.onDestroyView();
mIsViewCreated = false;
if (mActionMode != null) {
mActionMode.finish();
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
if (mListener != null) {
mListener.onFilterEditRequest(id);
}
}
private void setupAdapter() {
/* Create adapter using Cursor. */
mListAdapter = createFilterCursorAdapter(getActivity());
setListAdapter(mListAdapter);
}
public static SimpleCursorAdapter createFilterCursorAdapter(Context context) {
/* Column field names to be bound. */
String[] columns = new String[] {
ProviderContract.Filters.Param.NAME,
ProviderContract.Filters.Param.QUERY,
ProviderContract.Filters.Param.POSITION,
};
/* Views which the data will be bound to. */
int[] to = new int[] {
R.id.item_filter_name,
R.id.item_filter_query,
R.id.item_filter_position,
};
/* Create adapter using Cursor. */
return new SimpleCursorAdapter(
context,
R.layout.item_filter,
null,
columns,
to,
0);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, savedInstanceState);
super.onActivityCreated(savedInstanceState);
getActivity().getSupportLoaderManager().initLoader(Loaders.FILTERS_FRAGMENT, null, this);
}
@Override
public void onResume() {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG);
super.onResume();
announceChangesToActivity();
}
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
return FiltersClient.getCursorLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
if (mIsViewCreated) {
/**
* Swapping instead of changing Cursor here, to keep the old one open.
* Loader should release the old Cursor - see note in
* {@link LoaderManager.LoaderCallbacks#onLoadFinished).
*/
mListAdapter.swapCursor(cursor);
if (mListAdapter.getCount() > 0) {
mViewFlipper.setDisplayedChild(0);
} else {
mViewFlipper.setDisplayedChild(1);
}
}
}
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
if (mIsViewCreated) {
mListAdapter.changeCursor(null);
}
}
@Override
public Runnable getFabAction() {
return new Runnable() {
@Override
public void run() {
mListener.onFilterNewRequest();
}
};
}
private void announceChangesToActivity() {
if (mListener != null) {
mListener.announceChanges(
FRAGMENT_TAG,
getString(R.string.searches),
null,
getListView().getCheckedItemCount());
}
}
public class MyActionMode implements AbsListView.MultiChoiceModeListener {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, getListView().getCheckedItemCount());
mActionMode = mode;
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.filters_cab, menu);
/* Needed for after orientation change. */
mode.setTitle(String.valueOf(getListView().getCheckedItemCount()));
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, getListView().getCheckedItemCount());
if (mode.getTag() != null) {
menu.findItem(R.id.filters_cab_move_up).setVisible(false);
menu.findItem(R.id.filters_cab_move_down).setVisible(false);
} else {
menu.findItem(R.id.filters_cab_move_up).setVisible(true);
menu.findItem(R.id.filters_cab_move_up).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
menu.findItem(R.id.filters_cab_move_down).setVisible(true);
menu.findItem(R.id.filters_cab_move_down).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
}
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
Set<Long> ids = ListViewUtils.getCheckedIds(getListView());
switch (item.getItemId()) {
case R.id.filters_cab_move_up:
mListener.onFilterMoveUpRequest(ids.iterator().next());
break;
case R.id.filters_cab_move_down:
mListener.onFilterMoveDownRequest(ids.iterator().next());
break;
case R.id.filters_cab_delete:
mListener.onFilterDeleteRequest(ids);
/* Close action mode. */
mode.finish();
break;
default:
return false; /* Not handled. */
}
return true; /* Handled. */
}
@Override
public void onDestroyActionMode(ActionMode mode) {
getListView().clearChoices();
announceChangesToActivity();
mActionMode = null;
}
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
mode.setTitle(String.valueOf(getListView().getCheckedItemCount()));
/*
* Request onPrepareActionMode to be called if actions for
* repositioning need to be added to removed.
*/
if (getListView().getCheckedItemCount() > 1) {
if (mode.getTag() == null) { /* Filter repositioning actions exist. */
mode.setTag(new Object());
mode.invalidate();
}
} else {
if (mode.getTag() != null) { /* Filter repositioning actions do not exist. */
mode.setTag(null);
mode.invalidate();
}
}
announceChangesToActivity();
}
}
public interface FiltersFragmentListener extends FragmentListener {
void onFilterNewRequest();
void onFilterDeleteRequest(Set<Long> ids);
void onFilterEditRequest(long id);
void onFilterMoveUpRequest(long id);
void onFilterMoveDownRequest(long id);
}
}