1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package foo.bar.print;
18
19import java.io.FileNotFoundException;
20import java.io.FileOutputStream;
21import java.io.IOException;
22import java.util.ArrayList;
23import java.util.List;
24
25import android.app.Activity;
26import android.content.Context;
27import android.graphics.pdf.PdfDocument.Page;
28import android.net.Uri;
29import android.os.AsyncTask;
30import android.os.Bundle;
31import android.os.CancellationSignal;
32import android.os.ParcelFileDescriptor;
33import android.print.PageRange;
34import android.print.PrintAttributes;
35import android.print.PrintDocumentAdapter;
36import android.print.PrintDocumentInfo;
37import android.print.PrintManager;
38import android.print.pdf.PrintedPdfDocument;
39import android.support.v4.print.PrintHelper;
40import android.util.SparseIntArray;
41import android.view.Menu;
42import android.view.MenuItem;
43import android.view.View;
44
45/**
46 * Simple sample of how to use the print APIs.
47 */
48public class Main extends Activity {
49
50    public static final String LOG_TAG = "PrintActivity";
51
52    private static final int PAGE_COUNT = 5;
53
54    @Override
55    protected void onCreate(Bundle savedInstanceState) {
56        super.onCreate(savedInstanceState);
57        setContentView(R.layout.activity_main);
58    }
59
60    @Override
61    public boolean onCreateOptionsMenu(Menu menu) {
62        super.onCreateOptionsMenu(menu);
63        getMenuInflater().inflate(R.menu.activity_main, menu);
64        return true;
65    }
66
67    @Override
68    public boolean onOptionsItemSelected(MenuItem item) {
69        switch (item.getItemId()) {
70            case R.id.menu_print:
71                printView();
72                return true;
73            case R.id.menu_print_uri_portrait_force:
74            case R.id.menu_print_uri_portrait:
75            case R.id.menu_print_uri_landscape:
76                try {
77                    PrintHelper ph = new PrintHelper(this);
78                    Uri uri = null;
79                    switch (item.getItemId()) {
80                        case R.id.menu_print_uri_portrait_force:
81                            ph.setOrientation(PrintHelper.ORIENTATION_PORTRAIT);
82                            /* fall through */
83                        case R.id.menu_print_uri_portrait:
84                            uri = Uri.parse("android.resource://foo.bar.print/raw/portrait");
85                            break;
86                        case R.id.menu_print_uri_landscape:
87                            uri = Uri.parse("android.resource://foo.bar.print/raw/landscape");
88                            break;
89                    }
90
91                    ph.printBitmap("Print Uri", uri);
92                } catch (FileNotFoundException e) {
93                    e.printStackTrace();
94                }
95                return true;
96            default:
97                return super.onOptionsItemSelected(item);
98        }
99    }
100
101    private void printView() {
102        PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
103        final View view = findViewById(R.id.content);
104
105        printManager.print("Print_View",
106            new PrintDocumentAdapter() {
107                private static final int RESULT_LAYOUT_FAILED = 1;
108                private static final int RESULT_LAYOUT_FINISHED = 2;
109
110                private PrintAttributes mPrintAttributes;
111
112                @Override
113                public void onLayout(final PrintAttributes oldAttributes,
114                        final PrintAttributes newAttributes,
115                        final CancellationSignal cancellationSignal,
116                        final LayoutResultCallback callback,
117                        final Bundle metadata) {
118
119                    new AsyncTask<Void, Void, Integer>() {
120                        @Override
121                        protected void onPreExecute() {
122                            // First register for cancellation requests.
123                            cancellationSignal.setOnCancelListener(() -> cancel(true));
124                            mPrintAttributes = newAttributes;
125                        }
126
127                        @Override
128                        protected Integer doInBackground(Void... params) {
129                            try {
130                                // Pretend we do some layout work.
131                                for (int i = 0; i < PAGE_COUNT; i++) {
132                                    // Be nice and respond to cancellation.
133                                    if (isCancelled()) {
134                                        return null;
135                                    }
136                                    pretendDoingLayoutWork();
137                                }
138                                return RESULT_LAYOUT_FINISHED;
139                            } catch (Exception e) {
140                                return RESULT_LAYOUT_FAILED;
141                            }
142                        }
143
144                        @Override
145                        protected void onPostExecute(Integer result) {
146                            // The task was not cancelled, so handle the layout result.
147                            switch (result) {
148                                case RESULT_LAYOUT_FINISHED: {
149                                    PrintDocumentInfo info = new PrintDocumentInfo
150                                            .Builder("print_view.pdf")
151                                            .setContentType(PrintDocumentInfo
152                                                    .CONTENT_TYPE_DOCUMENT)
153                                            .setPageCount(PAGE_COUNT)
154                                            .build();
155                                    callback.onLayoutFinished(info, false);
156                                } break;
157
158                                case RESULT_LAYOUT_FAILED: {
159                                    callback.onLayoutFailed(null);
160                                } break;
161                            }
162                        }
163
164                        @Override
165                        protected void onCancelled(Integer result) {
166                            // Task was cancelled, report that.
167                            callback.onLayoutCancelled();
168                        }
169
170                        private void pretendDoingLayoutWork() throws Exception {
171
172                        }
173                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
174                }
175
176                @Override
177                public void onWrite(final PageRange[] pages,
178                        final ParcelFileDescriptor destination,
179                        final CancellationSignal canclleationSignal,
180                        final WriteResultCallback callback) {
181
182                    new AsyncTask<Void, Void, Integer>() {
183                        private static final int RESULT_WRITE_FAILED = 1;
184                        private static final int RESULT_WRITE_FINISHED = 2;
185
186                        private final SparseIntArray mWrittenPages = new SparseIntArray();
187                        private final PrintedPdfDocument mPdfDocument = new PrintedPdfDocument(
188                                Main.this, mPrintAttributes);
189
190                        @Override
191                        protected void onPreExecute() {
192                            // First register for cancellation requests.
193                            canclleationSignal.setOnCancelListener(() -> cancel(true));
194
195                            for (int i = 0; i < PAGE_COUNT; i++) {
196                                // Be nice and respond to cancellation.
197                                if (isCancelled()) {
198                                    return;
199                                }
200
201                                // Write the page only if it was requested.
202                                if (containsPage(pages, i)) {
203                                    mWrittenPages.append(mWrittenPages.size(), i);
204                                    Page page = mPdfDocument.startPage(i);
205                                    // The page of the PDF backed canvas size is in pixels (1/72") and
206                                    // smaller that the view. We scale down the drawn content and to
207                                    // fit. This does not lead to losing data as PDF is a vector format.
208                                    final float scale = (float) Math.min(mPdfDocument.getPageWidth(),
209                                            mPdfDocument.getPageHeight()) / Math.max(view.getWidth(), view.getHeight());
210                                    page.getCanvas().scale(scale, scale);
211                                    view.draw(page.getCanvas());
212                                    mPdfDocument.finishPage(page);
213                                }
214                            }
215                        }
216
217                        @Override
218                        protected Integer doInBackground(Void... params) {
219                            // Write the data and return success or failure.
220                            try {
221                                mPdfDocument.writeTo(new FileOutputStream(
222                                        destination.getFileDescriptor()));
223                                return RESULT_WRITE_FINISHED;
224                            } catch (IOException ioe) {
225                                return RESULT_WRITE_FAILED;
226                            }
227                        }
228
229                        @Override
230                        protected void onPostExecute(Integer result) {
231                            // The task was not cancelled, so handle the write result.
232                            switch (result) {
233                                case RESULT_WRITE_FINISHED: {
234                                    PageRange[] pageRanges = computePageRanges(mWrittenPages);
235                                    callback.onWriteFinished(pageRanges);
236                                } break;
237
238                                case RESULT_WRITE_FAILED: {
239                                    callback.onWriteFailed(null);
240                                } break;
241                            }
242
243                            mPdfDocument.close();
244                        }
245
246                        @Override
247                        protected void onCancelled(Integer result) {
248                            // Task was cancelled, report that.
249                            callback.onWriteCancelled();
250                            mPdfDocument.close();
251                        }
252                    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
253                }
254
255                private PageRange[] computePageRanges(SparseIntArray writtenPages) {
256                    List<PageRange> pageRanges = new ArrayList<>();
257
258                    int start;
259                    int end;
260                    final int writtenPageCount = writtenPages.size();
261                    for (int i = 0; i < writtenPageCount; i++) {
262                        start = writtenPages.valueAt(i);
263                        int oldEnd = end = start;
264                        while (i < writtenPageCount && (end - oldEnd) <= 1) {
265                            oldEnd = end;
266                            end = writtenPages.valueAt(i);
267                            i++;
268                        }
269                        PageRange pageRange = new PageRange(start, end);
270                        pageRanges.add(pageRange);
271                    }
272
273                    PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
274                    pageRanges.toArray(pageRangesArray);
275                    return pageRangesArray;
276                }
277
278                private boolean containsPage(PageRange[] pageRanges, int page) {
279                    final int pageRangeCount = pageRanges.length;
280                    for (int i = 0; i < pageRangeCount; i++) {
281                        if (pageRanges[i].getStart() <= page
282                                && pageRanges[i].getEnd() >= page) {
283                            return true;
284                        }
285                    }
286                    return false;
287                }
288        }, null);
289    }
290}
291