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

Java tutorial

Introduction

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

Source

//---------------------------------------------------------------------------------------
// Copyright (c) 2001-2016 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.content.Context;
import android.database.DataSetObserver;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.pdftron.common.PDFNetException;
import com.pdftron.pdf.Annot;
import com.pdftron.pdf.PDFDoc;
import com.pdftron.pdf.PDFViewCtrl;
import com.pdftron.pdf.Page;
import com.pdftron.pdf.PageIterator;
import com.pdftron.pdf.TextExtractor;
import com.pdftron.pdf.annots.Markup;
import com.pdftron.pdf.annots.Popup;
import com.pdftron.pdf.tools.R;
import com.pdftron.pdf.utils.AnalyticsHandlerAdapter;
import com.pdftron.pdf.utils.ViewerUtils;

import java.util.ArrayList;

/**
 * The AnnotationDialogFragment shows a list of all the annotations in a
 * document being viewed by a {@link com.pdftron.pdf.PDFViewCtrl}. The list will
 * contain any comments that have been added to the annotations and clicking on
 * an annotation will show it in the PDFViewCtrl.
 */
public class AnnotationDialogFragment extends DialogFragment {

    private static final int CONTEXT_MENU_DELETE_ITEM = 0;
    private static final int CONTEXT_MENU_DELETE_ITEM_ONPAGE = 1;
    private static final int CONTEXT_MENU_DELETE_ALL = 2;

    public interface AnnotationDialogFragmentListener {
        void onAnnotationClicked(Annot annotation, int pageNum);

        void onExportAnnotationsClicked();
    }

    private AnnotationDialogFragmentListener mListener;

    private ArrayList<AnnotationInfo> mAnnotation;
    private AnnotationListAdapter mAnnotationListAdapter;
    private ListView mListViewAnnotation;
    private PopulateAnnotationInfoListTask mPopulateAnnotationListTask;

    private TextView mEmptyTextView;

    private PDFViewCtrl mPDFViewCtrl;

    private boolean mDebug = false;

    public static AnnotationDialogFragment newInstance(PDFViewCtrl pdfViewCtrl) {
        AnnotationDialogFragment f = new AnnotationDialogFragment();
        f.setPDFViewCtrl(pdfViewCtrl);
        return f;
    }

    public void setPDFViewCtrl(PDFViewCtrl pdfViewCtrl) {
        if (pdfViewCtrl == null) {
            throw new NullPointerException("pdfViewCtrl can't be null");
        }
        mPDFViewCtrl = pdfViewCtrl;
    }

    public void setListener(AnnotationDialogFragmentListener listener) {
        mListener = listener;
    }

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

    @Override
    public void onStop() {
        if (mPopulateAnnotationListTask != null) {
            mPopulateAnnotationListTask.cancel(true);
        }
        super.onStop();
    }

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

        // Get reference to controls
        mListViewAnnotation = (ListView) view.findViewById(R.id.control_annotation_listview);
        mEmptyTextView = (TextView) view.findViewById(R.id.control_annotation_textview_empty);
        mListViewAnnotation.setEmptyView(mEmptyTextView);

