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