2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.printspooler.ui;
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.drawable.BitmapDrawable;
23 import android.os.ParcelFileDescriptor;
24 import android.print.PageRange;
25 import android.print.PrintAttributes.MediaSize;
26 import android.print.PrintAttributes.Margins;
27 import android.print.PrintDocumentInfo;
28 import android.support.v7.widget.RecyclerView.Adapter;
29 import android.support.v7.widget.RecyclerView.ViewHolder;
30 import android.util.Log;
31 import android.util.SparseArray;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.View.OnClickListener;
35 import android.view.ViewGroup;
36 import android.view.ViewGroup.LayoutParams;
37 import android.view.View.MeasureSpec;
38 import android.widget.TextView;
39 import com.android.printspooler.R;
40 import com.android.printspooler.model.PageContentRepository;
41 import com.android.printspooler.model.PageContentRepository.PageContentProvider;
42 import com.android.printspooler.util.PageRangeUtils;
43 import com.android.printspooler.widget.PageContentView;
44 import com.android.printspooler.widget.PreviewPageFrame;
45 import dalvik.system.CloseGuard;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.List;
52 * This class represents the adapter for the pages in the print preview list.
54 public final class PageAdapter extends Adapter implements
55 PageContentRepository.OnMalformedPdfFileListener {
56 private static final String LOG_TAG = "PageAdapter";
58 private static final int MAX_PREVIEW_PAGES_BATCH = 50;
60 private static final boolean DEBUG = false;
62 private static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {
66 private static final int INVALID_PAGE_INDEX = -1;
68 private static final int STATE_CLOSED = 0;
69 private static final int STATE_OPENED = 1;
70 private static final int STATE_DESTROYED = 2;
72 private final CloseGuard mCloseGuard = CloseGuard.get();
74 private final SparseArray<Void> mBoundPagesInAdapter = new SparseArray<>();
75 private final SparseArray<Void> mConfirmedPagesInDocument = new SparseArray<>();
77 private final PageClickListener mPageClickListener = new PageClickListener();
79 private final Context mContext;
80 private final LayoutInflater mLayoutInflater;
82 private final ContentCallbacks mCallbacks;
83 private final PageContentRepository mPageContentRepository;
84 private final PreviewArea mPreviewArea;
86 // Which document pages to be written.
87 private PageRange[] mRequestedPages;
88 // Pages written in the current file.
89 private PageRange[] mWrittenPages;
90 // Pages the user selected in the UI.
91 private PageRange[] mSelectedPages;
93 private BitmapDrawable mEmptyState;
95 private int mDocumentPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
96 private int mSelectedPageCount;
98 private int mPreviewPageMargin;
99 private int mPreviewPageMinWidth;
100 private int mPreviewListPadding;
101 private int mFooterHeight;
103 private int mColumnCount;
105 private MediaSize mMediaSize;
106 private Margins mMinMargins;
110 private int mPageContentWidth;
111 private int mPageContentHeight;
113 public interface ContentCallbacks {
114 public void onRequestContentUpdate();
115 public void onMalformedPdfFile();
118 public interface PreviewArea {
119 public int getWidth();
120 public int getHeight();
121 public void setColumnCount(int columnCount);
122 public void setPadding(int left, int top, int right, int bottom);
125 public PageAdapter(Context context, ContentCallbacks callbacks, PreviewArea previewArea) {
127 mCallbacks = callbacks;
128 mLayoutInflater = (LayoutInflater) context.getSystemService(
129 Context.LAYOUT_INFLATER_SERVICE);
130 mPageContentRepository = new PageContentRepository(context, this);
132 mPreviewPageMargin = mContext.getResources().getDimensionPixelSize(
133 R.dimen.preview_page_margin);
135 mPreviewPageMinWidth = mContext.getResources().getDimensionPixelSize(
136 R.dimen.preview_page_min_width);
138 mPreviewListPadding = mContext.getResources().getDimensionPixelSize(
139 R.dimen.preview_list_padding);
141 mColumnCount = mContext.getResources().getInteger(
142 R.integer.preview_page_per_row_count);
144 mFooterHeight = mContext.getResources().getDimensionPixelSize(
145 R.dimen.preview_page_footer_height);
147 mPreviewArea = previewArea;
149 mCloseGuard.open("destroy");
151 setHasStableIds(true);
153 mState = STATE_CLOSED;
155 Log.i(LOG_TAG, "STATE_CLOSED");
160 public void onMalformedPdfFile() {
161 mCallbacks.onMalformedPdfFile();
164 public void onOrientationChanged() {
165 mColumnCount = mContext.getResources().getInteger(
166 R.integer.preview_page_per_row_count);
167 notifyDataSetChanged();
170 public boolean isOpened() {
171 return mState == STATE_OPENED;
174 public int getFilePageCount() {
175 return mPageContentRepository.getFilePageCount();
178 public void open(ParcelFileDescriptor source, final Runnable callback) {
180 mState = STATE_OPENED;
182 Log.i(LOG_TAG, "STATE_OPENED");
184 mPageContentRepository.open(source, new Runnable() {
187 notifyDataSetChanged();
193 public void update(PageRange[] writtenPages, PageRange[] selectedPages,
194 int documentPageCount, MediaSize mediaSize, Margins minMargins) {
195 boolean documentChanged = false;
196 boolean updatePreviewAreaAndPageSize = false;
198 // If the app does not tell how many pages are in the document we cannot
199 // optimize and ask for all pages whose count we get from the renderer.
200 if (documentPageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN) {
201 if (writtenPages == null) {
202 // If we already requested all pages, just wait.
203 if (!Arrays.equals(ALL_PAGES_ARRAY, mRequestedPages)) {
204 mRequestedPages = ALL_PAGES_ARRAY;
205 mCallbacks.onRequestContentUpdate();
209 documentPageCount = mPageContentRepository.getFilePageCount();
210 if (documentPageCount <= 0) {
216 if (!Arrays.equals(mSelectedPages, selectedPages)) {
217 mSelectedPages = selectedPages;
218 mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
219 mSelectedPages, documentPageCount);
220 setConfirmedPages(mSelectedPages, documentPageCount);
221 updatePreviewAreaAndPageSize = true;
222 documentChanged = true;
225 if (mDocumentPageCount != documentPageCount) {
226 mDocumentPageCount = documentPageCount;
227 documentChanged = true;
230 if (mMediaSize == null || !mMediaSize.equals(mediaSize)) {
231 mMediaSize = mediaSize;
232 updatePreviewAreaAndPageSize = true;
233 documentChanged = true;
236 if (mMinMargins == null || !mMinMargins.equals(minMargins)) {
237 mMinMargins = minMargins;
238 updatePreviewAreaAndPageSize = true;
239 documentChanged = true;
242 // If *all pages* is selected we need to convert that to absolute
243 // range as we will be checking if some pages are written or not.
244 if (writtenPages != null) {
245 // If we get all pages, this means all pages that we requested.
246 if (PageRangeUtils.isAllPages(writtenPages)) {
247 writtenPages = mRequestedPages;
249 if (!Arrays.equals(mWrittenPages, writtenPages)) {
250 // TODO: Do a surgical invalidation of only written pages changed.
251 mWrittenPages = writtenPages;
252 documentChanged = true;
256 if (updatePreviewAreaAndPageSize) {
257 updatePreviewAreaPageSizeAndEmptyState();
260 if (documentChanged) {
261 notifyDataSetChanged();
265 public void close(Runnable callback) {
267 mState = STATE_CLOSED;
269 Log.i(LOG_TAG, "STATE_CLOSED");
271 mPageContentRepository.close(callback);
275 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
276 View page = mLayoutInflater.inflate(R.layout.preview_page, parent, false);
277 ViewHolder holder = new MyViewHolder(page);
278 holder.setIsRecyclable(true);
283 public void onBindViewHolder(ViewHolder holder, int position) {
285 Log.i(LOG_TAG, "Binding holder: " + holder + " with id: " + getItemId(position)
286 + " for position: " + position);
289 MyViewHolder myHolder = (MyViewHolder) holder;
291 PreviewPageFrame page = (PreviewPageFrame) holder.itemView;
292 page.setOnClickListener(mPageClickListener);
296 myHolder.mPageInAdapter = position;
298 final int pageInDocument = computePageIndexInDocument(position);
299 final int pageIndexInFile = computePageIndexInFile(pageInDocument);
301 PageContentView content = (PageContentView) page.findViewById(R.id.page_content);
303 LayoutParams params = content.getLayoutParams();
304 params.width = mPageContentWidth;
305 params.height = mPageContentHeight;
307 PageContentProvider provider = content.getPageContentProvider();
309 if (pageIndexInFile != INVALID_PAGE_INDEX) {
311 Log.i(LOG_TAG, "Binding provider:"
312 + " pageIndexInAdapter: " + position
313 + ", pageInDocument: " + pageInDocument
314 + ", pageIndexInFile: " + pageIndexInFile);
317 // OK, there are bugs in recycler view which tries to bind views
318 // without recycling them which would give us a chance to clean up.
319 PageContentProvider boundProvider = mPageContentRepository
320 .peekPageContentProvider(pageIndexInFile);
321 if (boundProvider != null) {
322 PageContentView owner = (PageContentView) boundProvider.getOwner();
323 owner.init(null, mEmptyState, mMediaSize, mMinMargins);
324 mPageContentRepository.releasePageContentProvider(boundProvider);
327 provider = mPageContentRepository.acquirePageContentProvider(
328 pageIndexInFile, content);
329 mBoundPagesInAdapter.put(position, null);
331 onSelectedPageNotInFile(pageInDocument);
333 content.init(provider, mEmptyState, mMediaSize, mMinMargins);
335 if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
336 page.setSelected(true, false);
338 page.setSelected(false, false);
341 page.setContentDescription(mContext.getString(R.string.page_description_template,
342 pageInDocument + 1, mDocumentPageCount));
344 TextView pageNumberView = (TextView) page.findViewById(R.id.page_number);
345 String text = mContext.getString(R.string.current_page_template,
346 pageInDocument + 1, mDocumentPageCount);
347 pageNumberView.setText(text);
351 public int getItemCount() {
352 return mSelectedPageCount;
356 public long getItemId(int position) {
357 return computePageIndexInDocument(position);
361 public void onViewRecycled(ViewHolder holder) {
362 MyViewHolder myHolder = (MyViewHolder) holder;
363 PageContentView content = (PageContentView) holder.itemView
364 .findViewById(R.id.page_content);
365 recyclePageView(content, myHolder.mPageInAdapter);
366 myHolder.mPageInAdapter = INVALID_PAGE_INDEX;
369 public PageRange[] getRequestedPages() {
370 return mRequestedPages;
373 public PageRange[] getSelectedPages() {
374 PageRange[] selectedPages = computeSelectedPages();
375 if (!Arrays.equals(mSelectedPages, selectedPages)) {
376 mSelectedPages = selectedPages;
377 mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
378 mSelectedPages, mDocumentPageCount);
379 updatePreviewAreaPageSizeAndEmptyState();
380 notifyDataSetChanged();
382 return mSelectedPages;
385 public void onPreviewAreaSizeChanged() {
386 if (mMediaSize != null) {
387 updatePreviewAreaPageSizeAndEmptyState();
388 notifyDataSetChanged();
392 private void updatePreviewAreaPageSizeAndEmptyState() {
393 if (mMediaSize == null) {
397 final int availableWidth = mPreviewArea.getWidth();
398 final int availableHeight = mPreviewArea.getHeight();
400 // Page aspect ratio to keep.
401 final float pageAspectRatio = (float) mMediaSize.getWidthMils()
402 / mMediaSize.getHeightMils();
404 // Make sure we have no empty columns.
405 final int columnCount = Math.min(mSelectedPageCount, mColumnCount);
406 mPreviewArea.setColumnCount(columnCount);
408 // Compute max page width.
409 final int horizontalMargins = 2 * columnCount * mPreviewPageMargin;
410 final int horizontalPaddingAndMargins = horizontalMargins + 2 * mPreviewListPadding;
411 final int pageContentDesiredWidth = (int) ((((float) availableWidth
412 - horizontalPaddingAndMargins) / columnCount) + 0.5f);
414 // Compute max page height.
415 final int pageContentDesiredHeight = (int) (((float) pageContentDesiredWidth
416 / pageAspectRatio) + 0.5f);
418 // If the page does not fit entirely in a vertical direction,
419 // we shirk it but not less than the minimal page width.
420 final int pageContentMinHeight = (int) (mPreviewPageMinWidth / pageAspectRatio + 0.5f);
421 final int pageContentMaxHeight = Math.max(pageContentMinHeight,
422 availableHeight - 2 * (mPreviewListPadding + mPreviewPageMargin) - mFooterHeight);
424 mPageContentHeight = Math.min(pageContentDesiredHeight, pageContentMaxHeight);
425 mPageContentWidth = (int) ((mPageContentHeight * pageAspectRatio) + 0.5f);
427 final int totalContentWidth = columnCount * mPageContentWidth + horizontalMargins;
428 final int horizontalPadding = (availableWidth - totalContentWidth) / 2;
430 final int rowCount = mSelectedPageCount / columnCount
431 + ((mSelectedPageCount % columnCount) > 0 ? 1 : 0);
432 final int totalContentHeight = rowCount * (mPageContentHeight + mFooterHeight + 2
433 * mPreviewPageMargin);
435 final int verticalPadding;
436 if (mPageContentHeight + mFooterHeight + mPreviewListPadding
437 + 2 * mPreviewPageMargin > availableHeight) {
438 verticalPadding = Math.max(0,
439 (availableHeight - mPageContentHeight - mFooterHeight) / 2
440 - mPreviewPageMargin);
442 verticalPadding = Math.max(mPreviewListPadding,
443 (availableHeight - totalContentHeight) / 2);
446 mPreviewArea.setPadding(horizontalPadding, verticalPadding,
447 horizontalPadding, verticalPadding);
449 // Now update the empty state drawable, as it depends on the page
450 // size and is reused for all views for better performance.
451 LayoutInflater inflater = LayoutInflater.from(mContext);
452 View content = inflater.inflate(R.layout.preview_page_loading, null, false);
453 content.measure(MeasureSpec.makeMeasureSpec(mPageContentWidth, MeasureSpec.EXACTLY),
454 MeasureSpec.makeMeasureSpec(mPageContentHeight, MeasureSpec.EXACTLY));
455 content.layout(0, 0, content.getMeasuredWidth(), content.getMeasuredHeight());
457 Bitmap bitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
458 Bitmap.Config.ARGB_8888);
459 Canvas canvas = new Canvas(bitmap);
460 content.draw(canvas);
462 // Do not recycle the old bitmap if such as it may be set as an empty
463 // state to any of the page views. Just let the GC take care of it.
464 mEmptyState = new BitmapDrawable(mContext.getResources(), bitmap);
467 private PageRange[] computeSelectedPages() {
468 ArrayList<PageRange> selectedPagesList = new ArrayList<>();
470 int startPageIndex = INVALID_PAGE_INDEX;
471 int endPageIndex = INVALID_PAGE_INDEX;
473 final int pageCount = mConfirmedPagesInDocument.size();
474 for (int i = 0; i < pageCount; i++) {
475 final int pageIndex = mConfirmedPagesInDocument.keyAt(i);
476 if (startPageIndex == INVALID_PAGE_INDEX) {
477 startPageIndex = endPageIndex = pageIndex;
479 if (endPageIndex + 1 < pageIndex) {
480 PageRange pageRange = new PageRange(startPageIndex, endPageIndex);
481 selectedPagesList.add(pageRange);
482 startPageIndex = pageIndex;
484 endPageIndex = pageIndex;
487 if (startPageIndex != INVALID_PAGE_INDEX
488 && endPageIndex != INVALID_PAGE_INDEX) {
489 PageRange pageRange = new PageRange(startPageIndex, endPageIndex);
490 selectedPagesList.add(pageRange);
493 PageRange[] selectedPages = new PageRange[selectedPagesList.size()];
494 selectedPagesList.toArray(selectedPages);
496 return selectedPages;
499 public void destroy() {
505 protected void finalize() throws Throwable {
507 if (mState != STATE_DESTROYED) {
508 mCloseGuard.warnIfOpen();
516 private int computePageIndexInDocument(int indexInAdapter) {
517 int skippedAdapterPages = 0;
518 final int selectedPagesCount = mSelectedPages.length;
519 for (int i = 0; i < selectedPagesCount; i++) {
520 PageRange pageRange = PageRangeUtils.asAbsoluteRange(
521 mSelectedPages[i], mDocumentPageCount);
522 skippedAdapterPages += pageRange.getSize();
523 if (skippedAdapterPages > indexInAdapter) {
524 final int overshoot = skippedAdapterPages - indexInAdapter - 1;
525 return pageRange.getEnd() - overshoot;
528 return INVALID_PAGE_INDEX;
531 private int computePageIndexInFile(int pageIndexInDocument) {
532 if (!PageRangeUtils.contains(mSelectedPages, pageIndexInDocument)) {
533 return INVALID_PAGE_INDEX;
535 if (mWrittenPages == null) {
536 return INVALID_PAGE_INDEX;
539 int indexInFile = INVALID_PAGE_INDEX;
540 final int rangeCount = mWrittenPages.length;
541 for (int i = 0; i < rangeCount; i++) {
542 PageRange pageRange = mWrittenPages[i];
543 if (!pageRange.contains(pageIndexInDocument)) {
544 indexInFile += pageRange.getSize();
546 indexInFile += pageIndexInDocument - pageRange.getStart() + 1;
550 return INVALID_PAGE_INDEX;
553 private void setConfirmedPages(PageRange[] pagesInDocument, int documentPageCount) {
554 mConfirmedPagesInDocument.clear();
555 final int rangeCount = pagesInDocument.length;
556 for (int i = 0; i < rangeCount; i++) {
557 PageRange pageRange = PageRangeUtils.asAbsoluteRange(pagesInDocument[i],
559 for (int j = pageRange.getStart(); j <= pageRange.getEnd(); j++) {
560 mConfirmedPagesInDocument.put(j, null);
565 private void onSelectedPageNotInFile(int pageInDocument) {
566 PageRange[] requestedPages = computeRequestedPages(pageInDocument);
567 if (!Arrays.equals(mRequestedPages, requestedPages)) {
568 mRequestedPages = requestedPages;
570 Log.i(LOG_TAG, "Requesting pages: " + Arrays.toString(mRequestedPages));
572 mCallbacks.onRequestContentUpdate();
576 private PageRange[] computeRequestedPages(int pageInDocument) {
577 if (mRequestedPages != null &&
578 PageRangeUtils.contains(mRequestedPages, pageInDocument)) {
579 return mRequestedPages;
582 List<PageRange> pageRangesList = new ArrayList<>();
584 int remainingPagesToRequest = MAX_PREVIEW_PAGES_BATCH;
585 final int selectedPagesCount = mSelectedPages.length;
587 // We always request the pages that are bound, i.e. shown on screen.
588 PageRange[] boundPagesInDocument = computeBoundPagesInDocument();
590 final int boundRangeCount = boundPagesInDocument.length;
591 for (int i = 0; i < boundRangeCount; i++) {
592 PageRange boundRange = boundPagesInDocument[i];
593 pageRangesList.add(boundRange);
595 remainingPagesToRequest -= PageRangeUtils.getNormalizedPageCount(
596 boundPagesInDocument, mDocumentPageCount);
598 final boolean requestFromStart = mRequestedPages == null
599 || pageInDocument > mRequestedPages[mRequestedPages.length - 1].getEnd();
601 if (!requestFromStart) {
603 Log.i(LOG_TAG, "Requesting from end");
606 // Reminder that ranges are always normalized.
607 for (int i = selectedPagesCount - 1; i >= 0; i--) {
608 if (remainingPagesToRequest <= 0) {
612 PageRange selectedRange = PageRangeUtils.asAbsoluteRange(mSelectedPages[i],
614 if (pageInDocument < selectedRange.getStart()) {
618 PageRange pagesInRange;
621 if (selectedRange.contains(pageInDocument)) {
622 rangeSpan = pageInDocument - selectedRange.getStart() + 1;
623 rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
624 final int fromPage = Math.max(pageInDocument - rangeSpan - 1, 0);
625 rangeSpan = Math.max(rangeSpan, 0);
626 pagesInRange = new PageRange(fromPage, pageInDocument);
628 rangeSpan = selectedRange.getSize();
629 rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
630 rangeSpan = Math.max(rangeSpan, 0);
631 final int fromPage = Math.max(selectedRange.getEnd() - rangeSpan - 1, 0);
632 final int toPage = selectedRange.getEnd();
633 pagesInRange = new PageRange(fromPage, toPage);
636 pageRangesList.add(pagesInRange);
637 remainingPagesToRequest -= rangeSpan;
641 Log.i(LOG_TAG, "Requesting from start");
644 // Reminder that ranges are always normalized.
645 for (int i = 0; i < selectedPagesCount; i++) {
646 if (remainingPagesToRequest <= 0) {
650 PageRange selectedRange = PageRangeUtils.asAbsoluteRange(mSelectedPages[i],
652 if (pageInDocument > selectedRange.getEnd()) {
656 PageRange pagesInRange;
659 if (selectedRange.contains(pageInDocument)) {
660 rangeSpan = selectedRange.getEnd() - pageInDocument + 1;
661 rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
662 final int toPage = Math.min(pageInDocument + rangeSpan - 1,
663 mDocumentPageCount - 1);
664 pagesInRange = new PageRange(pageInDocument, toPage);
666 rangeSpan = selectedRange.getSize();
667 rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
668 final int fromPage = selectedRange.getStart();
669 final int toPage = Math.min(selectedRange.getStart() + rangeSpan - 1,
670 mDocumentPageCount - 1);
671 pagesInRange = new PageRange(fromPage, toPage);
675 Log.i(LOG_TAG, "computeRequestedPages() Adding range:" + pagesInRange);
677 pageRangesList.add(pagesInRange);
678 remainingPagesToRequest -= rangeSpan;
682 PageRange[] pageRanges = new PageRange[pageRangesList.size()];
683 pageRangesList.toArray(pageRanges);
685 return PageRangeUtils.normalize(pageRanges);
688 private PageRange[] computeBoundPagesInDocument() {
689 List<PageRange> pagesInDocumentList = new ArrayList<>();
691 int fromPage = INVALID_PAGE_INDEX;
692 int toPage = INVALID_PAGE_INDEX;
694 final int boundPageCount = mBoundPagesInAdapter.size();
695 for (int i = 0; i < boundPageCount; i++) {
696 // The container is a sparse array, so keys are sorted in ascending order.
697 final int boundPageInAdapter = mBoundPagesInAdapter.keyAt(i);
698 final int boundPageInDocument = computePageIndexInDocument(boundPageInAdapter);
700 if (fromPage == INVALID_PAGE_INDEX) {
701 fromPage = boundPageInDocument;
704 if (toPage == INVALID_PAGE_INDEX) {
705 toPage = boundPageInDocument;
708 if (boundPageInDocument > toPage + 1) {
709 PageRange pageRange = new PageRange(fromPage, toPage);
710 pagesInDocumentList.add(pageRange);
711 fromPage = toPage = boundPageInDocument;
713 toPage = boundPageInDocument;
717 if (fromPage != INVALID_PAGE_INDEX && toPage != INVALID_PAGE_INDEX) {
718 PageRange pageRange = new PageRange(fromPage, toPage);
719 pagesInDocumentList.add(pageRange);
722 PageRange[] pageInDocument = new PageRange[pagesInDocumentList.size()];
723 pagesInDocumentList.toArray(pageInDocument);
726 Log.i(LOG_TAG, "Bound pages: " + Arrays.toString(pageInDocument));
729 return pageInDocument;
732 private void recyclePageView(PageContentView page, int pageIndexInAdapter) {
733 PageContentProvider provider = page.getPageContentProvider();
734 if (provider != null) {
735 page.init(null, null, null, null);
736 mPageContentRepository.releasePageContentProvider(provider);
738 mBoundPagesInAdapter.remove(pageIndexInAdapter);
742 public void startPreloadContent(PageRange pageRangeInAdapter) {
743 final int startPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getStart());
744 final int startPageInFile = computePageIndexInFile(startPageInDocument);
745 final int endPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getEnd());
746 final int endPageInFile = computePageIndexInFile(endPageInDocument);
747 if (startPageInDocument != INVALID_PAGE_INDEX && endPageInDocument != INVALID_PAGE_INDEX) {
748 mPageContentRepository.startPreload(startPageInFile, endPageInFile);
752 public void stopPreloadContent() {
753 mPageContentRepository.stopPreload();
756 private void doDestroy() {
757 mPageContentRepository.destroy();
759 mState = STATE_DESTROYED;
761 Log.i(LOG_TAG, "STATE_DESTROYED");
765 private void throwIfNotOpened() {
766 if (mState != STATE_OPENED) {
767 throw new IllegalStateException("Not opened");
771 private void throwIfNotClosed() {
772 if (mState != STATE_CLOSED) {
773 throw new IllegalStateException("Not closed");
777 private final class MyViewHolder extends ViewHolder {
780 private MyViewHolder(View itemView) {
785 private final class PageClickListener implements OnClickListener {
787 public void onClick(View view) {
788 PreviewPageFrame page = (PreviewPageFrame) view;
789 MyViewHolder holder = (MyViewHolder) page.getTag();
790 final int pageInAdapter = holder.mPageInAdapter;
791 final int pageInDocument = computePageIndexInDocument(pageInAdapter);
792 if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) < 0) {
793 mConfirmedPagesInDocument.put(pageInDocument, null);
794 page.setSelected(true, true);
796 if (mConfirmedPagesInDocument.size() <= 1) {
799 mConfirmedPagesInDocument.remove(pageInDocument);
800 page.setSelected(false, true);