        ImageButton exportButton = (ImageButton) view.findViewById(R.id.export_annotations_button);
        exportButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListener != null) {
                    mListener.onExportAnnotationsClicked();
                }
            }
        });

        // Add click listener to the list
        mListViewAnnotation.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

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

                AnnotationInfo annotInfo = mAnnotation.get(position);

                if (mPDFViewCtrl != null) {
                    ViewerUtils.jumpToAnnotation(mPDFViewCtrl, annotInfo.getAnnotation(), annotInfo.getPageNum());
                }

                // Notify listeners
                if (mListener != null) {
                    mListener.onAnnotationClicked(annotInfo.getAnnotation(), annotInfo.getPageNum());
                }
            }
        });
        registerForContextMenu(mListViewAnnotation);

        return view;
    }

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

        mAnnotation = new ArrayList<AnnotationInfo>();
        mAnnotationListAdapter = new AnnotationListAdapter(getActivity(),
                R.layout.controls_fragment_annotation_listview_item, mAnnotation);
        mListViewAnnotation.setAdapter(mAnnotationListAdapter);

        // This task will populate mAnnotation
        mPopulateAnnotationListTask = new PopulateAnnotationInfoListTask();
        mPopulateAnnotationListTask.execute();
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);

        if (v.getId() == R.id.control_annotation_listview) {
            AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
            AnnotationInfo item = mAnnotationListAdapter.getItem(info.position);
            String title = String.format(getResources().getString(R.string.controls_annotation_dialog_page),
                    item.getPageNum());
            if (item.getAuthor() != null && !item.getAuthor().isEmpty()) {
                title = title + " " + getResources().getString(R.string.controls_annotation_dialog_author) + " "
                        + item.getAuthor();
            }
            menu.setHeaderTitle(title);
            String[] menuItems = getResources().getStringArray(R.array.annotation_dialog_context_menu);
            menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ITEM, CONTEXT_MENU_DELETE_ITEM,
                    menuItems[CONTEXT_MENU_DELETE_ITEM + 1]);
            if (mDebug) {
                // Convenient for debugging
                menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ITEM_ONPAGE, CONTEXT_MENU_DELETE_ITEM_ONPAGE,
                        menuItems[CONTEXT_MENU_DELETE_ITEM_ONPAGE + 1]);
                menu.add(Menu.NONE, CONTEXT_MENU_DELETE_ALL, CONTEXT_MENU_DELETE_ALL,
                        menuItems[CONTEXT_MENU_DELETE_ALL + 1]);
            }
            MenuItem.OnMenuItemClickListener listener = new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem item) {
                    onContextItemSelected(item);
                    return true;
                }
            };
            menu.getItem(CONTEXT_MENU_DELETE_ITEM).setOnMenuItemClickListener(listener);
            if (mDebug) {
                menu.getItem(CONTEXT_MENU_DELETE_ITEM_ONPAGE).setOnMenuItemClickListener(listener);
                menu.getItem(CONTEXT_MENU_DELETE_ALL).setOnMenuItemClickListener(listener);
            }
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        int menuItemIndex = item.getItemId();
        switch (menuItemIndex) {
        case CONTEXT_MENU_DELETE_ITEM:
            AnnotationInfo annotationInfo = mAnnotationListAdapter.getItem(info.position);
            if (annotationInfo.getAnnotation() != null) {
                try {
                    // Locks the document first as accessing annotation/doc information isn't thread
                    // safe.
                    mPDFViewCtrl.docLock(true);
                    Page page = mPDFViewCtrl.getDoc().getPage(annotationInfo.getPageNum());
                    page.annotRemove(annotationInfo.getAnnotation());
                    mPDFViewCtrl.update(annotationInfo.getAnnotation(), annotationInfo.getPageNum());
                    mAnnotationListAdapter.remove(mAnnotationListAdapter.getItem(info.position));
                    mAnnotationListAdapter.notifyDataSetChanged();
                } catch (Exception e) {
                } finally {
                    mPDFViewCtrl.docUnlock();
                }
            }
            com.pdftron.pdf.utils.AnalyticsHandlerAdapter.getInstance()
                    .sendEvent(AnalyticsHandlerAdapter.CATEGORY_ANNOTATIONTOOLBAR, "Annotation Delete Clicked");
            break;
        case CONTEXT_MENU_DELETE_ITEM_ONPAGE:
            if (mDebug) {
                annotationInfo = mAnnotationListAdapter.getItem(info.position);
                if (annotationInfo.getAnnotation() != null) {
                    deleteOnPage(annotationInfo);
                }
            }
            break;
        case CONTEXT_MENU_DELETE_ALL:
            if (mDebug) {
                deleteAll();
            }
            break;
        }
        return super.onContextItemSelected(item);
    }

    public void deleteOnPage(AnnotationInfo annotationInfo) {
        try {
            // Locks the document first as accessing annotation/doc information isn't thread
            // safe.
            mPDFViewCtrl.docLock(true);
            ArrayList<AnnotationInfo> items = mAnnotationListAdapter.getItemsOnPage(annotationInfo.getPageNum());
            for (AnnotationInfo info : items) {
                if (info.getAnnotation() != null) {
                    Page page = mPDFViewCtrl.getDoc().getPage(info.getPageNum());
                    page.annotRemove(info.getAnnotation());
                    mAnnotationListAdapter.remove(info);
                }
            }
            mPDFViewCtrl.update(true);
        } catch (Exception e) {

        } finally {
            mPDFViewCtrl.docUnlock();
        }

        mAnnotationListAdapter.notifyDataSetChanged();
    }

    public void deleteAll() {
        try {
            // Locks the document first as accessing annotation/doc information isn't thread
            // safe.
            mPDFViewCtrl.docLock(true);

            // delete all annots
            PageIterator pageIterator = mPDFViewCtrl.getDoc().getPageIterator();
            while (pageIterator.hasNext()) {
                Page page = (Page) pageIterator.next();
                page.getSDFObj().erase("Annots");
            }
            mPDFViewCtrl.update(true);
        } catch (Exception e) {

        } finally {
            mPDFViewCtrl.docUnlock();
        }

        mAnnotationListAdapter.clear();
        mAnnotationListAdapter.notifyDataSetChanged();
    }

    public static int getAnnotationImageResId(int type) {
        int resId = android.R.id.empty;

        switch (type) {
        case Annot.e_Text:
            resId = R.drawable.annotation_sticky_note;
            break;
        case Annot.e_Line:
            resId = R.drawable.annotation_line;
            break;
        case Annot.e_Square:
            resId = R.drawable.annotation_square;
            break;
        case Annot.e_Circle:
            resId = R.drawable.annotation_circle;
            break;
        case Annot.e_Polygon:
            resId = R.drawable.annotation_polygon;
            break;
        case Annot.e_Underline:
            resId = R.drawable.annotation_underline;
            break;
        case Annot.e_StrikeOut:
            resId = R.drawable.annotation_strikeout;
            break;
        case Annot.e_Ink:
            resId = R.drawable.annotation_free_hand;
            break;
        case Annot.e_Highlight:
            resId = R.drawable.annotation_highlight;
            break;
        case Annot.e_FreeText:
            resId = R.drawable.annotation_free_text;
            break;
        case Annot.e_Squiggly:
            resId = R.drawable.annotation_squiggly;
            break;
        case Annot.e_Stamp:
            resId = R.drawable.annotation_rubber_stamp;
            break;
        case Annot.e_Caret:
            resId = R.drawable.annotation_caret;
            break;
        case Annot.e_Polyline:
            resId = R.drawable.annotation_polyline;
            break;
        case Annot.e_Redact:
            resId = R.drawable.annotation_redaction;
            break;
        default:
            break;
        }

        return resId;
    }

    public AnnotationDialogFragment setDebug(boolean debug) {
        mDebug = debug;
        return this;
    }

    private class AnnotationInfo {
        /**
         * The annotation type is one of the types found in com.pdftron.pdf.Annot.
         */
        private int mType;

        /**
         * Holds the page where this annotation is found.
         */
        private int mPageNum;

        /**
         * The contents of the annotation are used in the list view of the
         * BookmarkDialogFragment.
         */
        private String mContent;

        /**
         * The author for this annotation.
         */
        private String mAuthor;

        private Annot mAnnotation;

        /**
         * Default constructor. Creates an empty annotation entry.
         */
        public AnnotationInfo() {
            // TODO Maybe -1, -1, "", ""?
            this(0, 0, "", "", null);
        }

        /**
         * Class constructor specifying the type, page and content of the
         * annotation.
         * 
         * @param type      the type of the annotation
         * @param pageNum   the page where this annotation lies in
         * @param content   the content of the annotation
         * 
         * @see <a href="http://www.pdftron.com/pdfnet/mobile/Javadoc/pdftron/PDF/Annot.html">Class Annot</a>
         */
        public AnnotationInfo(int type, int pageNum, String content, String author, Annot annotation) {
            this.mType = type;
            this.mPageNum = pageNum;
            this.mContent = content;
            this.mAuthor = author;
            this.mAnnotation = annotation;
        }

        /**
         * Get the type of this annotation.
         * 
         * @return the type of the annotation
         * 
         * @see {@link com.pdftron.pdf.Annot}
         */
        public int getType() {
            return mType;
        }

        public void setType(int mType) {
            this.mType = mType;
        }

        public int getPageNum() {
            return mPageNum;
        }

        public void setPageNum(int mPageNum) {
            this.mPageNum = mPageNum;
        }

        public String getContent() {
            return mContent;
        }

        public void setContent(String mContent) {
            this.mContent = mContent;
        }

        public String getAuthor() {
            return mAuthor;
        }

        public void setAuthor(String author) {
            this.mAuthor = author;
        }

        public Annot getAnnotation() {
            return mAnnotation;
        }
    }

    private class AnnotationListAdapter extends ArrayAdapter<AnnotationInfo> {

        private Context mContext;
        private int mLayoutResourceId;
        private ArrayList<AnnotationInfo> mAnnotation;

        private ViewHolder mViewHolder;
        private int[] mCellStates;

        private static final int STATE_UNKNOWN = 0;
        private static final int STATE_SECTIONED_CELL = 1;
        private static final int STATE_REGULAR_CELL = 2;

        private DataSetObserver observer = new DataSetObserver() {
            public void onChanged() {
                mCellStates = mAnnotation == null ? null : new int[mAnnotation.size()];
            };
        };

        public AnnotationListAdapter(Context context, int resource, ArrayList<AnnotationInfo> objects) {
            super(context, resource, objects);

            mContext = context;
            mLayoutResourceId = resource;
            mAnnotation = objects;

            mCellStates = objects == null ? null : new int[objects.size()];

            registerDataSetObserver(observer);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(mLayoutResourceId, null);

                mViewHolder = new ViewHolder();
                mViewHolder.separator = (TextView) convertView
                        .findViewById(R.id.control_annotation_listview_item_separator);
                mViewHolder.icon = (ImageView) convertView
                        .findViewById(R.id.control_annotation_listview_item_imageview);
                mViewHolder.line1 = (TextView) convertView
                        .findViewById(R.id.control_annotation_listview_item_textview1);

                convertView.setTag(mViewHolder);

            } else {
                mViewHolder = (ViewHolder) convertView.getTag();
            }

            boolean needSeparator = false;
            AnnotationInfo annotationInfo = mAnnotation.get(position);

            switch (mCellStates[position]) {
            case STATE_SECTIONED_CELL:
                needSeparator = true;
                break;
            case STATE_REGULAR_CELL:
                needSeparator = false;
                break;
            case STATE_UNKNOWN:
            default:
                if (position == 0) {
                    needSeparator = true;
                } else {
                    AnnotationInfo previousAnnotation = mAnnotation.get(position - 1);
                    if (annotationInfo.getPageNum() != previousAnnotation.getPageNum()) {
                        needSeparator = true;
                    }
                }

                // Cache the result
                mCellStates[position] = needSeparator ? STATE_SECTIONED_CELL : STATE_REGULAR_CELL;
                break;
            }

            if (needSeparator) {
                mViewHolder.separator
                        .setText(String.format(getResources().getString(R.string.controls_annotation_dialog_page),
                                annotationInfo.getPageNum()));
                mViewHolder.separator.setVisibility(View.VISIBLE);
            } else {
                mViewHolder.separator.setVisibility(View.GONE);
            }

            // Author and content
            StringBuilder content = new StringBuilder();
            String author = annotationInfo.getAuthor();
            if (!author.isEmpty()) {
                content.append(
                        getResources().getString(R.string.controls_annotation_dialog_author) + " " + author + ". ");
            }
            content.append(annotationInfo.getContent());
            mViewHolder.line1.setText(content.toString());

            // Set icon based on the annotation type
            mViewHolder.icon.setImageResource(getAnnotationImageResId(annotationInfo.getType()));

            return convertView;
        }

        @Override
        public AnnotationInfo getItem(int position) {
            if (mAnnotation != null) {
                return mAnnotation.get(position);
            } else {
                return super.getItem(position);
            }
        }

        public ArrayList<AnnotationInfo> getItemsOnPage(int pageNum) {
            ArrayList<AnnotationInfo> list = new ArrayList<>();
            if (mAnnotation != null) {
                for (AnnotationInfo info : mAnnotation) {
                    if (info.getPageNum() == pageNum) {
                        list.add(info);
                    }
                }
                return list;
            }
            return null;
        }

        @Override
        public int getCount() {
            if (mAnnotation != null) {
                return mAnnotation.size();
            } else {
                return 0;
            }
        }

        private class ViewHolder {
            public TextView separator;
            public TextView line1;
            public ImageView icon;
        }
    }

    private class PopulateAnnotationInfoListTask extends AsyncTask<Void, Void, ArrayList<AnnotationInfo>> {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            mEmptyTextView.setText(R.string.controls_annotation_dialog_loading);
        }

        @Override
        protected ArrayList<AnnotationInfo> doInBackground(Void... params) {
            ArrayList<AnnotationInfo> annotList = new ArrayList<AnnotationInfo>();
            if (mPDFViewCtrl == null) {
                return annotList;
            }
            PageIterator pageIterator;
            boolean shouldUnlock = false;
            try {
                mPDFViewCtrl.docLockRead();
                shouldUnlock = true;
                PDFDoc doc = mPDFViewCtrl.getDoc();
                if (doc != null) {
                    pageIterator = mPDFViewCtrl.getDoc().getPageIterator(1);

                    int pageNum = 0;
                    TextExtractor textExtractor = new TextExtractor();

                    while (pageIterator.hasNext()) {
                        if (isCancelled()) {
                            break;
                        }

                        pageNum++;
                        Page page = (Page) pageIterator.next();

                        if (page.isValid()) {
                            int annotationCount = page.getNumAnnots();
                            for (int a = 0; a < annotationCount; a++) {
                                if (isCancelled()) {
                                    return annotList;
                                }

                                try {
                                    Annot annotation = page.getAnnot(a);
                                    String contents = "";
                                    int type = annotation.getType();

                                    if (annotation == null || !annotation.isValid()) {
                                        continue;
                                    }
                                    if (getAnnotationImageResId(type) == android.R.id.empty) {
                                        continue;
                                    }

                                    Markup markup = new Markup(annotation);
                                    switch (type) {
                                    case Annot.e_FreeText:
                                        contents = annotation.getContents();
                                        break;
                                    case Annot.e_Line:
                                    case Annot.e_Square:
                                    case Annot.e_Circle:
                                    case Annot.e_Polygon:
                                    case Annot.e_Text:
                                    case Annot.e_Ink:
                                        Popup popup = markup.getPopup();
                                        if (popup.isValid()) {
                                            contents = popup.getContents();
                                        }
                                        break;
                                    case Annot.e_Underline:
                                    case Annot.e_StrikeOut:
                                    case Annot.e_Highlight:
                                    case Annot.e_Squiggly:
                                        // For text markup we show the text itself as the contents
                                        textExtractor.begin(page);
                                        contents = textExtractor.getTextUnderAnnot(annotation);
                                        break;
                                    default:
                                        break;
                                    }
                                    annotList.add(new AnnotationInfo(type, pageNum, contents, markup.getTitle(),
                                            annotation));
                                } catch (PDFNetException e) {
                                    // this annotation has some problem, let's skip it and continue with others
                                }
                            }
                        }
                    }
                }
            } catch (PDFNetException e) {
                // TODO Perform action on the exception
            } finally {
                if (shouldUnlock) {
                    mPDFViewCtrl.docUnlockRead();
                    shouldUnlock = false;
                }
            }

            return annotList;
        }

        @Override
        protected void onPostExecute(ArrayList<AnnotationInfo> result) {
            mAnnotation.clear();
            mAnnotation.addAll(result);
            mAnnotationListAdapter.notifyDataSetChanged();

            mEmptyTextView.setText(R.string.controls_annotation_dialog_empty);
        }
    }
}