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