com.pdftron.pdf.controls.UserBookmarkDialogFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.pdftron.pdf.controls.UserBookmarkDialogFragment.java

Source

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2014 by PDFTron Systems Inc. All Rights Reserved.
// Consult legal.txt regarding legal and license information.
//---------------------------------------------------------------------------------------

package com.pdftron.pdf.controls;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.TextView;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.Action;
import com.pdftron.pdf.Bookmark;
import com.pdftron.pdf.Destination;
import com.pdftron.pdf.PDFDoc;
import com.pdftron.pdf.tools.R;
import com.pdftron.pdf.utils.AnalyticsHandlerAdapter;
import com.pdftron.pdf.utils.CustomEditText;
import com.pdftron.pdf.utils.Utils;
import com.pdftron.pdf.utils.recyclerview.ItemClickHelper;
import com.pdftron.pdf.utils.recyclerview.SimpleRecyclerView;
import com.pdftron.pdf.utils.recyclerview.SimpleRecyclerViewAdapter;
import com.pdftron.pdf.utils.recyclerview.ViewHolderBindListener;
import com.pdftron.pdf.utils.recyclerview.decoration.DividerItemDecoration;
import com.pdftron.sdf.Obj;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

import co.paulburke.android.itemtouchhelperdemo.helper.ItemTouchHelperAdapter;
import co.paulburke.android.itemtouchhelperdemo.helper.SimpleItemTouchHelperCallback;

public class UserBookmarkDialogFragment extends DialogFragment {

    public static final String PREFS_CONTROLS_FILE_NAME = "com_pdftron_pdfnet_pdfviewctrl_controls_prefs_file";

    private static final String KEY_PREF_USER_BOOKMARK = "user_bookmarks_key";
    private static final String KEY_PREF_USER_BOOKMARK_OBJ_TITLE = "pdftronUserBookmarks";
    private static final String CURRENT_FILE_PATH = "current_file_path";
    private static final String CURRENT_PAGE_NUMBER = "current_page_number";
    private static final String CURRENT_PAGE_OBJNUM = "current_page_obj_num";
    private static final String CURRENT_FILE_READONLY = "current_file_readonly";
    private static final int CONTEXT_MENU_EDIT_ITEM = 0;
    private static final int CONTEXT_MENU_DELETE_ITEM = 1;
    private static final int CONTEXT_MENU_DELETE_ALL = 2;

    public interface UserBookmarkDialogFragmentListener {
        void onUserBookmarkClicked(int pageNum);
    }

    private UserBookmarkDialogFragmentListener mListener;

    private PopulateUserBookmarkListTask mTask;

    private ArrayList<UserBookmarkItem> mSource;
    private UserBookmarkViewAdapter mBookmarkViewAdapter;

    private SimpleRecyclerView mRecyclerViewBookmarks;

    private ImageButton mButtonAdd;

    private int mCurrentPage;
    private String mCurrentFilePath;
    private long mCurrentObjNum;
    private boolean mReadOnly;

    private PDFDoc mPDFDoc;

    private boolean mModified = false;
    private static boolean mRebuild = false;

    private ItemTouchHelper mItemTouchHelper;
    private ItemClickHelper mItemClickHelper;

    private boolean mIsDragging = false;

    public static UserBookmarkDialogFragment newInstance(String filePath, int currentPage, long currentObjNum) {
        UserBookmarkDialogFragment f = new UserBookmarkDialogFragment();

        Bundle args = new Bundle();
        args.putString(CURRENT_FILE_PATH, filePath);
        args.putInt(CURRENT_PAGE_NUMBER, currentPage);
        args.putLong(CURRENT_PAGE_OBJNUM, currentObjNum);
        args.putBoolean(CURRENT_FILE_READONLY, true);
        f.setArguments(args);

        return f;
    }

    public static UserBookmarkDialogFragment newInstance(String filePath, int currentPage, long currentObjNum,
            boolean readOnly) {
        UserBookmarkDialogFragment f = new UserBookmarkDialogFragment();

        Bundle args = new Bundle();
        args.putString(CURRENT_FILE_PATH, filePath);
        args.putInt(CURRENT_PAGE_NUMBER, currentPage);
        args.putLong(CURRENT_PAGE_OBJNUM, currentObjNum);
        args.putBoolean(CURRENT_FILE_READONLY, readOnly);
        f.setArguments(args);

        return f;
    }

    public UserBookmarkDialogFragment setPDFDoc(PDFDoc doc) {
        mPDFDoc = doc;
        return this;
    }

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

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

