public abstract class AbstractDetailFragment extends Fragment {
    
    private static final String KEY_SCROLL_Y = "scroll_y";

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        if (savedInstanceState != null && savedInstanceState.containsKey(KEY_SCROLL_Y)) {
            int scrollY = savedInstanceState.getInt(KEY_SCROLL_Y);
            View scrollView = getActivity().findViewById(R.id.detail_scroll_view);
            scrollView.setScrollY(scrollY);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        View scrollView = getActivity().findViewById(R.id.detail_scroll_view);
        if (scrollView != null) {
            int scrollY = scrollView.getScrollY();
            outState.putInt(KEY_SCROLL_Y, scrollY);
        }
    }

} public abstract class AbstractListFragment extends ListFragment {
    
    protected TextView mEmptyView;

    /** The padding, in pixels, to apply to {@code mEmptyView}. */
    private static final int EMPTY_LIST_SIDE_PADDING = 100;

    /**
     * The serialization (saved instance state) Bundle key representing the
     * activated item position. Only used on tablets.
     */
    private static final String STATE_ACTIVATED_POSITION = "activated_position";

    /**
     * The current activated item position. Only used on tablets.
     */
    private int mActivatedPosition = ListView.INVALID_POSITION;

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        if (((MainActivity) getActivity()).isTwoPane()) {
            setActivateOnItemClick(true);
        }

        // Restore the previously serialized activated item position.
        if (savedInstanceState != null
                && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
            setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
        }

        // Create a progress bar to display while the list loads.
        ProgressBar progressBar = new ProgressBar(getActivity());
        FrameLayout.LayoutParams params
                = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
        progressBar.setLayoutParams(params);
        progressBar.setIndeterminate(true);
        getListView().setEmptyView(progressBar);
        ((ViewGroup) view).addView(progressBar);

        // Create a custom empty view to display after loading the list.
        mEmptyView = new TextView(getActivity());
        mEmptyView.setGravity(Gravity.CENTER);
        mEmptyView.setTextSize(25f);
        ((ViewGroup) view).addView(mEmptyView);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mActivatedPosition != ListView.INVALID_POSITION) {
            outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
        }
    }

    /**
     * Turns on activate-on-click mode. When this mode is on, list items will be
     * given the 'activated' state when touched.
     */
    protected void setActivateOnItemClick(boolean activateOnItemClick) {
        // When setting CHOICE_MODE_SINGLE, ListView will automatically
        // give items the 'activated' state when touched.
        int choiceMode;
        if (activateOnItemClick) {
            choiceMode = ListView.CHOICE_MODE_SINGLE;
        } else {
            choiceMode = ListView.CHOICE_MODE_NONE;
        }
        getListView().setChoiceMode(choiceMode);
    }

    /**
     * Sets this fragment's {@link ListView}'s activated position.
     * @param position the position to set as activated
     */
    protected void setActivatedPosition(int position) {
        if (position == ListView.INVALID_POSITION) {
            getListView().setItemChecked(mActivatedPosition, false);
        } else {
            getListView().setItemChecked(position, true);
        }
        mActivatedPosition = position;
    }

    /**
     * Replaces this fragment's {@link ListView}'s current empty view with a
     * special empty view containing a message explaining how to populate the
     * view.
     * @param stringId the resource ID of the string to display in the new
     *     empty view
     */
    protected void setNoItemsView(int stringId) {
        // We can't actually remove the current empty view, so we just make it invisible.
        getListView().getEmptyView().setVisibility(View.INVISIBLE);
        getListView().setEmptyView(mEmptyView);
        mEmptyView.setText(stringId);
        mEmptyView.setPadding(EMPTY_LIST_SIDE_PADDING, 0, EMPTY_LIST_SIDE_PADDING, 0);
    }

    /**
     * A callback interface that all activities containing this fragment must
     * implement. This mechanism allows activities to be notified of item
     * selections.
     */
    public interface Callbacks {
        /**
         * Callback for when an item has been selected.
         */
        void onItemSelected(String fragmentName);
    }
} public enum Mode {
    LEXICON_BROWSE(1, "lexicon_browse"),
    LEXICON_FAVORITES(2, "lexicon_favorites"),
    LEXICON_HISTORY(3, "lexicon_history"),
    SYNTAX_BROWSE(5, "syntax_browse"),
    SYNTAX_BOOKMARKS(6, "syntax_bookmarks");

    private final int mPosition;
    private final String mName;

    /**
     * Constructs a new enum constant.
     * @param position the navigation drawer position corresponding to this mode
     * @param name the name of this mode
     */
    Mode(int position, String name) {
        mPosition = position;
        mName = name;
    }

    @Override
    public String toString() {
        return mName;
    }

    /**
     * Returns the name of this mode.
     * <p>
     * This is just a duplicate of toString() for now, but it's included here in case we ever
     * want to make toString() return something other than mName.
     */
    public String getName() {
        return mName;
    }

    public int getPosition() {
        return mPosition;
    }

    /**
     * Returns the {@code Mode} corresponding to the specified navigation drawer position.
     * @param  position the navigation drawer position for which to search
     * @return the {@code Mode} corresponding to the specified position
     */
    public static Mode getModeFromPosition(int position) {
        for (Mode m : Mode.values()) {
            if (m.mPosition == position) {
                return m;
            }
        }
        throw new IllegalArgumentException("Invalid nav drawer position");
    }

    /**
     * Returns the {@code Mode} corresponding to the specified name.
     * @param  name the name for which to search
     * @return the {@code Mode} corresponding to the specified name
     */
    public static Mode getModeFromName(String name) {
        for (Mode m : Mode.values()) {
            if (m.mName.equals(name)) {
                return m;
            }
        }
        throw new IllegalArgumentException("Invalid name");
    }

    /**
     * Returns true if this is a lexicon mode.
     * @return true if this is a lexicon mode, or false otherwise
     */
    public boolean isLexiconMode() {
        return this.equals(LEXICON_BROWSE)
                || this.equals(LEXICON_FAVORITES)
                || this.equals(LEXICON_HISTORY);
    }

    /**
     * Returns trueif this is a syntax mode.
     * @return trueif this is a syntax mode, or false otherwise
     */
    public boolean isSyntaxMode() {
        return this.equals(SYNTAX_BROWSE) || this.equals(SYNTAX_BOOKMARKS);
    }
} public abstract class AbstractLexiconListFragment extends AbstractListFragment {

    private static final int NO_SELECTION = -1;

    protected int mSelectedLexiconId = NO_SELECTION;

    /**
     * @return the lexicon database ID of the selected item
     */
    public int getSelectedLexiconId() {
        return mSelectedLexiconId;
    }

    /**
     * Sets the selected item ID.
     * @param id the {@code ListView} position of the item to select
     */
    protected abstract void setSelectedLexiconItemId(int id);
} public class LexiconBrowseListFragment extends AbstractLexiconListFragment
        implements LoaderManager.LoaderCallbacks<Cursor> {

    public static final String NAME = "lexicon_browse";

    private static final String TAG = "LexiconBrowseListFragment";
    private static final String[] PROJECTION
            = new String[] {LexiconContract._ID, LexiconContract.COLUMN_GREEK_FULL_WORD,
                    LexiconContract.COLUMN_GREEK_LOWERCASE};
    private static final String SELECTION = "";
    private static final String[] SELECTION_ARGS = {};
    private static final String ORDER_BY = LexiconContract.COLUMN_GREEK_LOWERCASE + " ASC";

    private SimpleCursorAdapter mAdapter;

    /**
     * The fragment's current callback object, which is notified of list item
     * clicks.
     */
    private Callbacks mCallbacks = sDummyCallbacks;

    /**
     * A dummy implementation of the {@link Callbacks} interface that does
     * nothing. Used only when this fragment is not attached to an activity.
     */
    private static final Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(String fragmentName) {
        }
    };

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public LexiconBrowseListFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create and set list adapter.
        // TODO: Replace this with a more efficient adapter.
        String[] fromColumns = {LexiconContract.COLUMN_GREEK_FULL_WORD};
        int[] toViews = {android.R.id.text1};
        mAdapter = new SimpleCursorAdapter(getActivity(),
                R.layout.greek_simple_list_item_activated_1, null, fromColumns, toViews, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), LexiconContract.CONTENT_URI, PROJECTION, SELECTION,
                SELECTION_ARGS, ORDER_BY);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        getListView().setFastScrollEnabled(true);
        getListView().setFastScrollAlwaysVisible(true);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // Activities containing this fragment must implement its callbacks.
        if (!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }

        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();

        // Reset the active callbacks interface to the dummy implementation.
        mCallbacks = sDummyCallbacks;
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);

        Cursor cursor = (Cursor) mAdapter.getItem(position);
        int lexiconId = cursor.getInt(0);
        setSelectedLexiconItemId(lexiconId);
        mCallbacks.onItemSelected(NAME); // Positions are off by one from database ID.
    }

    @Override
    protected void setSelectedLexiconItemId(int id) {
        mSelectedLexiconId = id;
    }

    /**
     * Selects the specified item.
     * @param id the lexicon database ID of the item to select
     */
    public void selectItem(int id) {
        mSelectedLexiconId = id;
        int position = id - 1;
        setActivatedPosition(position);
        getListView().setSelection(position);
        mCallbacks.onItemSelected(NAME);
    }
} public class LexiconFavoritesListFragment extends AbstractLexiconListFragment
        implements LoaderManager.LoaderCallbacks<Cursor>{

    public static final String NAME = "lexicon_favorites";

    // Note that we have to select the lexicon ID in order to alphabetize; alphabetizing by word
    // doesn't work due to accented characters.
    private static final String[] PROJECTION = new String[] {AppDataContract.LexiconFavorites._ID,
            AppDataContract.LexiconFavorites.COLUMN_NAME_WORD,
            AppDataContract.LexiconFavorites.COLUMN_NAME_LEXICON_ID};
    private static final String SELECTION = "";
    private static final String[] SELECTION_ARGS = {};
    private static final String SORT_ORDER
            = AppDataContract.LexiconFavorites.COLUMN_NAME_LEXICON_ID + " ASC";

    private SimpleCursorAdapter mAdapter;

    /**
     * The serialization (saved instance state) Bundle key representing the
     * activated item position. Only used on tablets.
     */
    private static final String STATE_ACTIVATED_POSITION = "activated_position";

    /**
     * The fragment's current callback object, which is notified of list item
     * clicks.
     */
    private Callbacks mCallbacks = sDummyCallbacks;

    /**
     * The current activated item position. Only used on tablets.
     */
    private int mActivatedPosition = ListView.INVALID_POSITION;

    /**
     * A dummy implementation of the {@link Callbacks} interface that does
     * nothing. Used only when this fragment is not attached to an activity.
     */
    private static final Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(String fragmentName) {}
    };

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public LexiconFavoritesListFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create and set list adapter.
        String[] fromColumns = {AppDataContract.LexiconFavorites.COLUMN_NAME_WORD};
        int[] toViews = {android.R.id.text1};
        mAdapter = new android.widget.SimpleCursorAdapter(getActivity(),
                R.layout.greek_simple_list_item_activated_1, null, fromColumns, toViews, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), LexiconFavoritesProvider.CONTENT_URI, PROJECTION, SELECTION,
                SELECTION_ARGS, SORT_ORDER);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
        setNoItemsView(R.string.lexicon_favorites_empty_view);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Restore the previously serialized activated item position.
        if (savedInstanceState != null
                && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
            setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // Activities containing this fragment must implement its callbacks.
        if (!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }

        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();

        // Reset the active callbacks interface to the dummy implementation.
        mCallbacks = sDummyCallbacks;
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);
        Cursor cursor = (Cursor) mAdapter.getItem(position);
        int lexiconFavoritesId = cursor.getInt(0);
        setSelectedLexiconItemId(lexiconFavoritesId);
        mCallbacks.onItemSelected(NAME);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mActivatedPosition != ListView.INVALID_POSITION) {
            // Serialize and persist the activated item position.
            outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
        }
    }

    @Override
    protected void setSelectedLexiconItemId(int id) {
        String[] columns = new String[] {AppDataContract.LexiconFavorites.COLUMN_NAME_LEXICON_ID};
        String selection = AppDataContract.LexiconFavorites._ID + " = ?";
        String[] selectionArgs = new String[] {Integer.toString(id)};
        ContentResolver resolver = getActivity().getContentResolver();
        Cursor cursor = resolver.query(AppDataContract.LexiconFavorites.CONTENT_URI, columns,
                                       selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentnResolver#query() returned null");
        }


        if (cursor.moveToFirst()) {
            mSelectedLexiconId = cursor.getInt(0);
            cursor.close();
        } else {
            cursor.close();
            throw new IllegalArgumentException("Invalid ID: " + id);
        }
    }
} public class LexiconHistoryListFragment extends AbstractLexiconListFragment
        implements LoaderManager.LoaderCallbacks<Cursor>{
    public static final String NAME = "lexicon_History";

    private static final String[] PROJECTION = new String[] {AppDataContract.LexiconHistory._ID,
            AppDataContract.LexiconHistory.COLUMN_NAME_WORD};
    private static final String SELECTION = "";
    private static final String[] SELECTION_ARGS = {};
    private static final String ORDER_BY = AppDataContract.LexiconHistory._ID + " DESC";

    private SimpleCursorAdapter mAdapter;

    /**
     * The fragment's current callback object, which is notified of list item
     * clicks.
     */
    private Callbacks mCallbacks = sDummyCallbacks;

    /**
     * A dummy implementation of the {@link Callbacks} interface that does
     * nothing. Used only when this fragment is not attached to an activity.
     */
    private static final Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(String fragmentName) {
        }
    };

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public LexiconHistoryListFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Create and set list adapter.
        String[] fromColumns = {AppDataContract.LexiconHistory.COLUMN_NAME_WORD};
        int[] toViews = {android.R.id.text1};
        mAdapter = new android.widget.SimpleCursorAdapter(getActivity(),
                R.layout.greek_simple_list_item_activated_1, null, fromColumns, toViews, 0);
        setListAdapter(mAdapter);

        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), LexiconHistoryProvider.CONTENT_URI, PROJECTION, SELECTION,
                SELECTION_ARGS, ORDER_BY);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
        setNoItemsView(R.string.lexicon_history_empty_view);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // Activities containing this fragment must implement its callbacks.
        if (!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }

        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();

        // Reset the active callbacks interface to the dummy implementation.
        mCallbacks = sDummyCallbacks;
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);

        Cursor cursor = (Cursor) mAdapter.getItem(position);
        int lexiconHistoryId = cursor.getInt(0);

        setSelectedLexiconItemId(lexiconHistoryId);

        // Notify the active callbacks interface (the activity, if the
        // fragment is attached to one) that an item has been selected.
        mCallbacks.onItemSelected(NAME); // Database IDs start at 1.
    }

    @Override
    protected void setSelectedLexiconItemId(int id) {
        String[] columns = new String[] {AppDataContract.LexiconHistory.COLUMN_NAME_LEXICON_ID};
        String selection = AppDataContract.LexiconHistory._ID + " = ?";
        String[] selectionArgs = new String[] {Integer.toString(id)};
        ContentResolver resolver = getActivity().getContentResolver();
        Cursor cursor = resolver.query(AppDataContract.LexiconHistory.CONTENT_URI, columns,
                                       selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        if (cursor.moveToFirst()) {
            mSelectedLexiconId = cursor.getInt(0);
            cursor.close();
        } else {
            cursor.close();
            throw new IllegalArgumentException("Invalid ID: " + id);
        }
    }
} public class NavigationDrawerFragment extends Fragment {

    /**
     * Remember the position of the selected item.
     */
    private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";

    /**
     * Per the design guidelines, you should show the drawer on launch until the user manually
     * expands it. This shared preference tracks this.
     */
    private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";

    /**
     * A pointer to the current callbacks instance (the Activity).
     */
    private NavigationDrawerCallbacks mCallbacks;

    /**
     * Helper component that ties the action bar to the navigation drawer.
     */
    private ActionBarDrawerToggle mDrawerToggle;

    private DrawerLayout mDrawerLayout;
    private ListView mDrawerListView;
    private View mFragmentContainerView;

    private int mCurrentSelectedPosition = 1; // Default position is Lexicon: Browse.
    private boolean mFromSavedInstanceState;
    private boolean mUserLearnedDrawer;

    public NavigationDrawerFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Read in the flag indicating whether or not the user has demonstrated awareness of the
        // drawer. See PREF_USER_LEARNED_DRAWER for details.
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
        mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);

        if (savedInstanceState != null) {
            mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
            mFromSavedInstanceState = true;
        }

        // Select either the default item (0) or the last selected item.
        selectItem(mCurrentSelectedPosition);

        // Indicate that this fragment would like to influence the set of actions in the action bar.
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        mDrawerListView = (ListView) inflater.inflate(
                R.layout.fragment_navigation_drawer, container, false);
        mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                selectItem(position);
            }
        });

        AbstractNavigationDrawerItem[] items = new AbstractNavigationDrawerItem[] {
                new NavigationDrawerHeading(100, getString(R.string.nav_drawer_heading_lexicon)),
                new NavigationDrawerRow(101, getString(R.string.nav_drawer_row_browse_lexicon),
                        R.drawable.ic_subject_grey600_24dp, R.drawable.ic_subject_black_24dp,
                        getActivity()),
                new NavigationDrawerRow(102, getString(R.string.nav_drawer_row_favorites),
                        R.drawable.ic_favorite_grey600_24dp, R.drawable.ic_favorite_black_24dp,
                        getActivity()),
                new NavigationDrawerRow(103, getString(R.string.nav_drawer_row_history),
                        R.drawable.ic_history_grey600_24dp, R.drawable.ic_history_black_24dp,
                        getActivity()),
                new NavigationDrawerHeading(200, getString(R.string.nav_drawer_heading_grammar)),
                new NavigationDrawerRow(201, getString(R.string.nav_drawer_row_browse_grammar),
                        R.drawable.ic_subject_grey600_24dp, R.drawable.ic_subject_black_24dp,
                        getActivity()),
                new NavigationDrawerRow(202, getString(R.string.nav_drawer_row_bookmarks),
                        R.drawable.ic_bookmark_grey600_24dp, R.drawable.ic_bookmark_black_24dp,
                        getActivity())
        };

        mDrawerListView.setAdapter(new NavigationDrawerAdapter(getActivity(),
                android.R.layout.simple_list_item_activated_2, items));

        mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
        return mDrawerListView;
    }

    /**
     * @return true if the navigation drawer is open or false otherwise
     */
    public boolean isDrawerOpen() {
        return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);
    }

    /**
     * Users of this fragment must call this method to set up the navigation drawer interactions.
     * @param fragmentId the id of this fragment in its activity's layout
     * @param drawerLayout the {@link DrawerLayout} containing this fragment's UI
     */
    public void setUp(int fragmentId, DrawerLayout drawerLayout) {
        mFragmentContainerView = getActivity().findViewById(fragmentId);
        mDrawerLayout = drawerLayout;

        // set a custom shadow that overlays the main content when the drawer opens
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, Gravity.START);
        // set up the drawer's list view with items and click listener

        ActionBar actionBar = getActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeButtonEnabled(true);

        // ActionBarDrawerToggle ties together the the proper interactions
        // between the navigation drawer and the action bar app icon.
        mDrawerToggle = new ActionBarDrawerToggle(
                getActivity(),                    // host Activity
                mDrawerLayout,                    // DrawerLayout object
                R.string.navigation_drawer_open,  // "open drawer" description for accessibility
                R.string.navigation_drawer_close         // "close drawer" description for accessibility
        ) {
            @Override
            public void onDrawerClosed(View drawerView) {
                super.onDrawerClosed(drawerView);
                if (!isAdded()) {
                    return;
                }

                getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                if (!isAdded()) {
                    return;
                }

                if (!mUserLearnedDrawer) {
                    // The user manually opened the drawer; store this flag to prevent auto-showing
                    // the navigation drawer automatically in the future.
                    mUserLearnedDrawer = true;
                    SharedPreferences sp = PreferenceManager
                            .getDefaultSharedPreferences(getActivity());
                    sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
                }

                getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
            }
        };

        // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer,
        // per the navigation drawer design guidelines.
        if (!mUserLearnedDrawer && !mFromSavedInstanceState) {
            mDrawerLayout.openDrawer(mFragmentContainerView);
        }

        // Defer code dependent on restoration of previous instance state.
        mDrawerLayout.post(new Runnable() {
            @Override
            public void run() {
                mDrawerToggle.syncState();
            }
        });

        mDrawerLayout.setDrawerListener(mDrawerToggle);
    }

    private void selectItem(int position) {
        mCurrentSelectedPosition = position;
        if (mDrawerListView != null) {
            mDrawerListView.setItemChecked(position, true);
        }
        if (mDrawerLayout != null) {
            mDrawerLayout.closeDrawer(mFragmentContainerView);
        }

        // We don't want to swap out the fragments if we're just returning from a configuration
        // change.
        if (mFromSavedInstanceState) {
            mFromSavedInstanceState = false;
            return;
        }

        if (mCallbacks != null) {
            mCallbacks.onNavigationDrawerItemSelected(position);
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mCallbacks = (NavigationDrawerCallbacks) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mCallbacks = null;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Forward the new configuration the drawer toggle component.
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        // If the drawer is open, show the global_menu app actions in the action bar. See also
        // showGlobalContextActionBar, which controls the top-left area of the action bar.
        if (mDrawerLayout != null && isDrawerOpen()) {
            inflater.inflate(R.menu.global_menu, menu);
            showGlobalContextActionBar();
        }
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Per the navigation drawer design guidelines, updates the action bar to show the global_menu
     * app 'context', rather than just what's in the current screen.
     */
    private void showGlobalContextActionBar() {
        ActionBar actionBar = getActionBar();
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setTitle(R.string.app_name);
        actionBar.setSubtitle(null);
    }

    private ActionBar getActionBar() {
        return ((ActionBarActivity) getActivity()).getSupportActionBar();
    }

    /**
     * Callbacks interface that all activities using this fragment must implement.
     */
    public interface NavigationDrawerCallbacks {
        /**
         * Called when an item in the navigation drawer is selected.
         */
        void onNavigationDrawerItemSelected(int position);
    }

    /**
     * Sets PREF_USER_LEARNED_DRAWER to true.
     */
    public void userLearnedDrawer() {
        mUserLearnedDrawer = true;
        SharedPreferences sp = PreferenceManager
                .getDefaultSharedPreferences(getActivity());
        sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).apply();
    }

    public int getCurrentSelectedPosition() {
        return mCurrentSelectedPosition;
    }

    public void setCurrentSelectedPosition(int position) {
        mCurrentSelectedPosition = position;
        mDrawerListView.setItemChecked(position, true);
    }
} public class LexiconPresenter {

    private final LexiconDetailView mView;
    private final Context mContext;
    private final ContentResolver mResolver;

    public LexiconPresenter(LexiconDetailView view, Context context) {
        mView = view;
        mContext = context;
        mResolver = mContext.getContentResolver();
    }

    public void onCreateOptionsMenu(Menu menu) {
        setLexiconFavoriteIcon(menu);
    }

    public void onPrepareOptionsMenu(Menu menu) {
        setLexiconFavoriteIcon(menu);
    }

    /**
     * Returns {@code true} if the word with the specified lexicon ID is
     * a member of the favorites list.
     * @param lexiconId the lexicon ID to check
     * @return {@code true} if the specified word is a member of the
     *     favorites list, or {@code false} otherwise
     */
    private boolean isFavorite(int lexiconId) {
        String[] columns = new String[] {AppDataContract.LexiconFavorites._ID};
        String selection = AppDataContract.LexiconFavorites.COLUMN_NAME_LEXICON_ID + " = ?";
        String[] selectionArgs = new String[] {Integer.toString(lexiconId)};
        Cursor cursor = mResolver.query(AppDataContract.LexiconFavorites.CONTENT_URI,
                                        columns, selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        boolean result = false;
        if (cursor.getCount() > 0) {
            result = true;
        }
        cursor.close();
        return result;
    }

    /**
     * Sets the Lexicon Favorite icon to the appropriate state based on the currently selected
     * lexicon entry.
     * @param menu the {@code Menu} containing the Favorite icon
     */
    private void setLexiconFavoriteIcon(Menu menu) {
        MenuItem addFavorite = menu.findItem(R.id.action_add_favorite);
        MenuItem removeFavorite = menu.findItem(R.id.action_remove_favorite);

        int id = mView.getSelectedLexiconId();
        if (!mView.isDetailFragmentVisible() || id == ListView.NO_ID) {
            // Hide both icons when the app is in one-pane mode or no item is selected.
            addFavorite.setVisible(false);
            removeFavorite.setVisible(false);
        } else if (isFavorite(id)) {
            addFavorite.setVisible(false);
            removeFavorite.setVisible(true);
        } else {
            addFavorite.setVisible(true);
            removeFavorite.setVisible(false);
        }
    }

    public void onAddFavorite() {
        int id = mView.getSelectedLexiconId();
        String word = getWordFromLexiconId(id);

        ContentValues values = new ContentValues();
        values.put(AppDataContract.LexiconFavorites.COLUMN_NAME_LEXICON_ID, id);
        values.put(AppDataContract.LexiconFavorites.COLUMN_NAME_WORD, word);
        mResolver.insert(AppDataContract.LexiconFavorites.CONTENT_URI, values);

        mView.invalidateOptionsMenu();

        String msg = mContext.getString(R.string.toast_favorite_added);
        mView.displayToast(msg);
    }

    public void onRemoveFavorite() {
        int id = mView.getSelectedLexiconId();

        String selection = AppDataContract.LexiconFavorites.COLUMN_NAME_LEXICON_ID + " = ?";
        String[] selectionArgs = {Integer.toString(id)};
        mResolver.delete(AppDataContract.LexiconFavorites.CONTENT_URI, selection, selectionArgs);

        mView.invalidateOptionsMenu();

        String msg = mContext.getString(R.string.toast_favorite_removed);
        mView.displayToast(msg);
    }

    public void onClearHistory() {
        mResolver.delete(AppDataContract.LexiconHistory.CONTENT_URI, null, null);

        String msg = mContext.getString(R.string.toast_clear_history);
        mView.displayToast(msg);
    }

    public void onAddHistory(String id, String word) {
        // If the word is already in the list, delete it.
        String selection = AppDataContract.LexiconHistory.COLUMN_NAME_LEXICON_ID + " = ?";
        String[] selectionArgs = {id};
        mResolver.delete(LexiconHistoryProvider.CONTENT_URI, selection, selectionArgs);

        // Add word to top of list.
        ContentValues values = new ContentValues();
        values.put(AppDataContract.LexiconHistory.COLUMN_NAME_LEXICON_ID, id);
        values.put(AppDataContract.LexiconHistory.COLUMN_NAME_WORD, word);
        mResolver.insert(LexiconHistoryProvider.CONTENT_URI, values);
    }

    public void onClearFavorites() {
        mResolver.delete(AppDataContract.LexiconFavorites.CONTENT_URI, null, null);

        String msg = mContext.getString(R.string.toast_clear_lexicon_favorites);
        mView.displayToast(msg);
    }

    /**
     * Returns the word corresponding to the specified lexicon ID.
     * @param id the lexicon ID for which to search
     * @return the corresponding word
     */
    private String getWordFromLexiconId(int id) {
        String[] projection = {LexiconContract.COLUMN_GREEK_FULL_WORD};
        String selection = LexiconContract._ID + " = ?";
        String[] selectionArgs = {Integer.toString(id)};
        ContentResolver resolver = mContext.getContentResolver();
        Cursor cursor = resolver.query(LexiconContract.CONTENT_URI, projection, selection,
                                       selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        String word;
        if (cursor.moveToFirst()) {
            word = cursor.getString(0);
            cursor.close();
        } else {
            cursor.close();
            throw new IllegalArgumentException("Invalid lexicon ID: " + id);
        }
        return word;
    }

} public final class MainPresenter {

    /** Custom intent action. */
    public static final String ACTION_SET_MODE = "com.benlinskey.greekreference.SET_MODE";

    private final MainView mView;
    private final Context mContext;
    private final ContentResolver mResolver;

    public MainPresenter(MainView view, Context context) {
        mView = view;
        mContext = context;
        mResolver = mContext.getContentResolver();
    }

    public void onCreate() {
        PreferenceManager.setDefaultValues(mContext, R.xml.preferences, false);
    }

    public void handleIntent(Intent intent) {
        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra((SearchManager.QUERY));
            search(query);
        } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
            Uri data = intent.getData();
            getLexiconEntry(data);
        } else if (ACTION_SET_MODE.equals(intent.getAction())) {
            String modeName = intent.getStringExtra(mView.KEY_MODE);
            Mode mode = Mode.getModeFromName(modeName);
            mView.switchToMode(mode);
        }
    }

    /**
     * Searches the lexicon for a word and displays the result.
     * @param query a string containing the word for which to search. This string is case
     *     insensitive and may be written in either Greek characters or Beta code.
     */
    public void search(String query) {
        // TODO: Handle words with multiple entries.

        String word = query.toLowerCase();

        String[] columns = new String[] {LexiconContract._ID};
        String selection = LexiconContract.COLUMN_BETA_SYMBOLS + " = ? OR "
                + LexiconContract.COLUMN_BETA_NO_SYMBOLS + " = ? OR "
                + LexiconContract.COLUMN_GREEK_LOWERCASE + " = ?";

        String[] selectionArgs = new String[] {word, word, word};

        String sortOrder = LexiconContract._ID + " ASC";

        Uri uri = LexiconProvider.CONTENT_URI;
        Cursor cursor = mResolver.query(uri, columns, selection, selectionArgs, sortOrder);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        if (cursor.moveToFirst()) {
            String idStr = cursor.getString(0);
            int id = Integer.parseInt(idStr);

            mView.ensureModeIsLexiconBrowse();
            mView.selectLexiconItem(id);
        } else {
            String msg = mContext.getString(R.string.toast_search_no_results);
            mView.displayToast(msg);
        }

        cursor.close();
    }

    /**
     * Finds and selects the lexicon entry corresponding to the specified URI.
     * @param data the URI of the lexicon entry to select
     */
    public void getLexiconEntry(Uri data) {
        Cursor cursor = mResolver.query(data, null, null, null, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        cursor.moveToFirst();
        int id;
        try {
            int idIndex = cursor.getColumnIndexOrThrow(LexiconContract._ID);
            String idStr = cursor.getString(idIndex);
            id = Integer.parseInt(idStr);
            cursor.close();
        } catch (IllegalArgumentException e) {
            cursor.close();
            String className = getClass().getCanonicalName();
            Log.e(className, "Failed to retrieve result from database", e);
            throw e;
        }

        mView.selectLexiconItem(id);
    }

    public void onLexiconItemSelected(String id) {
        String[] columns = new String[] {
            LexiconContract.COLUMN_ENTRY,
            LexiconContract.COLUMN_GREEK_FULL_WORD
        };

        String selection = LexiconContract._ID + " = ?";
        String[] selectionArgs = new String[] {id};
        Uri uri = LexiconContract.CONTENT_URI;

        Cursor cursor = mResolver.query(uri, columns, selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        String entry;
        String word;
        if (cursor.moveToFirst()) {
            entry = cursor.getString(0);
            word = cursor.getString(1);
            cursor.close();
        } else {
            cursor.close();
            throw new IllegalStateException("Failed to retrieve lexicon entry");
        }

        mView.displayLexiconEntry(id, word, entry);
    }

    public void onSyntaxItemSelected(String id) {
        String[] columns = new String[] {
            SyntaxContract.COLUMN_NAME_XML,
            SyntaxContract.COLUMN_NAME_SECTION
        };
        String selection = SyntaxContract._ID + " = ?";
        String[] selectionArgs = new String[] {id};
        Uri uri = SyntaxContract.CONTENT_URI;
        Cursor cursor = mResolver.query(uri, columns, selection, selectionArgs, null);
        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        String xml;
        String section;
        if (cursor.moveToFirst()) {
            xml = cursor.getString(0);
            section = cursor.getString(1);
            cursor.close();
            Log.w("Syntax item selected", section + ": " + xml);
        } else {
            cursor.close();
            throw new IllegalStateException("Failed to retrieve syntax section");
        }

        mView.displaySyntaxSection(section, xml);
    }

    public void onEditSettings() {
        mContext.startActivity(new Intent(mContext, SettingsActivity.class));
    }

} public class SyntaxPresenter {

    private final SyntaxDetailView mView;
    private final Context mContext;
    private final ContentResolver mResolver;

    public SyntaxPresenter(SyntaxDetailView view, Context context) {
        mView = view;
        mContext = context;
        mResolver = mContext.getContentResolver();
    }

    public void onCreateOptionsMenu(Menu menu) {
        setBookmarkIcon(menu);
    }

    public void onPrepareOptionsMenu(Menu menu) {
        setBookmarkIcon(menu);
    }

    /**
     * Sets the Syntax Bookmark icon to the appropriate state based on the currently selected
     * syntax section.
     * @param menu the {@code Menu} containing the Bookmark icon
     */
    private void setBookmarkIcon(Menu menu) {
        MenuItem addBookmark = menu.findItem(R.id.action_add_bookmark);
        MenuItem removeBookmark = menu.findItem(R.id.action_remove_bookmark);

        int id = mView.getSelectedSyntaxId();
        if (!mView.isDetailFragmentVisible() || id == ListView.NO_ID) {
            addBookmark.setVisible(false);
            removeBookmark.setVisible(false);
        } else if (isBookmarked(id)) {
            addBookmark.setVisible(false);
            removeBookmark.setVisible(true);
        } else {
            addBookmark.setVisible(true);
            removeBookmark.setVisible(false);
        }
    }

    /**
     * Returns true if the word with the specified syntax ID is a member of the bookmarks list.
     * @param syntaxId the syntax ID to check
     * @return true if the specified word is a member of the bookmarks list, or false otherwise
     */
    private boolean isBookmarked(int syntaxId) {
        String[] columns = new String[] {AppDataContract.SyntaxBookmarks._ID};
        String selection = AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_ID + " = ?";
        String[] selectionArgs = new String[] {Integer.toString(syntaxId)};
        Uri uri = AppDataContract.SyntaxBookmarks.CONTENT_URI;
        Cursor cursor = mResolver.query(uri, columns, selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        boolean result = false;
        if (cursor.getCount() > 0) {
            result = true;
        }
        cursor.close();
        return result;
    }


    public void onAddBookmark() {
        int id = mView.getSelectedSyntaxId();
        String section = getSectionFromId(id);

        ContentValues values = new ContentValues();
        values.put(AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_ID, id);
        values.put(AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_SECTION, section);
        Uri uri = AppDataContract.SyntaxBookmarks.CONTENT_URI;
        mResolver.insert(uri, values);

        mView.invalidateOptionsMenu();

        String msg = mContext.getString(R.string.toast_bookmark_added);
        mView.displayToast(msg);
    }

    public void onRemoveBookmark() {
        int id = mView.getSelectedSyntaxId();

        String selection = AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_ID + " = ?";
        String[] selectionArgs = {Integer.toString(id)};
        mResolver.delete(AppDataContract.SyntaxBookmarks.CONTENT_URI, selection, selectionArgs);

        mView.invalidateOptionsMenu();

        String msg = mContext.getString(R.string.toast_bookmark_removed);
        mView.displayToast(msg);
    }

    public void onClearBookmarks() {
        mResolver.delete(AppDataContract.SyntaxBookmarks.CONTENT_URI, null, null);

        String msg = mContext.getString(R.string.toast_clear_syntax_bookmarks);
        mView.displayToast(msg);
    }

    /**
     * Returns the section title corresponding to the specified syntax ID.
     * @param id the syntax ID for which to search
     * @return the corresponding section title
     */
    private String getSectionFromId(int id) {
        String[] projection = {SyntaxContract.COLUMN_NAME_SECTION};
        String selection = SyntaxContract._ID + " = ?";
        String[] selectionArgs = {Integer.toString(id)};
        Uri uri = SyntaxContract.CONTENT_URI;
        Cursor cursor = mResolver.query(uri, projection, selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        String section;
        if (cursor.moveToFirst()) {
            section = cursor.getString(0);
            cursor.close();
        } else {
            cursor.close();
            throw new IllegalArgumentException("Invalid syntax ID: " + id);
        }
        return section;
    }
} public abstract class AbstractSyntaxListFragment extends AbstractListFragment {
    
    // TODO: Simplify callback interface of this class's children now that we're getting the
    // selected item's ID from the getSelectedLexiconId() method here.
    
    private static final int NO_SELECTION = -1;
    
    protected int mSelectedSyntaxId = NO_SELECTION;

    /**
     * @return the syntax database ID of the selected item
     */
    public int getSelectedSyntaxId() { return mSelectedSyntaxId; }

    /**
     * Sets the selected item ID.
     * @param id the {@code ListView} position of the item to select
     */
    protected abstract void setSelectedSyntaxItemId(int id);
} public class SyntaxBookmarksListFragment extends AbstractSyntaxListFragment
        implements LoaderManager.LoaderCallbacks<Cursor> {

    public static final String NAME = "syntax_bookmarks";

    private static final String[] PROJECTION = new String[] {
        AppDataContract.SyntaxBookmarks._ID,
        AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_SECTION,
        AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_ID
    };
    private static final String SELECTION = "";
    private static final String[] SELECTION_ARGS = {};
    private static final String SORT_ORDER = 
            AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_ID + " ASC";

    /**
     * The serialization (saved instance state) Bundle key representing the
     * activated item position. Only used on tablets.
     */
    private static final String STATE_ACTIVATED_POSITION = "activated_position";

    private SimpleCursorAdapter mAdapter;
    
    /** The fragment's current callback object, which is notified of list item clicks. */
    private Callbacks mCallbacks = sDummyCallbacks;

    /** The current activated item position. Only used on tablets. */
    private int mActivatedPosition = ListView.INVALID_POSITION;

    /**
     * A dummy implementation of the {@link Callbacks} interface that does
     * nothing. Used only when this fragment is not attached to an activity.
     */
    private static final Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(String fragmentName) {}
    };

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public SyntaxBookmarksListFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // TODO: Add chapter name before section name in this list?
        String[] fromColumns = {AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_SECTION};
        int[] toViews = {android.R.id.text1};
        int layout = android.R.layout.simple_list_item_activated_1;
        mAdapter = new SimpleCursorAdapter(getActivity(), layout, null, fromColumns, toViews, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), AppDataContract.SyntaxBookmarks.CONTENT_URI,
                PROJECTION, SELECTION, SELECTION_ARGS, SORT_ORDER);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
        setNoItemsView(R.string.syntax_bookmarks_empty_view);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Restore the previously serialized activated item position.
        if (savedInstanceState != null
                && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
            setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // Activities containing this fragment must implement its callbacks.
        if (!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }

        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();

        // Reset the active callbacks interface to the dummy implementation.
        mCallbacks = sDummyCallbacks;
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);
        Cursor cursor = (Cursor) mAdapter.getItem(position);
        int syntaxBookmarksId = cursor.getInt(0);
        setSelectedSyntaxItemId(syntaxBookmarksId);
        mCallbacks.onItemSelected(NAME);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mActivatedPosition != ListView.INVALID_POSITION) {
            // Serialize and persist the activated item position.
            outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
        }
    }

    @Override
    protected void setSelectedSyntaxItemId(int id) {
        String[] columns = new String[] {AppDataContract.SyntaxBookmarks.COLUMN_NAME_SYNTAX_ID};
        String selection = AppDataContract.SyntaxBookmarks._ID + " = ?";
        String[] selectionArgs = new String[] {Integer.toString(id)};
        ContentResolver resolver = getActivity().getContentResolver();
        Uri uri = AppDataContract.SyntaxBookmarks.CONTENT_URI;
        Cursor cursor = resolver.query(uri, columns, selection, selectionArgs, null);

        if (cursor == null) {
            throw new NullPointerException("ContentResolver#query() returned null");
        }

        if (cursor.moveToFirst()) {
            mSelectedSyntaxId = cursor.getInt(0);
            cursor.close();
        } else {
            cursor.close();
            throw new IllegalArgumentException("Invalid ID: " + id);
        }
    }
} public class SyntaxBrowseListFragment extends AbstractSyntaxListFragment
        implements LoaderManager.LoaderCallbacks<Cursor> {

    public static final String NAME = "syntax_browse";

    private SimpleCursorAdapter mAdapter;
    private static final String[] PROJECTION = new String[] {
        SyntaxContract._ID,
        SyntaxContract.COLUMN_NAME_SECTION
    };
    private static final String SELECTION = "";
    private static final String[] SELECTION_ARGS = {};

    /**
     * The serialization (saved instance state) Bundle key representing the
     * activated item position. Only used on tablets.
     */
    private static final String STATE_ACTIVATED_POSITION = "activated_position";

    /**
     * A dummy implementation of the {@link Callbacks} interface that does
     * nothing. Used only when this fragment is not attached to an activity.
     */
    private static final Callbacks sDummyCallbacks = new Callbacks() {
        @Override
        public void onItemSelected(String fragmentName) {}
    };
    
    /** The fragment's current callback object, which is notified of list item clicks. */
    private Callbacks mCallbacks = sDummyCallbacks;

    /** The current activated item position. Only used on tablets. */
    private int mActivatedPosition = ListView.INVALID_POSITION;
    
    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public SyntaxBrowseListFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // TODO: Create a custom adapter with support for chapter headings.
        String[] fromColumns = {SyntaxContract.COLUMN_NAME_SECTION};
        int[] toViews = {android.R.id.text1};
        int layout = android.R.layout.simple_list_item_activated_1;
        mAdapter = new SimpleCursorAdapter(getActivity(), layout, null, fromColumns, toViews, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), SyntaxContract.CONTENT_URI, PROJECTION, SELECTION,
                SELECTION_ARGS, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        // Restore the previously serialized activated item position.
        if (savedInstanceState != null
                && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
            setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
        }
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // Activities containing this fragment must implement its callbacks.
        if (!(activity instanceof Callbacks)) {
            throw new IllegalStateException("Activity must implement fragment's callbacks.");
        }

        mCallbacks = (Callbacks) activity;
    }

    @Override
    public void onDetach() {
        super.onDetach();

        // Reset the active callbacks interface to the dummy implementation.
        mCallbacks = sDummyCallbacks;
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        super.onListItemClick(listView, view, position, id);
        setSelectedSyntaxItemId(position);
        mCallbacks.onItemSelected(NAME);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mActivatedPosition != ListView.INVALID_POSITION) {
            // Serialize and persist the activated item position.
            outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
        }
    }

    @Override
    protected void setSelectedSyntaxItemId(int id) {
        mSelectedSyntaxId = id + 1;
    }
} public class LexiconDetailActivity extends AbstractDetailActivity implements LexiconDetailView {
    
    public static final String ARG_LEXICON_ID = "lexicon_id";
    public static final String ARG_WORD = "word";

    private LexiconPresenter mLexiconPresenter;

    private int mLexiconId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mLexiconPresenter = new LexiconPresenter(this, this);

        Intent intent = getIntent();
        mLexiconId = intent.getIntExtra(ARG_LEXICON_ID, -1);

        mTitle = getString(R.string.title_lexicon);

        // savedInstanceState is non-null when there is fragment state
        // saved from previous configurations of this activity
        // (e.g. when rotating the screen from portrait to landscape).
        // In this case, the fragment will automatically be re-added
        // to its container so we don't need to manually add it.
        // For more information, see the Fragments API guide at:
        //
        // http://developer.android.com/guide/components/fragments.html
        if (savedInstanceState == null) {
            Bundle arguments = new Bundle();
            arguments.putString(LexiconDetailFragment.ARG_ENTRY,
                    getIntent().getStringExtra(LexiconDetailFragment.ARG_ENTRY));
            LexiconDetailFragment fragment = new LexiconDetailFragment();
            fragment.setArguments(arguments);
            getFragmentManager().beginTransaction()
                    .replace(R.id.item_detail_container, fragment)
                    .commitAllowingStateLoss();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.lexicon_detail_activity_menu, menu);

        mLexiconPresenter.onCreateOptionsMenu(menu);

        restoreActionBar();
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        mLexiconPresenter.onPrepareOptionsMenu(menu);

        restoreActionBar();
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public int getSelectedLexiconId() {
        return mLexiconId;
    }

    @Override
    public boolean isDetailFragmentVisible() {
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class));
            return true;
        case R.id.action_add_favorite:
            mLexiconPresenter.onAddFavorite();
            return true;
        case R.id.action_remove_favorite:
            mLexiconPresenter.onRemoveFavorite();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void restoreActionBar() {
        ActionBar actionBar = getSupportActionBar();
        assert actionBar != null;
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }

} public class LexiconDetailFragment extends AbstractDetailFragment {

    public static final String TAG = "LexiconDetailFragment";
    public static final String PERSEUS_TOOL_EXTRA_KEY = "com.benlinskey.greekreference.views.list.lexicon.PerseusToolExtraKey";

    // Fragment arguments representing strings containing entry information
    public static final String ARG_ENTRY = "entry";

    private LexiconEntry mLexiconEntry = null;
    private boolean mBlank = true; // True if no entry displayed.

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public LexiconDetailFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setHasOptionsMenu(true);

        if (getArguments() != null && getArguments().containsKey(ARG_ENTRY)) {
            mBlank = false;

            // Load entry represented by fragment argument.
            String entry = getArguments().getString(ARG_ENTRY);

            // Parse XML.
            LexiconXmlParser parser = new LexiconXmlParser();
            InputStream in = new ByteArrayInputStream(entry.getBytes());

            try {
                mLexiconEntry = parser.parse(in);
            } catch (Exception e) {
                String msg = "Failed to retrieve lexicon entry for '" + entry + "'";
                throw new NullPointerException(msg);
            }
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_item_detail, container, false);

        if (!mBlank) {
            // Display lexicon entry.
            GreekTextView textView = (GreekTextView) rootView.findViewById(R.id.item_detail);
            textView.setText(mLexiconEntry.toSpanned(textView.getTextSize()));
        }

        return rootView;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.lexicon_detail_fragment_menu, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        MenuItem item = menu.findItem(R.id.action_perseus_tool);
        if (perseusToolOptionDisabled()) {
            item.setVisible(false);
        } else {
            item.setVisible(true);
        }
    }

    /**
     * Checks whether the user has disabled the View on Perseus option in the settings.
     * @return {@code true} if the user has disabled the option or {@code false}
     *     otherwise
     */
    private boolean perseusToolOptionDisabled() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
        return prefs.getBoolean(getString(R.string.pref_perseus_tool_key), false);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_perseus_tool:
                displayPerseusTool();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private AbstractLexiconListFragment getListFragment() {
        FragmentManager mgr = getActivity().getFragmentManager();
        return (AbstractLexiconListFragment) mgr.findFragmentById(R.id.item_list_container);
    }

    @SuppressWarnings("unused") // Erroneous warning
    public int getSelectedLexiconId() {
        AbstractLexiconListFragment fragment = getListFragment();
        return fragment.getSelectedLexiconId();
    }

    // NOTE: The following two methods should only be used in two-pane mode.
    // TODO: Throw exception if these methods are called in one-pane mode.

    /**
     * Searches for this word in the Perseus Greek Word Study Tool and displays the resulting
     * page in a <code>WebView</code>, or displays an error dialog if there is no network
     * connection.
     */
    private void displayPerseusTool() {
        if (!networkConnectionAvailable()) {
            displayNoNetworkConnectionError();
            return;
        }

        Intent intent = new Intent(getActivity(), PerseusToolActivity.class);
        String morph = mLexiconEntry.getOrth();
        intent.putExtra(PERSEUS_TOOL_EXTRA_KEY, morph);
        startActivity(intent);
    }

    /**
     * Checks whether the device is connected to the Internet.
     * @return {@code true} if the device is connected to the Internet or {@code false}
     *     otherwise
     */
    private boolean networkConnectionAvailable() {
        ConnectivityManager manager = (ConnectivityManager)
                getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        return networkInfo != null && networkInfo.isConnected();
    }

    /**
     * Displays an error dialog that explains that a network connection is required to use the
     * selected feature.
     */
    private void displayNoNetworkConnectionError() {
        new NoNetworkConnectionDialogFragment().show(getFragmentManager(), null);
    }

    public static class NoNetworkConnectionDialogFragment extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setMessage(R.string.dialog_no_network_connection)
                   .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                       @Override
                       public void onClick(DialogInterface dialog, int which) {
                           dismiss();
                       }
                   });
            return builder.create();
        }
    }

} public interface LexiconDetailView extends DetailView {

    // TODO: We can just change this to getSelectedId(), move it to DetailView,
    // and get rid of the mode-specific interfaces.
    int getSelectedLexiconId();

} public class SyntaxDetailActivity extends AbstractDetailActivity implements SyntaxDetailView {

    public static final String ARG_SYNTAX_ID = "syntax_id";
    public static final String ARG_SECTION = "section";

    private SyntaxPresenter mPresenter;
    private CharSequence mTitle; // Used to store the last screen title.
    private int mSyntaxId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPresenter = new SyntaxPresenter(this, this);

        Intent intent = getIntent();
        mSyntaxId = intent.getIntExtra(ARG_SYNTAX_ID, -1);

        mTitle = getString(R.string.title_syntax);

        if (savedInstanceState == null) {
            Bundle arguments = new Bundle();
            String stringExtra = getIntent().getStringExtra(SyntaxDetailFragment.ARG_XML);
            arguments.putString(SyntaxDetailFragment.ARG_XML, stringExtra);
            SyntaxDetailFragment fragment = new SyntaxDetailFragment();
            fragment.setArguments(arguments);
            getFragmentManager().beginTransaction()
                                .replace(R.id.item_detail_container, fragment)
                                .commitAllowingStateLoss();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.syntax_detail_menu, menu);

        mPresenter.onCreateOptionsMenu(menu);

        restoreActionBar();
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        mPresenter.onPrepareOptionsMenu(menu);
        restoreActionBar();
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public int getSelectedSyntaxId() {
        return mSyntaxId;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            NavUtils.navigateUpTo(this, new Intent(this, MainActivity.class));
            return true;
        case R.id.action_add_bookmark:
            mPresenter.onAddBookmark();;
            return true;
        case R.id.action_remove_bookmark:
            mPresenter.onRemoveBookmark();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean isDetailFragmentVisible() {
        return true;
    }

    @Override
    protected void restoreActionBar() {
        ActionBar actionBar = getSupportActionBar();
        assert actionBar != null;
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setDisplayShowTitleEnabled(true);
        actionBar.setTitle(mTitle);
    }

} public class SyntaxDetailFragment extends AbstractDetailFragment {
    
    public static final String TAG = "SyntaxDetailFragment";
    public static final String ARG_XML = "xml";
    
    private SyntaxSection mSection;
    private boolean mBlank = false;

    /**
     * Mandatory empty constructor for the fragment manager to instantiate the
     * fragment (e.g. upon screen orientation changes).
     */
    public SyntaxDetailFragment() {}

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getArguments() != null && getArguments().containsKey(ARG_XML)) {
            String xml = getArguments().getString(ARG_XML);

            SyntaxXmlParser parser = new SyntaxXmlParser();
            InputStream in = new ByteArrayInputStream(xml.getBytes());
            try {
                mSection = parser.parse(in);
            } catch (XmlPullParserException | IOException e) {
                Log.e(TAG, Log.getStackTraceString(e));
            }
        } else {
            mBlank = true;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_item_detail, container, false);

        if (!mBlank) {
            // We add the header here since we can't access resources from a static context in
            // the SyntaxSection class.
            Spanned html = Html.fromHtml(mSection.toString() + getString(R.string.syntax_footer));
            GreekTextView textView = (GreekTextView) rootView.findViewById(R.id.item_detail);
            textView.setMovementMethod(LinkMovementMethod.getInstance());
            textView.setText(html); // Replace with parsed data.
        }

        return rootView;
    }

} public interface SyntaxDetailView extends DetailView {

    int getSelectedSyntaxId();
}
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.benlinskey.greekreference.views.detail.AbstractDetailFragment;
import com.benlinskey.greekreference.views.list.AbstractListFragment;
import com.benlinskey.greekreference.Mode;
import com.benlinskey.greekreference.R;
import com.benlinskey.greekreference.views.list.lexicon.AbstractLexiconListFragment;
import com.benlinskey.greekreference.views.list.lexicon.LexiconBrowseListFragment;
import com.benlinskey.greekreference.views.list.lexicon.LexiconFavoritesListFragment;
import com.benlinskey.greekreference.views.list.lexicon.LexiconHistoryListFragment;
import com.benlinskey.greekreference.navigationdrawer.NavigationDrawerFragment;
import com.benlinskey.greekreference.presenters.LexiconPresenter;
import com.benlinskey.greekreference.presenters.MainPresenter;
import com.benlinskey.greekreference.presenters.SyntaxPresenter;
import com.benlinskey.greekreference.views.list.syntax.AbstractSyntaxListFragment;
import com.benlinskey.greekreference.views.list.syntax.SyntaxBookmarksListFragment;
import com.benlinskey.greekreference.views.list.syntax.SyntaxBrowseListFragment;
import com.benlinskey.greekreference.views.detail.lexicon.LexiconDetailActivity;
import com.benlinskey.greekreference.views.detail.lexicon.LexiconDetailFragment;
import com.benlinskey.greekreference.views.detail.lexicon.LexiconDetailView;
import com.benlinskey.greekreference.views.detail.syntax.SyntaxDetailActivity;
import com.benlinskey.greekreference.views.detail.syntax.SyntaxDetailFragment;
import com.benlinskey.greekreference.views.detail.syntax.SyntaxDetailView;
/*
 * Copyright 2014-2015 Benjamin Linskey
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.benlinskey.greekreference.views;



/**
 * The app's primary activity. On tablets, this activity displays a two-pane layout containing an 
 * {@link AbstractListFragment} and an {@link AbstractDetailFragment}. On phones, it displays only
 * an {@code AbstractListFragment}.
 */
public class MainActivity
        extends AbstractContainerActivity
        implements MainView, LexiconDetailView, SyntaxDetailView,
                NavigationDrawerFragment.NavigationDrawerCallbacks, AbstractListFragment.Callbacks,
                ConfirmClearLexiconFavoritesDialog.OnClearLexiconFavoritesDialogListener,
                ConfirmClearSyntaxBookmarksDialogFragment.OnClearSyntaxBookmarksDialogListener {

    // Application state bundle keys
    private static final String KEY_TITLE = "action_bar_title";
    private static final String KEY_SUBTITLE = "action_bar_subtitle";

    private MainPresenter mMainPresenter;
    private LexiconPresenter mLexiconPresenter;
    private SyntaxPresenter mSyntaxPresenter;

    private NavigationDrawerFragment mNavigationDrawerFragment;
    private CharSequence mTitle;
    private CharSequence mSubtitle;
    private Mode mMode;