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.os.Handler;
20525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.os.Looper;
21525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.os.Message;
22525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.os.ParcelFileDescriptor;
23525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PageRange;
24525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintAttributes.MediaSize;
25525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintAttributes.Margins;
26525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.print.PrintDocumentInfo;
276f249835a4ff9e7e7e3ca0190b7ecf72e689656dSvetoslavimport android.support.v7.widget.GridLayoutManager;
28525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.support.v7.widget.RecyclerView;
29525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.support.v7.widget.RecyclerView.ViewHolder;
30525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.support.v7.widget.RecyclerView.LayoutManager;
31525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport android.view.View;
32525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.internal.os.SomeArgs;
33525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.R;
34525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.model.MutexFileProvider;
35525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.widget.PrintContentView;
36525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.widget.EmbeddedContentContainer;
37525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport com.android.printspooler.widget.PrintOptionsLayout;
38525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
39525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.io.File;
40525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.io.FileNotFoundException;
41525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.util.ArrayList;
42525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovimport java.util.List;
43525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
44525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganovclass PrintPreviewController implements MutexFileProvider.OnReleaseRequestCallback,
45525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        PageAdapter.PreviewArea, EmbeddedContentContainer.OnSizeChangeListener {
46525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
4715cbc8a03250eafdf947cd8ad4e77f34444d5ba4Svetoslav    private final PrintActivity mActivity;
48525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
49525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final MutexFileProvider mFileProvider;
50525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final MyHandler mHandler;
51525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
52525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final PageAdapter mPageAdapter;
536f249835a4ff9e7e7e3ca0190b7ecf72e689656dSvetoslav    private final GridLayoutManager mLayoutManger;
54525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
5515cbc8a03250eafdf947cd8ad4e77f34444d5ba4Svetoslav    private final PrintOptionsLayout mPrintOptionsLayout;
56525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final RecyclerView mRecyclerView;
57525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final PrintContentView mContentView;
58525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final EmbeddedContentContainer mEmbeddedContentContainer;
59525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
60525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final PreloadController mPreloadController;
61525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
62525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private int mDocumentPageCount;
63525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
64525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public PrintPreviewController(PrintActivity activity, MutexFileProvider fileProvider) {
65525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mActivity = activity;
66525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mHandler = new MyHandler(activity.getMainLooper());
67525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mFileProvider = fileProvider;
68525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
69525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPrintOptionsLayout = (PrintOptionsLayout) activity.findViewById(R.id.options_container);
70525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPageAdapter = new PageAdapter(activity, activity, this);
71525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
72525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int columnCount = mActivity.getResources().getInteger(
73525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.integer.preview_page_per_row_count);
74525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
756f249835a4ff9e7e7e3ca0190b7ecf72e689656dSvetoslav        mLayoutManger = new GridLayoutManager(mActivity, columnCount);
7615cbc8a03250eafdf947cd8ad4e77f34444d5ba4Svetoslav
77525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mRecyclerView = (RecyclerView) activity.findViewById(R.id.preview_content);
78525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mRecyclerView.setLayoutManager(mLayoutManger);
79525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mRecyclerView.setAdapter(mPageAdapter);
807fd5ada98aa9e035682531d9fe25633fdd24a058Svetoslav        mRecyclerView.setItemViewCacheSize(0);
81525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPreloadController = new PreloadController(mRecyclerView);
82525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mRecyclerView.setOnScrollListener(mPreloadController);
83525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
84525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mContentView = (PrintContentView) activity.findViewById(R.id.options_content);
85525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mEmbeddedContentContainer = (EmbeddedContentContainer) activity.findViewById(
86525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.id.embedded_content_container);
87525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mEmbeddedContentContainer.setOnSizeChangeListener(this);
88525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
89525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
90525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
91525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onSizeChanged(int width, int height) {
92525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPageAdapter.onPreviewAreaSizeChanged();
93525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
94525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
95525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public boolean isOptionsOpened() {
96525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mContentView.isOptionsOpened();
97525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
98525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
99525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void closeOptions() {
100525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mContentView.closeOptions();
101525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
102525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
103525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void setUiShown(boolean shown) {
104525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (shown) {
105525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mRecyclerView.setVisibility(View.VISIBLE);
106525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        } else {
107525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mRecyclerView.setVisibility(View.GONE);
108525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
109525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
110525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
111525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onOrientationChanged() {
112525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // Adjust the print option column count.
113525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        final int optionColumnCount = mActivity.getResources().getInteger(
114525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                R.integer.print_option_column_count);
115525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPrintOptionsLayout.setColumnCount(optionColumnCount);
116525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mPageAdapter.onOrientationChanged();
117525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
118525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
119525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public int getFilePageCount() {
120525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mPageAdapter.getFilePageCount();
121525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
122525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
123525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public PageRange[] getSelectedPages() {
124525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mPageAdapter.getSelectedPages();
125525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
126525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
127525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public PageRange[] getRequestedPages() {
128525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mPageAdapter.getRequestedPages();
129525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
130525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
131525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onContentUpdated(boolean documentChanged, int documentPageCount,
132525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageRange[] writtenPages, PageRange[] selectedPages, MediaSize mediaSize,
133525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Margins minMargins) {
134f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav        boolean contentChanged = false;
135525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
136525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (documentChanged) {
137525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            contentChanged = true;
138525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
139525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
140525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (documentPageCount != mDocumentPageCount) {
141525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mDocumentPageCount = documentPageCount;
142525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            contentChanged = true;
143525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
144525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
145525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (contentChanged) {
146525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            // If not closed, close as we start over.
147525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (mPageAdapter.isOpened()) {
148525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Message operation = mHandler.obtainMessage(MyHandler.MSG_CLOSE);
149525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mHandler.enqueueOperation(operation);
150525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
151525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
152525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
153525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // The content changed. In this case we have to invalidate
154525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // all rendered pages and reopen the file...
155f3f963b0bebea91b17f7e60d9b826c458bfde38cSvetoslav        if ((contentChanged || !mPageAdapter.isOpened()) && writtenPages != null) {
156525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            Message operation = mHandler.obtainMessage(MyHandler.MSG_OPEN);
157525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mHandler.enqueueOperation(operation);
158525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
159525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
160525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // Update the attributes before after closed to avoid flicker.
161525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        SomeArgs args = SomeArgs.obtain();
162525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        args.arg1 = writtenPages;
163525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        args.arg2 = selectedPages;
164525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        args.arg3 = mediaSize;
165525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        args.arg4 = minMargins;
166525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        args.argi1 = documentPageCount;
167525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
168525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        Message operation = mHandler.obtainMessage(MyHandler.MSG_UPDATE, args);
169525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mHandler.enqueueOperation(operation);
170525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
171525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // If document changed and has pages we want to start preloading.
172525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        if (contentChanged && writtenPages != null) {
173525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            operation = mHandler.obtainMessage(MyHandler.MSG_START_PRELOAD);
174525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mHandler.enqueueOperation(operation);
175525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
176525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
177525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
178525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
179525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void onReleaseRequested(final File file) {
180525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // This is called from the async task's single threaded executor
181525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        // thread, i.e. not on the main thread - so post a message.
182525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mHandler.post(new Runnable() {
183525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            @Override
184525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            public void run() {
185525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                // At this point the other end will write to the file, hence
186525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                // we have to close it and reopen after the write completes.
18762ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav                if (mPageAdapter.isOpened()) {
18862ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav                    Message operation = mHandler.obtainMessage(MyHandler.MSG_CLOSE);
18962ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav                    mHandler.enqueueOperation(operation);
19062ce332c141cf7bc7200c4c87d63e395874fc3ecSvetoslav                }
191525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
192525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        });
193525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
194525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
195c80814e70aea8940d0d43cd7170baae8c0c4e8e3Svet Ganov    public void destroy(Runnable callback) {
1964237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        mHandler.cancelQueuedOperations();
1974237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        mRecyclerView.setAdapter(null);
198c80814e70aea8940d0d43cd7170baae8c0c4e8e3Svet Ganov        mPageAdapter.destroy(callback);
199525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
200525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
201525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
202525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public int getWidth() {
203525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mEmbeddedContentContainer.getWidth();
204525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
205525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
206525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
207525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public int getHeight() {
208525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        return mEmbeddedContentContainer.getHeight();
209525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
210525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
211525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
212525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void setColumnCount(int columnCount) {
213525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mLayoutManger.setSpanCount(columnCount);
214525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
215525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
216525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    @Override
217525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    public void setPadding(int left, int top , int right, int bottom) {
218525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        mRecyclerView.setPadding(left, top, right, bottom);
219525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
220525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
221525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    private final class MyHandler extends Handler {
222525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public static final int MSG_OPEN = 1;
223525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public static final int MSG_CLOSE = 2;
224525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public static final int MSG_UPDATE = 4;
225525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public static final int MSG_START_PRELOAD = 5;
226525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
227525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private boolean mAsyncOperationInProgress;
228525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
229525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private final Runnable mOnAsyncOperationDoneCallback = new Runnable() {
230525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            @Override
231525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            public void run() {
232525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                mAsyncOperationInProgress = false;
233525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                handleNextOperation();
234525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
235525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        };
236525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
237525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private final List<Message> mPendingOperations = new ArrayList<>();
238525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
239525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public MyHandler(Looper looper) {
240525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            super(looper, null, false);
241525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
242525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
2434237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        public void cancelQueuedOperations() {
2444237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov            mPendingOperations.clear();
2454237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov        }
2464237c92d850b7fb0fa0be15df94e4d1689e353fcSvet Ganov
247525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void enqueueOperation(Message message) {
248525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mPendingOperations.add(message);
249525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            handleNextOperation();
250525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
251525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
252525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void handleNextOperation() {
253525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            while (!mPendingOperations.isEmpty() && !mAsyncOperationInProgress) {
254525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                Message operation = mPendingOperations.remove(0);
255525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                handleMessage(operation);
256525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
257525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
258525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
259525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        @Override
260525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void handleMessage(Message message) {
261525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            switch (message.what) {
262525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case MSG_OPEN: {
263525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    try {
264525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        File file = mFileProvider.acquireFile(PrintPreviewController.this);
265525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file,
266525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                                ParcelFileDescriptor.MODE_READ_ONLY);
267525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
268525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        mAsyncOperationInProgress = true;
269525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        mPageAdapter.open(pfd, new Runnable() {
270525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            @Override
271525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            public void run() {
272525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                                if (mDocumentPageCount == PrintDocumentInfo.PAGE_COUNT_UNKNOWN) {
273525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                                    mDocumentPageCount = mPageAdapter.getFilePageCount();
274525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                                    mActivity.updateOptionsUi();
275525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                                }
276525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                                mOnAsyncOperationDoneCallback.run();
277525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            }
278525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        });
279525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    } catch (FileNotFoundException fnfe) {
280525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        /* ignore - file guaranteed to be there */
281525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    }
282525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } break;
283525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
284525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case MSG_CLOSE: {
285525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mAsyncOperationInProgress = true;
286525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mPageAdapter.close(new Runnable() {
287525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        @Override
288525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        public void run() {
289525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            mFileProvider.releaseFile();
290525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            mOnAsyncOperationDoneCallback.run();
291525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        }
292525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    });
293525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } break;
294525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
295525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case MSG_UPDATE: {
296525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    SomeArgs args = (SomeArgs) message.obj;
297525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    PageRange[] writtenPages = (PageRange[]) args.arg1;
298525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    PageRange[] selectedPages = (PageRange[]) args.arg2;
299525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    MediaSize mediaSize = (MediaSize) args.arg3;
300525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    Margins margins = (Margins) args.arg4;
301525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    final int pageCount = args.argi1;
302525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    args.recycle();
303525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
304525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mPageAdapter.update(writtenPages, selectedPages, pageCount,
305525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            mediaSize, margins);
306525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
307525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } break;
308525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
309525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case MSG_START_PRELOAD: {
310525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    mPreloadController.startPreloadContent();
311525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } break;
312525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
313525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
314525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
315525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
316753c114d84328219a0ccbffdd462a69e04ef8c4fYigit Boyar    private final class PreloadController extends RecyclerView.OnScrollListener {
317525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private final RecyclerView mRecyclerView;
318525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
319525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private int mOldScrollState;
320525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
321525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public PreloadController(RecyclerView recyclerView) {
322525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mRecyclerView = recyclerView;
323525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mOldScrollState = mRecyclerView.getScrollState();
324525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
325525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
326525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        @Override
327753c114d84328219a0ccbffdd462a69e04ef8c4fYigit Boyar        public void onScrollStateChanged(RecyclerView recyclerView, int state) {
328525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            switch (mOldScrollState) {
329525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case RecyclerView.SCROLL_STATE_SETTLING: {
330525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    if (state == RecyclerView.SCROLL_STATE_IDLE
331525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                            || state == RecyclerView.SCROLL_STATE_DRAGGING){
332525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        startPreloadContent();
333525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    }
334525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } break;
335525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
336525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case RecyclerView.SCROLL_STATE_IDLE:
337525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                case RecyclerView.SCROLL_STATE_DRAGGING: {
338525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    if (state == RecyclerView.SCROLL_STATE_SETTLING) {
339525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                        stopPreloadContent();
340525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    }
341525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                } break;
342525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
343525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            mOldScrollState = state;
344525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
345525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
346525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void startPreloadContent() {
347525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageAdapter pageAdapter = (PageAdapter) mRecyclerView.getAdapter();
3487fd5ada98aa9e035682531d9fe25633fdd24a058Svetoslav            if (pageAdapter != null && pageAdapter.isOpened()) {
349525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                PageRange shownPages = computeShownPages();
350525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                if (shownPages != null) {
351525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                    pageAdapter.startPreloadContent(shownPages);
352525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                }
353525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
354525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
355525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
356525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        public void stopPreloadContent() {
357525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            PageAdapter pageAdapter = (PageAdapter) mRecyclerView.getAdapter();
3587fd5ada98aa9e035682531d9fe25633fdd24a058Svetoslav            if (pageAdapter != null && pageAdapter.isOpened()) {
359525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                pageAdapter.stopPreloadContent();
360525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
361525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
362525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
363525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        private PageRange computeShownPages() {
364525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            final int childCount = mRecyclerView.getChildCount();
365525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            if (childCount > 0) {
366525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                LayoutManager layoutManager = mRecyclerView.getLayoutManager();
367525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
368525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                View firstChild = layoutManager.getChildAt(0);
369525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                ViewHolder firstHolder = mRecyclerView.getChildViewHolder(firstChild);
370525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
371525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                View lastChild = layoutManager.getChildAt(layoutManager.getChildCount() - 1);
372525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                ViewHolder lastHolder = mRecyclerView.getChildViewHolder(lastChild);
373525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov
374525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov                return new PageRange(firstHolder.getPosition(), lastHolder.getPosition());
375525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            }
376525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov            return null;
377525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov        }
378525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov    }
379525a66b2bb5abf844aff2109bdc9ed819566beceSvet Ganov}
380