Java tutorial
//--------------------------------------------------------------------------------------- // 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); } } }