1525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov/*
2525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Copyright (C) 2014 The Android Open Source Project
3525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
4525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Licensed under the Apache License, Version 2.0 (the "License");
5525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * you may not use this file except in compliance with the License.
6525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * You may obtain a copy of the License at
7525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
8525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *      http://www.apache.org/licenses/LICENSE-2.0
9525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov *
10525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * Unless required by applicable law or agreed to in writing, software
11525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * distributed under the License is distributed on an "AS IS" BASIS,
12525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * See the License for the specific language governing permissions and
14525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * limitations under the License.
15525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov */
16525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
17525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovpackage com.android.printspooler.ui;
18525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
19525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.content.Context;
206552bf3da60159607d9266eb295ee3c448f6c3deSvetoslavimport android.graphics.Bitmap;
216552bf3da60159607d9266eb295ee3c448f6c3deSvetoslavimport android.graphics.Canvas;
226552bf3da60159607d9266eb295ee3c448f6c3deSvetoslavimport android.graphics.drawable.BitmapDrawable;
23525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.os.ParcelFileDescriptor;
24525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PageRange;
25525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintAttributes.MediaSize;
26525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintAttributes.Margins;
27525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintDocumentInfo;
28525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.support.v7.widget.RecyclerView.Adapter;
29525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.support.v7.widget.RecyclerView.ViewHolder;
30525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.util.Log;
31525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.util.SparseArray;
32525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.view.LayoutInflater;
33525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.view.View;
34525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.view.View.OnClickListener;
35525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.view.ViewGroup;
36525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.view.ViewGroup.LayoutParams;
376552bf3da60159607d9266eb295ee3c448f6c3deSvetoslavimport android.view.View.MeasureSpec;
38525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.widget.TextView;
39525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.R;
40fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganovimport com.android.printspooler.model.OpenDocumentCallback;
41525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.model.PageContentRepository;
42525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.model.PageContentRepository.PageContentProvider;
43525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.util.PageRangeUtils;
44525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.widget.PageContentView;
45e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslavimport com.android.printspooler.widget.PreviewPageFrame;
46525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport dalvik.system.CloseGuard;
47525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
48525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.util.ArrayList;
49525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.util.Arrays;
50525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.util.List;
51525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
52525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov/**
53525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov * This class represents the adapter for the pages in the print preview list.
54525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov */
55fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganovpublic final class PageAdapter extends Adapter {
56525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final String LOG_TAG = "PageAdapter";
57525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
58525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final int MAX_PREVIEW_PAGES_BATCH = 50;
59525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
60f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav    private static final boolean DEBUG = false;
61525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
62525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final PageRange[] ALL_PAGES_ARRAY = new PageRange[] {
63525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange.ALL_PAGES
64525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    };
65525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
66525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final int INVALID_PAGE_INDEX = -1;
67525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
68525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final int STATE_CLOSED = 0;
69525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final int STATE_OPENED = 1;
70525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private static final int STATE_DESTROYED = 2;
71525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
72525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final CloseGuard mCloseGuard = CloseGuard.get();
73525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
74525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final SparseArray<Void> mBoundPagesInAdapter = new SparseArray<>();
75525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final SparseArray<Void> mConfirmedPagesInDocument = new SparseArray<>();
76525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
77525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final PageClickListener mPageClickListener = new PageClickListener();
78525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
79525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final Context mContext;
8015cbc8a03250eafdf947cd8ad4e77f34444d5ba4Svetoslav    private final LayoutInflater mLayoutInflater;
81525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
825ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav    private final ContentCallbacks mCallbacks;
83525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final PageContentRepository mPageContentRepository;
84525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final PreviewArea mPreviewArea;
85525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
86525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    // Which document pages to be written.
87525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private PageRange[] mRequestedPages;
88525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    // Pages written in the current file.
89525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private PageRange[] mWrittenPages;
90525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    // Pages the user selected in the UI.
91525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private PageRange[] mSelectedPages;
92525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
936552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav    private BitmapDrawable mEmptyState;
946552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav
9515cbc8a03250eafdf947cd8ad4e77f34444d5ba4Svetoslav    private int mDocumentPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
9615cbc8a03250eafdf947cd8ad4e77f34444d5ba4Svetoslav    private int mSelectedPageCount;
97525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
98525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mPreviewPageMargin;
9938781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov    private int mPreviewPageMinWidth;
100525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mPreviewListPadding;
101525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mFooterHeight;
102525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
103525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mColumnCount;
104525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
105525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private MediaSize mMediaSize;
106525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private Margins mMinMargins;
107525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
108525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mState;
109525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
110525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mPageContentWidth;
111525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mPageContentHeight;
112525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
1135ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav    public interface ContentCallbacks {
1145ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav        public void onRequestContentUpdate();
1155ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav        public void onMalformedPdfFile();
116fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov        public void onSecurePdfFile();
117525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
118525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
119525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public interface PreviewArea {
120525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public int getWidth();
121525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public int getHeight();
122525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void setColumnCount(int columnCount);
123525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void setPadding(int left, int top, int right, int bottom);
124525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
125525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
1265ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav    public PageAdapter(Context context, ContentCallbacks callbacks, PreviewArea previewArea) {
127525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mContext = context;
1285ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav        mCallbacks = callbacks;
129525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mLayoutInflater = (LayoutInflater) context.getSystemService(
130525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Context.LAYOUT_INFLATER_SERVICE);
131fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov        mPageContentRepository = new PageContentRepository(context);
132525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
133525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPreviewPageMargin = mContext.getResources().getDimensionPixelSize(
134525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.dimen.preview_page_margin);
135525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
13638781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        mPreviewPageMinWidth = mContext.getResources().getDimensionPixelSize(
13738781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov                R.dimen.preview_page_min_width);
13838781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov
139525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPreviewListPadding = mContext.getResources().getDimensionPixelSize(
140525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.dimen.preview_list_padding);
141525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
142525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mColumnCount = mContext.getResources().getInteger(
143525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.integer.preview_page_per_row_count);
144525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
14538781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        mFooterHeight = mContext.getResources().getDimensionPixelSize(
14638781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov                R.dimen.preview_page_footer_height);
147525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
148525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPreviewArea = previewArea;
149525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
150525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mCloseGuard.open("destroy");
151525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
152525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        setHasStableIds(true);
153525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
154525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mState = STATE_CLOSED;
155525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (DEBUG) {
156525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Log.i(LOG_TAG, "STATE_CLOSED");
157525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
158525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
159525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
160525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onOrientationChanged() {
161525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mColumnCount = mContext.getResources().getInteger(
162525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.integer.preview_page_per_row_count);
163139ba7fb0ed976bdbc7c283e3f7fe9980eed22a6Svetoslav        notifyDataSetChanged();
164525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
165525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
166525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public boolean isOpened() {
167525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mState == STATE_OPENED;
168525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
169525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
170525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public int getFilePageCount() {
171525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mPageContentRepository.getFilePageCount();
172525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
173525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
174f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav    public void open(ParcelFileDescriptor source, final Runnable callback) {
175525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        throwIfNotClosed();
176525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mState = STATE_OPENED;
177525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (DEBUG) {
178525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Log.i(LOG_TAG, "STATE_OPENED");
179525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
180fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov        mPageContentRepository.open(source, new OpenDocumentCallback() {
181f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav            @Override
182fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            public void onSuccess() {
183f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav                notifyDataSetChanged();
184f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav                callback.run();
185f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav            }
186fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov
187fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            @Override
188fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            public void onFailure(int error) {
189fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                switch (error) {
190fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                    case OpenDocumentCallback.ERROR_MALFORMED_PDF_FILE: {
191fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                        mCallbacks.onMalformedPdfFile();
192fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                    } break;
193fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov
194fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                    case OpenDocumentCallback.ERROR_SECURE_PDF_FILE: {
195fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                        mCallbacks.onSecurePdfFile();
196fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                    } break;
197fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov                }
198fce84f035c35606c5707e735f503f7bdcfd5b2a1Svet Ganov            }
199f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav        });
200525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
201525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
202525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void update(PageRange[] writtenPages, PageRange[] selectedPages,
203525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            int documentPageCount, MediaSize mediaSize, Margins minMargins) {
204525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        boolean documentChanged = false;
205525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        boolean updatePreviewAreaAndPageSize = false;
206525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
207525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // If the app does not tell how many pages are in the document we cannot
208525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // optimize and ask for all pages whose count we get from the renderer.
209525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (documentPageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN) {
210525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (writtenPages == null) {
211525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                // If we already requested all pages, just wait.
212525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (!Arrays.equals(ALL_PAGES_ARRAY, mRequestedPages)) {
213525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mRequestedPages = ALL_PAGES_ARRAY;
2145ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav                    mCallbacks.onRequestContentUpdate();
215525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
216525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                return;
217525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            } else {
218525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                documentPageCount = mPageContentRepository.getFilePageCount();
219525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (documentPageCount <= 0) {
220525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    return;
221525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
222525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
223525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
224525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
225525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (!Arrays.equals(mSelectedPages, selectedPages)) {
226525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mSelectedPages = selectedPages;
227525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
228525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mSelectedPages, documentPageCount);
229525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            setConfirmedPages(mSelectedPages, documentPageCount);
230525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            updatePreviewAreaAndPageSize = true;
231525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            documentChanged = true;
232525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
233525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
234525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mDocumentPageCount != documentPageCount) {
235525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mDocumentPageCount = documentPageCount;
236525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            documentChanged = true;
237525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
238525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
239525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mMediaSize == null || !mMediaSize.equals(mediaSize)) {
240525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mMediaSize = mediaSize;
241525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            updatePreviewAreaAndPageSize = true;
242525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            documentChanged = true;
243525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
244525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
245525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mMinMargins == null || !mMinMargins.equals(minMargins)) {
246525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mMinMargins = minMargins;
247525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            updatePreviewAreaAndPageSize = true;
248525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            documentChanged = true;
249525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
250525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
251525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // If *all pages* is selected we need to convert that to absolute
252525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // range as we will be checking if some pages are written or not.
253525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (writtenPages != null) {
254525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // If we get all pages, this means all pages that we requested.
255525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (PageRangeUtils.isAllPages(writtenPages)) {
256525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                writtenPages = mRequestedPages;
257525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
258525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (!Arrays.equals(mWrittenPages, writtenPages)) {
259525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                // TODO: Do a surgical invalidation of only written pages changed.
260525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mWrittenPages = writtenPages;
261525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                documentChanged = true;
262525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
263525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
264525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
265525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (updatePreviewAreaAndPageSize) {
2666552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav            updatePreviewAreaPageSizeAndEmptyState();
267525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
268525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
269525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (documentChanged) {
270525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            notifyDataSetChanged();
271525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
272525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
273525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
274525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void close(Runnable callback) {
275525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        throwIfNotOpened();
276525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mState = STATE_CLOSED;
277525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (DEBUG) {
278525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Log.i(LOG_TAG, "STATE_CLOSED");
279525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
280525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPageContentRepository.close(callback);
281525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
282525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
283525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
284525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
285525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        View page = mLayoutInflater.inflate(R.layout.preview_page, parent, false);
2860d2d9637ed4f46062d508cfa7da0e9776fc444f8Svetoslav        return new MyViewHolder(page);
287525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
288525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
289525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
290525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onBindViewHolder(ViewHolder holder, int position) {
291525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (DEBUG) {
292525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Log.i(LOG_TAG, "Binding holder: " + holder + " with id: " + getItemId(position)
293525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    + " for position: " + position);
294525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
295525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
296525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        MyViewHolder myHolder = (MyViewHolder) holder;
297525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
298e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav        PreviewPageFrame page = (PreviewPageFrame) holder.itemView;
299c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav        page.setOnClickListener(mPageClickListener);
300c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav
301525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        page.setTag(holder);
302525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
303525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        myHolder.mPageInAdapter = position;
304525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
305525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int pageInDocument = computePageIndexInDocument(position);
306525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int pageIndexInFile = computePageIndexInFile(pageInDocument);
307525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
308525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageContentView content = (PageContentView) page.findViewById(R.id.page_content);
309525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
310525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        LayoutParams params = content.getLayoutParams();
311525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        params.width = mPageContentWidth;
312525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        params.height = mPageContentHeight;
313525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
314525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageContentProvider provider = content.getPageContentProvider();
315525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
316525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (pageIndexInFile != INVALID_PAGE_INDEX) {
317525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (DEBUG) {
318525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Log.i(LOG_TAG, "Binding provider:"
319525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        + " pageIndexInAdapter: " + position
320525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        + ", pageInDocument: " + pageInDocument
321525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        + ", pageIndexInFile: " + pageIndexInFile);
322525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
323525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
324525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            provider = mPageContentRepository.acquirePageContentProvider(
325525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    pageIndexInFile, content);
326525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mBoundPagesInAdapter.put(position, null);
327525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        } else {
328525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            onSelectedPageNotInFile(pageInDocument);
329525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
3306552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        content.init(provider, mEmptyState, mMediaSize, mMinMargins);
331525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
332525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) >= 0) {
333e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav            page.setSelected(true, false);
334525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        } else {
335e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav            page.setSelected(false, false);
336525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
337525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
338e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav        page.setContentDescription(mContext.getString(R.string.page_description_template,
339e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav                pageInDocument + 1, mDocumentPageCount));
340e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav
341525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        TextView pageNumberView = (TextView) page.findViewById(R.id.page_number);
342525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        String text = mContext.getString(R.string.current_page_template,
343525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                pageInDocument + 1, mDocumentPageCount);
344525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        pageNumberView.setText(text);
345525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
346525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
347525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
348525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public int getItemCount() {
349525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mSelectedPageCount;
350525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
351525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
352525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
353525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public long getItemId(int position) {
354525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return computePageIndexInDocument(position);
355525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
356525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
357525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
358525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onViewRecycled(ViewHolder holder) {
359525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        MyViewHolder myHolder = (MyViewHolder) holder;
360525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageContentView content = (PageContentView) holder.itemView
361525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                .findViewById(R.id.page_content);
362525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        recyclePageView(content, myHolder.mPageInAdapter);
363525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        myHolder.mPageInAdapter = INVALID_PAGE_INDEX;
364525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
365525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
366525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public PageRange[] getRequestedPages() {
367525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mRequestedPages;
368525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
369525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
370525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public PageRange[] getSelectedPages() {
371525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageRange[] selectedPages = computeSelectedPages();
372525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (!Arrays.equals(mSelectedPages, selectedPages)) {
373525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mSelectedPages = selectedPages;
374525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mSelectedPageCount = PageRangeUtils.getNormalizedPageCount(
375525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mSelectedPages, mDocumentPageCount);
3766552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav            updatePreviewAreaPageSizeAndEmptyState();
377525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            notifyDataSetChanged();
378525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
379525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mSelectedPages;
380525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
381525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
382525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onPreviewAreaSizeChanged() {
383525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mMediaSize != null) {
3846552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav            updatePreviewAreaPageSizeAndEmptyState();
385525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            notifyDataSetChanged();
386525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
387525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
388525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
3896552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav    private void updatePreviewAreaPageSizeAndEmptyState() {
390e771caf332bb3f2b668aaf249b30f37f13755553Svet Ganov        if (mMediaSize == null) {
391e771caf332bb3f2b668aaf249b30f37f13755553Svet Ganov            return;
392e771caf332bb3f2b668aaf249b30f37f13755553Svet Ganov        }
393e771caf332bb3f2b668aaf249b30f37f13755553Svet Ganov
394525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int availableWidth = mPreviewArea.getWidth();
395525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int availableHeight = mPreviewArea.getHeight();
396525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
397525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // Page aspect ratio to keep.
398525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final float pageAspectRatio = (float) mMediaSize.getWidthMils()
399525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                / mMediaSize.getHeightMils();
400525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
401525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // Make sure we have no empty columns.
402525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int columnCount = Math.min(mSelectedPageCount, mColumnCount);
403525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPreviewArea.setColumnCount(columnCount);
404525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
405525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // Compute max page width.
406525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int horizontalMargins = 2 * columnCount * mPreviewPageMargin;
407525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int horizontalPaddingAndMargins = horizontalMargins + 2 * mPreviewListPadding;
408525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int pageContentDesiredWidth = (int) ((((float) availableWidth
409525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                - horizontalPaddingAndMargins) / columnCount) + 0.5f);
410525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
411525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // Compute max page height.
412525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int pageContentDesiredHeight = (int) (((float) pageContentDesiredWidth
413525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                / pageAspectRatio) + 0.5f);
41438781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov
4156552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        // If the page does not fit entirely in a vertical direction,
41638781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        // we shirk it but not less than the minimal page width.
41738781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        final int pageContentMinHeight = (int) (mPreviewPageMinWidth / pageAspectRatio + 0.5f);
41838781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        final int pageContentMaxHeight = Math.max(pageContentMinHeight,
41938781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov                availableHeight - 2 * (mPreviewListPadding + mPreviewPageMargin) - mFooterHeight);
420525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
421525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPageContentHeight = Math.min(pageContentDesiredHeight, pageContentMaxHeight);
422525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPageContentWidth = (int) ((mPageContentHeight * pageAspectRatio) + 0.5f);
423525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
424525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int totalContentWidth = columnCount * mPageContentWidth + horizontalMargins;
425525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int horizontalPadding = (availableWidth - totalContentWidth) / 2;
426525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
427525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int rowCount = mSelectedPageCount / columnCount
428525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                + ((mSelectedPageCount % columnCount) > 0 ? 1 : 0);
42938781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        final int totalContentHeight = rowCount * (mPageContentHeight + mFooterHeight + 2
430525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                * mPreviewPageMargin);
43138781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov
43238781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        final int verticalPadding;
433dfa3e7c6fd4e8a28ba51b54c769b891191459012Svetoslav        if (mPageContentHeight + mFooterHeight + mPreviewListPadding
434dfa3e7c6fd4e8a28ba51b54c769b891191459012Svetoslav                + 2 * mPreviewPageMargin > availableHeight) {
435c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav            verticalPadding = Math.max(0,
436c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav                    (availableHeight - mPageContentHeight - mFooterHeight) / 2
437c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav                            - mPreviewPageMargin);
43838781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        } else {
43938781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov            verticalPadding = Math.max(mPreviewListPadding,
44038781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov                    (availableHeight - totalContentHeight) / 2);
44138781bd2c2c61385651314098b4a615cb8e8efa1Svet Ganov        }
442525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
443525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPreviewArea.setPadding(horizontalPadding, verticalPadding,
444525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                horizontalPadding, verticalPadding);
4456552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav
4466552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        // Now update the empty state drawable, as it depends on the page
4476552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        // size and is reused for all views for better performance.
4486552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        LayoutInflater inflater = LayoutInflater.from(mContext);
4496552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        View content = inflater.inflate(R.layout.preview_page_loading, null, false);
4506552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        content.measure(MeasureSpec.makeMeasureSpec(mPageContentWidth, MeasureSpec.EXACTLY),
4516552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav                MeasureSpec.makeMeasureSpec(mPageContentHeight, MeasureSpec.EXACTLY));
4526552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        content.layout(0, 0, content.getMeasuredWidth(), content.getMeasuredHeight());
4536552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav
4546552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        Bitmap bitmap = Bitmap.createBitmap(mPageContentWidth, mPageContentHeight,
4556552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav                Bitmap.Config.ARGB_8888);
4566552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        Canvas canvas = new Canvas(bitmap);
4576552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        content.draw(canvas);
4586552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav
4596552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        // Do not recycle the old bitmap if such as it may be set as an empty
4606552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        // state to any of the page views. Just let the GC take care of it.
4616552bf3da60159607d9266eb295ee3c448f6c3deSvetoslav        mEmptyState = new BitmapDrawable(mContext.getResources(), bitmap);
462525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
463525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
464525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private PageRange[] computeSelectedPages() {
465525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        ArrayList<PageRange> selectedPagesList = new ArrayList<>();
466525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
467525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int startPageIndex = INVALID_PAGE_INDEX;
468525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int endPageIndex = INVALID_PAGE_INDEX;
469525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
470525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int pageCount = mConfirmedPagesInDocument.size();
471525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        for (int i = 0; i < pageCount; i++) {
472525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            final int pageIndex = mConfirmedPagesInDocument.keyAt(i);
473525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (startPageIndex == INVALID_PAGE_INDEX) {
474525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                startPageIndex = endPageIndex = pageIndex;
475525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
476525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (endPageIndex + 1 < pageIndex) {
477525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange pageRange = new PageRange(startPageIndex, endPageIndex);
478525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                selectedPagesList.add(pageRange);
479525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                startPageIndex = pageIndex;
480525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
481525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            endPageIndex = pageIndex;
482525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
483525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
484525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (startPageIndex != INVALID_PAGE_INDEX
485525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                && endPageIndex != INVALID_PAGE_INDEX) {
486525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange pageRange = new PageRange(startPageIndex, endPageIndex);
487525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            selectedPagesList.add(pageRange);
488525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
489525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
490525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageRange[] selectedPages = new PageRange[selectedPagesList.size()];
491525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        selectedPagesList.toArray(selectedPages);
492525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
493525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return selectedPages;
494525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
495525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
496c80814e70aea8940d0d43cd7170baae8c0c4e8e3Svet Ganov    public void destroy(Runnable callback) {
4974237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        mCloseGuard.close();
4984237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        mState = STATE_DESTROYED;
4994237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        if (DEBUG) {
5004237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov            Log.i(LOG_TAG, "STATE_DESTROYED");
5014237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        }
502c80814e70aea8940d0d43cd7170baae8c0c4e8e3Svet Ganov        mPageContentRepository.destroy(callback);
503525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
504525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
505525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
506525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    protected void finalize() throws Throwable {
507525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        try {
508525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (mState != STATE_DESTROYED) {
509525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mCloseGuard.warnIfOpen();
510c80814e70aea8940d0d43cd7170baae8c0c4e8e3Svet Ganov                destroy(null);
511525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
512525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        } finally {
513525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            super.finalize();
514525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
515525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
516525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
517525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int computePageIndexInDocument(int indexInAdapter) {
518525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int skippedAdapterPages = 0;
519525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int selectedPagesCount = mSelectedPages.length;
520525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        for (int i = 0; i < selectedPagesCount; i++) {
521525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange pageRange = PageRangeUtils.asAbsoluteRange(
522525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mSelectedPages[i], mDocumentPageCount);
523525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            skippedAdapterPages += pageRange.getSize();
524525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (skippedAdapterPages > indexInAdapter) {
525525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                final int overshoot = skippedAdapterPages - indexInAdapter - 1;
526525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                return pageRange.getEnd() - overshoot;
527525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
528525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
529525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return INVALID_PAGE_INDEX;
530525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
531525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
532525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int computePageIndexInFile(int pageIndexInDocument) {
533525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (!PageRangeUtils.contains(mSelectedPages, pageIndexInDocument)) {
534525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            return INVALID_PAGE_INDEX;
535525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
536525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mWrittenPages == null) {
537525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            return INVALID_PAGE_INDEX;
538525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
539525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
540525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int indexInFile = INVALID_PAGE_INDEX;
541525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int rangeCount = mWrittenPages.length;
542525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        for (int i = 0; i < rangeCount; i++) {
543525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange pageRange = mWrittenPages[i];
544525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (!pageRange.contains(pageIndexInDocument)) {
545525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                indexInFile += pageRange.getSize();
546525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            } else {
547525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                indexInFile += pageIndexInDocument - pageRange.getStart() + 1;
548525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                return indexInFile;
549525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
550525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
551525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return INVALID_PAGE_INDEX;
552525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
553525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
554525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private void setConfirmedPages(PageRange[] pagesInDocument, int documentPageCount) {
555525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mConfirmedPagesInDocument.clear();
556525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int rangeCount = pagesInDocument.length;
557525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        for (int i = 0; i < rangeCount; i++) {
558525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange pageRange = PageRangeUtils.asAbsoluteRange(pagesInDocument[i],
559525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    documentPageCount);
560525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            for (int j = pageRange.getStart(); j <= pageRange.getEnd(); j++) {
561525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mConfirmedPagesInDocument.put(j, null);
562525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
563525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
564525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
565525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
566525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private void onSelectedPageNotInFile(int pageInDocument) {
567525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageRange[] requestedPages = computeRequestedPages(pageInDocument);
568525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (!Arrays.equals(mRequestedPages, requestedPages)) {
569525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mRequestedPages = requestedPages;
570525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (DEBUG) {
571525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Log.i(LOG_TAG, "Requesting pages: " + Arrays.toString(mRequestedPages));
572525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
5735ef522bc19cc9fc1af48cccd2865744228c5ec02Svetoslav            mCallbacks.onRequestContentUpdate();
574525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
575525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
576525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
577525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private PageRange[] computeRequestedPages(int pageInDocument) {
578525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mRequestedPages != null &&
579525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRangeUtils.contains(mRequestedPages, pageInDocument)) {
580525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            return mRequestedPages;
581525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
582525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
583525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        List<PageRange> pageRangesList = new ArrayList<>();
584525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
585525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int remainingPagesToRequest = MAX_PREVIEW_PAGES_BATCH;
586525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int selectedPagesCount = mSelectedPages.length;
587525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
588525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // We always request the pages that are bound, i.e. shown on screen.
589525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageRange[] boundPagesInDocument = computeBoundPagesInDocument();
590525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
591525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int boundRangeCount = boundPagesInDocument.length;
592525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        for (int i = 0; i < boundRangeCount; i++) {
593525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange boundRange = boundPagesInDocument[i];
594525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            pageRangesList.add(boundRange);
595525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
596525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        remainingPagesToRequest -= PageRangeUtils.getNormalizedPageCount(
597525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                boundPagesInDocument, mDocumentPageCount);
598525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
599525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final boolean requestFromStart = mRequestedPages == null
600525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                || pageInDocument > mRequestedPages[mRequestedPages.length - 1].getEnd();
601525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
602525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (!requestFromStart) {
603525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (DEBUG) {
604525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Log.i(LOG_TAG, "Requesting from end");
605525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
606525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
607525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // Reminder that ranges are always normalized.
608525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            for (int i = selectedPagesCount - 1; i >= 0; i--) {
609525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (remainingPagesToRequest <= 0) {
610525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    break;
611525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
612525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
613525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange selectedRange = PageRangeUtils.asAbsoluteRange(mSelectedPages[i],
614525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        mDocumentPageCount);
615525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (pageInDocument < selectedRange.getStart()) {
616525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    continue;
617525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
618525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
619525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange pagesInRange;
620525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                int rangeSpan;
621525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
622525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (selectedRange.contains(pageInDocument)) {
623525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = pageInDocument - selectedRange.getStart() + 1;
624525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
625525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int fromPage = Math.max(pageInDocument - rangeSpan - 1, 0);
626525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = Math.max(rangeSpan, 0);
627525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    pagesInRange = new PageRange(fromPage, pageInDocument);
628525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } else {
629525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = selectedRange.getSize();
630525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
631525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = Math.max(rangeSpan, 0);
632525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int fromPage = Math.max(selectedRange.getEnd() - rangeSpan - 1, 0);
633525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int toPage = selectedRange.getEnd();
634525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    pagesInRange = new PageRange(fromPage, toPage);
635525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
636525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
637525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                pageRangesList.add(pagesInRange);
638525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                remainingPagesToRequest -= rangeSpan;
639525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
640525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        } else {
641525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (DEBUG) {
642525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Log.i(LOG_TAG, "Requesting from start");
643525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
644525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
645525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // Reminder that ranges are always normalized.
646525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            for (int i = 0; i < selectedPagesCount; i++) {
647525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (remainingPagesToRequest <= 0) {
648525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    break;
649525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
650525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
651525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange selectedRange = PageRangeUtils.asAbsoluteRange(mSelectedPages[i],
652525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        mDocumentPageCount);
653525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (pageInDocument > selectedRange.getEnd()) {
654525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    continue;
655525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
656525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
657525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange pagesInRange;
658525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                int rangeSpan;
659525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
660525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (selectedRange.contains(pageInDocument)) {
661525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = selectedRange.getEnd() - pageInDocument + 1;
662525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
663525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int toPage = Math.min(pageInDocument + rangeSpan - 1,
664525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            mDocumentPageCount - 1);
665525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    pagesInRange = new PageRange(pageInDocument, toPage);
666525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } else {
667525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = selectedRange.getSize();
668525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    rangeSpan = Math.min(rangeSpan, remainingPagesToRequest);
669525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int fromPage = selectedRange.getStart();
670525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int toPage = Math.min(selectedRange.getStart() + rangeSpan - 1,
671525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            mDocumentPageCount - 1);
672525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    pagesInRange = new PageRange(fromPage, toPage);
673525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
674525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
675525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (DEBUG) {
676525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    Log.i(LOG_TAG, "computeRequestedPages() Adding range:" + pagesInRange);
677525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
678525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                pageRangesList.add(pagesInRange);
679525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                remainingPagesToRequest -= rangeSpan;
680525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
681525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
682525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
683525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageRange[] pageRanges = new PageRange[pageRangesList.size()];
684525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        pageRangesList.toArray(pageRanges);
685525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
686525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return PageRangeUtils.normalize(pageRanges);
687525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
688525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
689525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private PageRange[] computeBoundPagesInDocument() {
690525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        List<PageRange> pagesInDocumentList = new ArrayList<>();
691525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
692525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int fromPage = INVALID_PAGE_INDEX;
693525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int toPage = INVALID_PAGE_INDEX;
694525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
695525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int boundPageCount = mBoundPagesInAdapter.size();
696525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        for (int i = 0; i < boundPageCount; i++) {
697525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // The container is a sparse array, so keys are sorted in ascending order.
698525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            final int boundPageInAdapter = mBoundPagesInAdapter.keyAt(i);
699525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            final int boundPageInDocument = computePageIndexInDocument(boundPageInAdapter);
700525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
701525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (fromPage == INVALID_PAGE_INDEX) {
702525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                fromPage = boundPageInDocument;
703525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
704525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
705525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (toPage == INVALID_PAGE_INDEX) {
706525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                toPage = boundPageInDocument;
707525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
708525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
709525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (boundPageInDocument > toPage + 1) {
710525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange pageRange = new PageRange(fromPage, toPage);
711525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                pagesInDocumentList.add(pageRange);
712525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                fromPage = toPage = boundPageInDocument;
713525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            } else {
714525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                toPage = boundPageInDocument;
715525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
716525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
717525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
718525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (fromPage != INVALID_PAGE_INDEX && toPage != INVALID_PAGE_INDEX) {
719525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange pageRange = new PageRange(fromPage, toPage);
720525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            pagesInDocumentList.add(pageRange);
721525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
722525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
723525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageRange[] pageInDocument = new PageRange[pagesInDocumentList.size()];
724525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        pagesInDocumentList.toArray(pageInDocument);
725525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
726525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (DEBUG) {
727525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Log.i(LOG_TAG, "Bound pages: " + Arrays.toString(pageInDocument));
728525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
729525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
730525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return pageInDocument;
731525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
732525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
733525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private void recyclePageView(PageContentView page, int pageIndexInAdapter) {
734525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageContentProvider provider = page.getPageContentProvider();
735525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (provider != null) {
7360d2d9637ed4f46062d508cfa7da0e9776fc444f8Svetoslav            page.init(null, mEmptyState, mMediaSize, mMinMargins);
737525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mPageContentRepository.releasePageContentProvider(provider);
738525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
739f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav        mBoundPagesInAdapter.remove(pageIndexInAdapter);
740525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        page.setTag(null);
741525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
742525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
743525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void startPreloadContent(PageRange pageRangeInAdapter) {
7441710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        final int startPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getStart());
7451710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        final int startPageInFile = computePageIndexInFile(startPageInDocument);
7461710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        final int endPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getEnd());
7471710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        final int endPageInFile = computePageIndexInFile(endPageInDocument);
7481710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        if (startPageInDocument != INVALID_PAGE_INDEX && endPageInDocument != INVALID_PAGE_INDEX) {
7491710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav            mPageContentRepository.startPreload(startPageInFile, endPageInFile);
7501710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        }
751525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
752525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
753525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void stopPreloadContent() {
7541710e0318db5db833974e390eb4bde54ab2f3c62Svetoslav        mPageContentRepository.stopPreload();
755525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
756525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
757525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private void throwIfNotOpened() {
758525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mState != STATE_OPENED) {
759525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            throw new IllegalStateException("Not opened");
760525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
761525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
762525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
763525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private void throwIfNotClosed() {
764525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (mState != STATE_CLOSED) {
765525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            throw new IllegalStateException("Not closed");
766525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
767525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
768525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
769525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final class MyViewHolder extends ViewHolder {
770525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        int mPageInAdapter;
771525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
772525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private MyViewHolder(View itemView) {
773525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            super(itemView);
774525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
775525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
776525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
777525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final class PageClickListener implements OnClickListener {
778525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        @Override
779e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav        public void onClick(View view) {
780e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav            PreviewPageFrame page = (PreviewPageFrame) view;
781525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            MyViewHolder holder = (MyViewHolder) page.getTag();
782525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            final int pageInAdapter = holder.mPageInAdapter;
783525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            final int pageInDocument = computePageIndexInDocument(pageInAdapter);
784525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (mConfirmedPagesInDocument.indexOfKey(pageInDocument) < 0) {
785525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mConfirmedPagesInDocument.put(pageInDocument, null);
786e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav                page.setSelected(true, true);
787525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            } else {
788c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav                if (mConfirmedPagesInDocument.size() <= 1) {
789c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav                    return;
790c404cacd3a480776dd625fb300810ffccc5f51b0Svetoslav                }
791525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mConfirmedPagesInDocument.remove(pageInDocument);
792e652b02d45bbda11c9cb8f663fa7f25903b90225Svetoslav                page.setSelected(false, true);
793525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
794525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
795525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
796525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov}
797