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 
17 package foo.bar.print;
18 
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.List;
23 
24 import android.app.Activity;
25 import android.content.Context;
26 import android.graphics.pdf.PdfDocument.Page;
27 import android.os.AsyncTask;
28 import android.os.Bundle;
29 import android.os.CancellationSignal;
30 import android.os.CancellationSignal.OnCancelListener;
31 import android.os.ParcelFileDescriptor;
32 import android.print.PageRange;
33 import android.print.PrintAttributes;
34 import android.print.PrintDocumentAdapter;
35 import android.print.PrintDocumentInfo;
36 import android.print.PrintManager;
37 import android.print.pdf.PrintedPdfDocument;
38 import android.util.SparseIntArray;
39 import android.view.Menu;
40 import android.view.MenuItem;
41 import android.view.View;
42 
43 /**
44  * Simple sample of how to use the print APIs.
45  */
46 public 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
onCreate(Bundle savedInstanceState)53     protected void onCreate(Bundle savedInstanceState) {
54         super.onCreate(savedInstanceState);
55         setContentView(R.layout.activity_main);
56     }
57 
58     @Override
onCreateOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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 
printView()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