    @Override
    public void onResume() {
        super.onResume();
        loadBookmarks(getActivity());
    }

    @Override
    public void onPause() {
        super.onPause();
        commitData(getActivity());
    }

    public void onShowPopupMenu(final int position, View anchor) {
        PopupMenu popupMenu = new PopupMenu(getActivity(), anchor);
        Menu menu = popupMenu.getMenu();

        final UserBookmarkItem bItem = mBookmarkViewAdapter.getItem(position);
        String[] menuItems = getResources().getStringArray(R.array.user_bookmark_dialog_context_menu);
        menu.add(Menu.NONE, CONTEXT_MENU_EDIT_ITEM, CONTEXT_MENU_EDIT_ITEM, menuItems[CONTEXT_MENU_EDIT_ITEM]);
        menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ITEM, CONTEXT_MENU_DELETE_ITEM,
                menuItems[CONTEXT_MENU_DELETE_ITEM]);
        menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ALL, CONTEXT_MENU_DELETE_ALL, menuItems[CONTEXT_MENU_DELETE_ALL]);
        MenuItem.OnMenuItemClickListener listener = new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                onPopupItemSelected(item, position);
                return true;
            }
        };
        menu.getItem(CONTEXT_MENU_EDIT_ITEM).setOnMenuItemClickListener(listener);
        menu.getItem(CONTEXT_MENU_DELETE_ITEM).setOnMenuItemClickListener(listener);
        menu.getItem(CONTEXT_MENU_DELETE_ALL).setOnMenuItemClickListener(listener);
        popupMenu.show();
    }

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

        mCurrentPage = getArguments().getInt(CURRENT_PAGE_NUMBER);
        mCurrentFilePath = getArguments().getString(CURRENT_FILE_PATH);
        mCurrentObjNum = getArguments().getLong(CURRENT_PAGE_OBJNUM);
        mReadOnly = getArguments().getBoolean(CURRENT_FILE_READONLY, false);

        if (mCurrentFilePath == null) {
            throw new NullPointerException("current file path can't be null");
        }

        mSource = new ArrayList<UserBookmarkItem>();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.controls_fragment_bookmark_dialog, null);

        mBookmarkViewAdapter = new UserBookmarkViewAdapter(getActivity(), mSource, null);
        View recView = view.findViewById(R.id.controls_bookmark_recycler_view);
        mRecyclerViewBookmarks = (SimpleRecyclerView) recView;
        //mRecyclerViewBookmarks.setEmptyView(view.findViewById(R.id.control_bookmark_textview_empty));

        mRecyclerViewBookmarks.setAdapter(mBookmarkViewAdapter);

        mButtonAdd = (ImageButton) view.findViewById(R.id.control_bookmark_add);
        mButtonAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mModified = true;

                UserBookmarkItem item = new UserBookmarkItem();
                item.pageObjNum = mCurrentObjNum;
                item.pageNumber = mCurrentPage;
                item.title = getActivity().getString(R.string.controls_bookmark_dialog_default_title)
                        + Integer.toString(mCurrentPage);
                mBookmarkViewAdapter.add(item);
                mBookmarkViewAdapter.notifyDataSetChanged();

                com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance()
                        .sendEvent(AnalyticsHandlerAdapter.CATEGORY_BOOKMARK, "User Bookmark Added");
            }
        });

        DividerItemDecoration mDivDecorator = new DividerItemDecoration(getContext());
        mRecyclerViewBookmarks.addItemDecoration(mDivDecorator);

        mItemClickHelper = new ItemClickHelper();
        mItemClickHelper.attachToRecyclerView(mRecyclerViewBookmarks);

        ItemTouchHelper.Callback touchCallback = new BookmarkItemTouchHelperCallback(mBookmarkViewAdapter, 1, true,
                false);
        mItemTouchHelper = new ItemTouchHelper(touchCallback);
        mItemTouchHelper.attachToRecyclerView(mRecyclerViewBookmarks);

        mItemClickHelper.setOnItemClickListener(new ItemClickHelper.OnItemClickListener() {
            @Override
            public void onItemClick(RecyclerView recyclerView, View view, int position, long id) {
                if (mListener != null) {
                    mListener.onUserBookmarkClicked(mBookmarkViewAdapter.getItem(position).pageNumber);

                    com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance().sendEvent(
                            AnalyticsHandlerAdapter.CATEGORY_BOOKMARK, "Navigated by User Bookmark List");
                }
            }
        });

        mItemClickHelper.setOnItemLongClickListener(new ItemClickHelper.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(RecyclerView recyclerView, View v, final int position, final long id) {

                mRecyclerViewBookmarks.post(new Runnable() {
                    @Override
                    public void run() {
                        mIsDragging = true;
                        RecyclerView.ViewHolder holder = mRecyclerViewBookmarks
                                .findViewHolderForAdapterPosition(position);
                        mItemTouchHelper.startDrag(holder);
                    }
                });

                return true;
            }
        });

        return view;
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        if (v.getId() == R.id.controls_bookmark_recycler_view) {
            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
            UserBookmarkItem item = mBookmarkViewAdapter.getItem(info.position);
            menu.setHeaderTitle(item.title);
            String[] menuItems = getResources().getStringArray(R.array.user_bookmark_dialog_context_menu);
            menu.add(Menu.NONE, CONTEXT_MENU_EDIT_ITEM, CONTEXT_MENU_EDIT_ITEM, menuItems[CONTEXT_MENU_EDIT_ITEM]);
            menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ITEM, CONTEXT_MENU_DELETE_ITEM,
                    menuItems[CONTEXT_MENU_DELETE_ITEM]);
            menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ALL, CONTEXT_MENU_DELETE_ALL,
                    menuItems[CONTEXT_MENU_DELETE_ALL]);

            MenuItem.OnMenuItemClickListener listener = new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {
                    onContextItemSelected(item);
                    return true;
                }
            };
            menu.getItem(CONTEXT_MENU_EDIT_ITEM).setOnMenuItemClickListener(listener);
            menu.getItem(CONTEXT_MENU_DELETE_ITEM).setOnMenuItemClickListener(listener);
            menu.getItem(CONTEXT_MENU_DELETE_ALL).setOnMenuItemClickListener(listener);
        }
    }

    public boolean onPopupItemSelected(MenuItem item, int position) {
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        int menuItemIndex = item.getItemId();
        switch (menuItemIndex) {
        case CONTEXT_MENU_EDIT_ITEM:
            mModified = true;

            mBookmarkViewAdapter.setEditMode(true);
            mBookmarkViewAdapter.setSelectedIndex(position);
            mBookmarkViewAdapter.notifyDataSetChanged();

            com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance()
                    .sendEvent(AnalyticsHandlerAdapter.CATEGORY_BOOKMARK, "User Bookmark Rename Clicked");
            break;
        case CONTEXT_MENU_DELETE_ITEM:
            mModified = true;

            if (!mReadOnly) {
                boolean needUnlock = false;
                try {
                    mPDFDoc.lock();
                    needUnlock = true;
                    if (mBookmarkViewAdapter.getItem(position).pdfBookmark != null) {
                        mBookmarkViewAdapter.getItem(position).pdfBookmark.delete();
                    }
                } catch (Exception e) {

                } finally {
                    if (needUnlock) {
                        try {
                            mPDFDoc.unlock();
                        } catch (PDFNetException ex) {
                        }
                    }
                }
            }
            mBookmarkViewAdapter.remove(mBookmarkViewAdapter.getItem(position));
            mBookmarkViewAdapter.notifyDataSetChanged();

            com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance()
                    .sendEvent(AnalyticsHandlerAdapter.CATEGORY_BOOKMARK, "User Bookmark Delete Clicked");
            break;
        case CONTEXT_MENU_DELETE_ALL:
            mModified = true;

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setMessage(R.string.controls_bookmark_dialog_delete_all_message)
                    .setTitle(R.string.controls_misc_delete_all)
                    .setPositiveButton(R.string.tools_misc_yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            if (!mReadOnly) {
                                removeRootPDFBookmark(mPDFDoc);
                            }
                            mBookmarkViewAdapter.clear();
                            mBookmarkViewAdapter.notifyDataSetChanged();

                            com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance().sendEvent(
                                    AnalyticsHandlerAdapter.CATEGORY_BOOKMARK, "User Bookmark Delete All Clicked");
                        }
                    }).setNegativeButton(R.string.controls_misc_cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // do nothing
                        }
                    }).create().show();
            break;
        }
        return true;
    }

    private void loadBookmarks(Context context) {
        if (mTask != null) {
            if (!(mTask.getStatus() == AsyncTask.Status.RUNNING)) {
                if (mReadOnly) {
                    mTask = new PopulateUserBookmarkListTask(context, mCurrentFilePath);
                } else {
                    Bookmark rootBookmark = getRootPDFBookmark(mPDFDoc, false);
                    mTask = new PopulateUserBookmarkListTask(context, mCurrentFilePath, mPDFDoc, rootBookmark);
                }
                mTask.execute();
            }
        } else {
            if (mReadOnly) {
                mTask = new PopulateUserBookmarkListTask(context, mCurrentFilePath);
            } else {
                Bookmark rootBookmark = getRootPDFBookmark(mPDFDoc, false);
                mTask = new PopulateUserBookmarkListTask(context, mCurrentFilePath, mPDFDoc, rootBookmark);
            }
            mTask.execute();
        }
    }

    private void commitData(Context context) {
        if (!mModified) {
            return;
        }
        if (mReadOnly) {
            UserBookmarkDialogFragment.saveUserBookmarks(context, mCurrentFilePath, mSource);
        } else {
            UserBookmarkDialogFragment.savePDFBookmarks(mPDFDoc, mSource);
        }
    }

    // START Bookmark using Bookmark object
    public static Bookmark getRootPDFBookmark(PDFDoc pdfDoc, boolean createNew) {
        Bookmark bookmark = null;
        if (null != pdfDoc) {
            try {
                Obj catalog = pdfDoc.getRoot();
                Obj bookmark_obj = catalog.findObj(KEY_PREF_USER_BOOKMARK_OBJ_TITLE);
                if (null != bookmark_obj) {
                    // found existing bookmark obj
                    bookmark = new Bookmark(bookmark_obj);
                } else {
                    if (createNew) {
                        // create new bookmark obj
                        bookmark = Bookmark.create(pdfDoc, KEY_PREF_USER_BOOKMARK_OBJ_TITLE);
                        pdfDoc.getRoot().put(KEY_PREF_USER_BOOKMARK_OBJ_TITLE, bookmark.getSDFObj());
                    }
                }
            } catch (PDFNetException e) {
                bookmark = null;
            }
        }
        return bookmark;
    }

    public static List<UserBookmarkItem> getPDFBookmarks(Bookmark rootBookmark) {
        ArrayList<UserBookmarkItem> data = new ArrayList<UserBookmarkItem>();
        if (null != rootBookmark) {
            try {
                if (rootBookmark.hasChildren()) {
                    Bookmark item = rootBookmark.getFirstChild();
                    for (; item.isValid(); item = item.getNext()) {
                        UserBookmarkItem bookmarkItem = new UserBookmarkItem();
                        bookmarkItem.isBookmarkEdited = false;
                        bookmarkItem.pdfBookmark = item;
                        bookmarkItem.title = item.getTitle();
                        Action action = item.getAction();
                        if (null != action && action.isValid()) {
                            if (action.getType() == Action.e_GoTo) {
                                Destination dest = action.getDest();
                                if (null != dest && dest.isValid()) {
                                    bookmarkItem.pageNumber = dest.getPage().getIndex();
                                    bookmarkItem.pageObjNum = dest.getPage().getSDFObj().getObjNum();
                                    data.add(bookmarkItem);
                                }
                            }
                        }
                    }
                }
            } catch (PDFNetException ex) {
                AnalyticsHandlerAdapter.getInstance().sendException(ex);
                Log.e("PDFNet", ex.getMessage());
            }
        }
        return data;
    }

    public static void savePDFBookmarks(PDFDoc pdfDoc, List<UserBookmarkItem> data) {
        if (data.size() > 0) {
            Bookmark rootBookmark = getRootPDFBookmark(pdfDoc, true);
            Bookmark firstBookmark = null;
            Bookmark currentBookmark = null;

            if (null != rootBookmark) {
                boolean needUnlock = false;
                try {
                    if (rootBookmark.hasChildren()) {
                        firstBookmark = rootBookmark.getFirstChild();
                    }
                    pdfDoc.lock();
                    needUnlock = true;

                    if (mRebuild) {
                        Obj catalog = pdfDoc.getRoot();
                        if (catalog != null) {
                            catalog.erase(KEY_PREF_USER_BOOKMARK_OBJ_TITLE);
                        }
                        rootBookmark = getRootPDFBookmark(pdfDoc, true);
                        firstBookmark = null;
                        mRebuild = false;
                    }
                    for (UserBookmarkItem item : data) {
                        if (null == item.pdfBookmark) {
                            if (null == currentBookmark) {
                                // No items in the list above this on are currently in the document
                                if (null == firstBookmark) {
                                    // this means there are no bookmarks at all, so create one.
                                    currentBookmark = rootBookmark.addChild(item.title);
                                    currentBookmark.setAction(Action
                                            .createGoto(Destination.createFit(pdfDoc.getPage(item.pageNumber))));
                                    firstBookmark = currentBookmark;
                                } else {
                                    // there already are bookmarks, so the new bookmark needs to be inserted in front of the first one
                                    currentBookmark = firstBookmark.addPrev(item.title);
                                    currentBookmark.setAction(Action
                                            .createGoto(Destination.createFit(pdfDoc.getPage(item.pageNumber))));
                                    firstBookmark = currentBookmark;
                                }
                            } else {
                                // at least one item in the list above the current item was in the list
                                currentBookmark = currentBookmark.addNext(item.title);
                                currentBookmark.setAction(
                                        Action.createGoto(Destination.createFit(pdfDoc.getPage(item.pageNumber))));
                            }
                            item.pdfBookmark = currentBookmark;
                        } else {
                            currentBookmark = item.pdfBookmark;
                            if (item.isBookmarkEdited) {
                                Action action = item.pdfBookmark.getAction();
                                Destination dest = action.getDest();
                                dest.setPage(pdfDoc.getPage(item.pageNumber));
                                item.pdfBookmark.setTitle(item.title);
                            }
                        }
                    }

                } catch (Exception ex) {
                    AnalyticsHandlerAdapter.getInstance().sendException(ex);
                    Log.e("PDFNet", ex.getMessage());
                } finally {
                    if (needUnlock) {
                        try {
                            pdfDoc.unlock();
                        } catch (PDFNetException e) {
                        }
                    }
                }
            }
        } else {
            removeRootPDFBookmark(pdfDoc);
        }
    }

    public static boolean removeRootPDFBookmark(PDFDoc pdfDoc) {
        boolean needUnlock = false;
        try {
            pdfDoc.lock();
            needUnlock = true;
            Obj catalog = pdfDoc.getRoot();
            if (catalog != null) {
                catalog.erase(KEY_PREF_USER_BOOKMARK_OBJ_TITLE);
            }
        } catch (PDFNetException e) {
            return false;
        } finally {
            if (needUnlock) {
                try {
                    pdfDoc.unlock();
                } catch (PDFNetException ex) {
                }
            }
        }
        return true;
    }

    private static List<UserBookmarkItem> getPDFData(Bookmark rootBookmark) {
        return UserBookmarkDialogFragment.getPDFBookmarks(rootBookmark);
    }

    public static void pageDeleted(PDFDoc pdfDoc, Long objNumber) {
        List<UserBookmarkItem> items = UserBookmarkDialogFragment
                .getPDFBookmarks(getRootPDFBookmark(pdfDoc, false));
        boolean needUnlock = false;
        try {
            pdfDoc.lock();
            needUnlock = true;
            for (UserBookmarkItem item : items) {
                if (item.pageObjNum == objNumber) {
                    item.pdfBookmark.delete();
                }
            }
        } catch (Exception e) {

        } finally {
            if (needUnlock) {
                try {
                    pdfDoc.unlock();
                } catch (PDFNetException ex) {
                }
            }
        }
    }

    public static void pageMoved(PDFDoc pdfDoc, long objNumber, long newObjNumber, int newPageNumber) {
        List<UserBookmarkItem> items = UserBookmarkDialogFragment
                .getPDFBookmarks(getRootPDFBookmark(pdfDoc, false));
        boolean needUnlock = false;
        try {
            pdfDoc.lock();
            needUnlock = true;
            for (UserBookmarkItem item : items) {
                if (item.pageObjNum == objNumber) {
                    item.pageObjNum = newObjNumber;
                    item.pageNumber = newPageNumber;
                    item.pdfBookmark.delete();
                    item.pdfBookmark = null;
                    break;
                }
            }
            savePDFBookmarks(pdfDoc, items);
        } catch (Exception e) {

        } finally {
            if (needUnlock) {
                try {
                    pdfDoc.unlock();
                } catch (PDFNetException ex) {
                }
            }
        }

    }
    // END Bookmark using Bookmark object

    // START Bookmark using SharedPreferences
    public static void saveUserBookmarks(Context context, String filePath, List<UserBookmarkItem> data) {
        SharedPreferences settings = context.getSharedPreferences(PREFS_CONTROLS_FILE_NAME, 0);
        SharedPreferences.Editor editor = settings.edit();
        Gson gson = new Gson();
        Type collectionType = new TypeToken<ArrayList<UserBookmarkItem>>() {
        }.getType();
        String serializedDocs = gson.toJson(data, collectionType);

        editor.putString(KEY_PREF_USER_BOOKMARK + filePath, serializedDocs);
        editor.apply();
    }

    public static void removeUserBookmarks(Context context, String filePath) {
        SharedPreferences settings = context.getSharedPreferences(PREFS_CONTROLS_FILE_NAME, 0);
        SharedPreferences.Editor editor = settings.edit();
        editor.remove(KEY_PREF_USER_BOOKMARK + filePath);
        editor.apply();
    }

    public static List<UserBookmarkItem> getUserBookmarks(Context context, String filePath) {
        SharedPreferences settings = context.getSharedPreferences(PREFS_CONTROLS_FILE_NAME, 0);
        String serializedDocs = settings.getString(KEY_PREF_USER_BOOKMARK + filePath, "");
        Gson gson = new Gson();
        Type collectionType = new TypeToken<ArrayList<UserBookmarkItem>>() {
        }.getType();
        ArrayList<UserBookmarkItem> data = null;
        try {
            data = gson.fromJson(serializedDocs, collectionType);
        } catch (Exception e) {
            AnalyticsHandlerAdapter.getInstance().sendException(e);
        }
        return data != null ? data : new ArrayList<UserBookmarkItem>();
    }

    public static void updateUserBookmarkPageObj(Context context, String filePath, long pageObjNum,
            long newPageObjNum, int newPageNum) {
        List<UserBookmarkItem> items = UserBookmarkDialogFragment.getUserBookmarks(context, filePath);
        for (UserBookmarkItem item : items) {
            if (item.pageObjNum == pageObjNum) {
                item.pageObjNum = newPageObjNum;
                item.pageNumber = newPageNum;
            }
        }
        UserBookmarkDialogFragment.saveUserBookmarks(context, filePath, items);
    }

    public static void pageDeleted(Context context, String filePath, Long objNumber, int pageNumber,
            int pageCount) {
        List<UserBookmarkItem> items = UserBookmarkDialogFragment.getUserBookmarks(context, filePath);
        List<UserBookmarkItem> newItems = new ArrayList<>();
        for (UserBookmarkItem item : items) {
            if (item.pageObjNum != objNumber) {
                newItems.add(item);
            }
        }
        UserBookmarkDialogFragment.saveUserBookmarks(context, filePath, newItems);
        updateBookmarksAfterRearranging(context, filePath, pageNumber, pageCount, false, -1);
    }

    public static void pageMoved(Context context, String filePath, long objNumber, long newObjNumber,
            int oldPageNumber, int newPageNumber) {
        updateUserBookmarkPageObj(context, filePath, objNumber, newObjNumber, newPageNumber);
        if (oldPageNumber < newPageNumber) {
            updateBookmarksAfterRearranging(context, filePath, oldPageNumber + 1, newPageNumber, false,
                    newObjNumber);
        } else {
            updateBookmarksAfterRearranging(context, filePath, newPageNumber, oldPageNumber - 1, true,
                    newObjNumber);
        }
    }

    public static void updateBookmarksAfterRearranging(Context context, String filePath, int fromPage, int toPage,
            boolean increment, long ignoreObjNumber) {
        if (fromPage > toPage) {
            int temp = fromPage;
            fromPage = toPage;
            toPage = temp;
        }
        int change = -1;
        if (increment) {
            change = 1;
        }
        List<UserBookmarkItem> items = UserBookmarkDialogFragment.getUserBookmarks(context, filePath);
        for (UserBookmarkItem item : items) {
            if (item.pageNumber >= fromPage && item.pageNumber <= toPage && item.pageObjNum != ignoreObjNumber) {
                item.pageNumber += change;
            }
        }
        UserBookmarkDialogFragment.saveUserBookmarks(context, filePath, items);
    }

    public static void updateUserBookmarksFilePath(Context context, String oldPath, String newPath) {
        List<UserBookmarkItem> items = UserBookmarkDialogFragment.getUserBookmarks(context, oldPath);
        if (items.size() > 0) {
            UserBookmarkDialogFragment.saveUserBookmarks(context, newPath, items);
            UserBookmarkDialogFragment.removeUserBookmarks(context, oldPath);
        }
    }

    private static List<UserBookmarkItem> getData(Context context, String filePath) {
        return UserBookmarkDialogFragment.getUserBookmarks(context, filePath);
    }

    // END Bookmark using SharedPreferences

    private class PopulateUserBookmarkListTask extends AsyncTask<Void, Void, Void> {
        private Context mContext;
        private String mFilePath;
        private List<UserBookmarkItem> mBookmarkList;
        private boolean mReadOnly;
        private Bookmark mBookmark;

        public PopulateUserBookmarkListTask(Context context, String filePath) {
            mContext = context;
            mBookmarkList = new ArrayList<UserBookmarkItem>();
            mFilePath = filePath;
            mReadOnly = true;
        }

        public PopulateUserBookmarkListTask(Context context, String filePath, PDFDoc pdfDoc, Bookmark bookmark) {
            mContext = context;
            mBookmarkList = new ArrayList<UserBookmarkItem>();
            mPDFDoc = pdfDoc;
            mBookmark = bookmark;
            mFilePath = filePath;
            mReadOnly = false;
        }

        @Override
        protected Void doInBackground(Void... params) {
            if (mReadOnly) {
                mBookmarkList.addAll(getData(mContext, mFilePath));
            } else {
                mBookmarkList.addAll(getPDFData(mBookmark));
                if (mBookmarkList.isEmpty()) {
                    // Backwards compatibility
                    // try to see if there were bookmarks from previous releases
                    mBookmarkList.addAll(getData(mContext, mFilePath));
                    if (!mBookmarkList.isEmpty()) {
                        mModified = true;
                        // after porting, remove the old bookmarks
                        removeUserBookmarks(mContext, mFilePath);
                    }
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            if (Utils.isHoneycomb()) {
                mBookmarkViewAdapter.clear();
                mBookmarkViewAdapter.addAll(mBookmarkList);
            } else {
                mSource.clear();
                mSource.addAll(mBookmarkList);
            }
            mBookmarkViewAdapter.notifyDataSetChanged();
        }
    }

    public class UserBookmarkViewAdapter
            extends SimpleRecyclerViewAdapter<UserBookmarkItem, UserBookmarkViewAdapter.PageViewHolder>
            implements ItemTouchHelperAdapter {

        private ArrayList<UserBookmarkItem> mBookmarks;
        private Context mContext;
        private boolean mEditMode;
        private int mSelectedIndex = -1;

        public UserBookmarkViewAdapter(Context context, ArrayList<UserBookmarkItem> objects,
                ViewHolderBindListener bindListener) {
            super(bindListener);

            mContext = context;
            mBookmarks = objects;
            mEditMode = false;

        }

        public void clear() {
            mBookmarks.clear();
        }

        public void addAll(List<UserBookmarkItem> listBookmarks) {
            mBookmarks.addAll(listBookmarks);
        }

        public void setEditMode(boolean editMode) {
            mEditMode = editMode;
            if (editMode) {
                unregisterForContextMenu(mRecyclerViewBookmarks);
                mButtonAdd.setVisibility(View.GONE);
            } else {
                registerForContextMenu(mRecyclerViewBookmarks);
                mButtonAdd.setVisibility(View.VISIBLE);
            }
        }

        public void setSelectedIndex(int index) {
            mSelectedIndex = index;
        }

        @Override
        public boolean onItemMove(int fromPosition, int toPosition) {
            UserBookmarkItem oldItem = mBookmarks.get(fromPosition);
            UserBookmarkItem item = new UserBookmarkItem();
            item.pageObjNum = oldItem.pageObjNum;
            item.pageNumber = oldItem.pageNumber;
            item.title = oldItem.title;

            for (UserBookmarkItem uitem : mBookmarks) {
                uitem.pdfBookmark = null;
            }
            mRebuild = true;

            mBookmarks.remove(fromPosition);
            mBookmarks.add(toPosition, item);

            notifyItemMoved(fromPosition, toPosition);
            mModified = true;

            return true;
        }

        @Override
        public void onItemDrop(int fromPosition, int toPosition) {

        }

        @Override
        public void onItemDismiss(int position) {

        }

        @Override
        public UserBookmarkItem getItem(int position) {
            return mBookmarks.get(position);
        }

        @Override
        public void add(UserBookmarkItem item) {
            mBookmarks.add(item);
        }

        @Override
        public boolean remove(UserBookmarkItem item) {
            if (mBookmarks.contains(item)) {
                mBookmarks.remove(item);
                return true;
            }
            return false;
        }

        @Override
        public UserBookmarkItem removeAt(int location) {
            if (location < mBookmarks.size()) {
                return mBookmarks.remove(location);
            }
            return null;
        }

        @Override
        public void insert(UserBookmarkItem item, int position) {
            mBookmarks.add(position, item);
        }

        @Override
        public void updateSpanCount(int count) {

        }

        @Override
        public UserBookmarkViewAdapter.PageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(mContext).inflate(R.layout.controls_fragment_bookmark_listview_item,
                    parent, false);
            return new PageViewHolder(view, UserBookmarkViewAdapter.this);
        }

        @Override
        public void onBindViewHolder(UserBookmarkViewAdapter.PageViewHolder holder, final int position) {
            super.onBindViewHolder(holder, position);

            UserBookmarkItem item = mBookmarks.get(position);

            holder.itemView.getBackground().setColorFilter(null);
            holder.itemView.getBackground().invalidateSelf();
            holder.bookmarkTextView.setText(item.title);

            if (mEditMode) {
                if (position == mSelectedIndex) {
                    holder.bookmarkTextView.setVisibility(View.GONE);
                    holder.bookmarkEditText.setVisibility(View.VISIBLE);
                    holder.bookmarkEditText.setText(item.title);
                    holder.bookmarkEditText.requestFocus();
                    holder.bookmarkEditText.selectAll();
                    showSoftInput();

                    // commit changes
                    holder.bookmarkEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
                        @Override
                        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                            if (actionId == EditorInfo.IME_ACTION_DONE) {
                                hideSoftInput(v);

                                saveEditTextChanges(v, position);
                                return true;
                            }
                            return false;
                        }
                    });
                }
            } else {
                holder.bookmarkEditText.clearFocus();
                holder.bookmarkTextView.setVisibility(View.VISIBLE);
                holder.bookmarkEditText.setVisibility(View.GONE);
            }
        }

        @Override
        public int getItemCount() {
            return mBookmarks.size();
        }

        private void saveEditTextChanges(TextView v, int position) {
            v.clearFocus();

            setEditMode(false);
            String title = v.getText().toString();
            if (title.isEmpty()) {
                title = mContext.getString(R.string.controls_bookmark_dialog_default_empty_title);
            }
            mBookmarkViewAdapter.getItem(position).title = title;
            mBookmarkViewAdapter.getItem(position).isBookmarkEdited = true;
            notifyDataSetChanged();
        }

        private void hideSoftInput(View view) {
            InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }

        private void showSoftInput() {
            InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
        }

        public class PageViewHolder extends RecyclerView.ViewHolder {

            public TextView bookmarkTextView;
            public CustomEditText bookmarkEditText;
            public ImageButton contextButton;

            public PageViewHolder(View itemView, UserBookmarkViewAdapter adapter) {
                super(itemView);
                this.bookmarkTextView = (TextView) itemView
                        .findViewById(R.id.control_bookmark_listview_item_textview);
                this.bookmarkEditText = (CustomEditText) itemView
                        .findViewById(R.id.control_bookmark_listview_item_edittext);
                this.contextButton = (ImageButton) itemView
                        .findViewById(R.id.control_bookmark_listview_item_context_button);
                this.contextButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        UserBookmarkDialogFragment.this.onShowPopupMenu(getPosition(), v);
                    }
                });
            }

        }
    }

    static class UserBookmarkItem {
        protected int pageNumber;
        protected long pageObjNum;
        protected String title;
        protected boolean isBookmarkEdited;
        protected Bookmark pdfBookmark;
    }

    private class BookmarkItemTouchHelperCallback extends SimpleItemTouchHelperCallback {
        public BookmarkItemTouchHelperCallback(ItemTouchHelperAdapter adapter, int span, boolean enableLongPress,
                boolean enableSwipe) {
            super(adapter, span, enableLongPress, enableSwipe);
        }

        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            // Set movement flags based on the layout manager
            if (viewHolder.getItemViewType() == VIEW_TYPE_HEADER) {
                return makeMovementFlags(0, 0);
            }
            final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            final int swipeFlags = 0;
            return makeMovementFlags(dragFlags, swipeFlags);
        }

        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX,
                float dY, int actionState, boolean isCurrentlyActive) {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            if (mIsDragging) {
                viewHolder.itemView.getBackground().setColorFilter(getResources().getColor(R.color.gray),
                        PorterDuff.Mode.MULTIPLY);
                viewHolder.itemView.getBackground().invalidateSelf();
            }
        }

        @Override
        public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            super.clearView(recyclerView, viewHolder);
            if (mIsDragging) {
                viewHolder.itemView.getBackground().setColorFilter(null);
                viewHolder.itemView.getBackground().invalidateSelf();
                mIsDragging = false;
            }
        }
    }
}