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