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