1 /*
2  * Copyright (C) 2020 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 com.android.bips;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.net.Uri;
23 import android.os.AsyncTask;
24 import android.os.Bundle;
25 import android.os.CancellationSignal;
26 import android.os.ParcelFileDescriptor;
27 import android.print.PageRange;
28 import android.print.PrintAttributes;
29 import android.print.PrintDocumentAdapter;
30 import android.print.PrintDocumentInfo;
31 import android.print.PrintManager;
32 import android.util.Log;
33 import android.webkit.URLUtil;
34 
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.io.OutputStream;
39 
40 /**
41  * Activity to receive share-to-print intents for PDF documents.
42  */
43 public class PdfPrintActivity extends Activity {
44     private static final String TAG = PdfPrintActivity.class.getSimpleName();
45     private static final boolean DEBUG = false;
46 
47     private CancellationSignal mCancellationSignal;
48     private String mJobName;
49     Uri mContentUri = null;
50 
51     @Override
onCreate(Bundle savedInstanceState)52     protected void onCreate(Bundle savedInstanceState) {
53         super.onCreate(savedInstanceState);
54         String action = getIntent().getAction();
55         if (Intent.ACTION_SEND.equals(action)) {
56             mContentUri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
57         } else if (Intent.ACTION_VIEW.equals(action)) {
58             mContentUri = getIntent().getData();
59         }
60         if (mContentUri == null) {
61             finish();
62         }
63         mJobName = URLUtil.guessFileName(getIntent().getStringExtra(Intent.EXTRA_TEXT), null,
64             getIntent().resolveType(this));
65         if (DEBUG) Log.d(TAG, "onCreate() uri=" + mContentUri + " jobName=" + mJobName);
66 
67         PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
68         if (printManager == null) {
69             finish();
70             return;
71         }
72 
73         PrintAttributes printAttributes = new PrintAttributes.Builder()
74                 .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
75                 .build();
76         printManager.print(mJobName, new PdfAdapter(), printAttributes);
77     }
78 
79     @Override
onDestroy()80     protected void onDestroy() {
81         if (DEBUG) Log.d(TAG, "onDestroy()");
82         if (mCancellationSignal != null) {
83             mCancellationSignal.cancel();
84         }
85         super.onDestroy();
86     }
87 
88     private class PdfAdapter extends PrintDocumentAdapter {
89         @Override
onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes, CancellationSignal cancellationSignal, LayoutResultCallback callback, Bundle bundle)90         public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
91                 CancellationSignal cancellationSignal, LayoutResultCallback callback,
92                 Bundle bundle) {
93             if (DEBUG) Log.d(TAG, "onLayout() attrs=" + newAttributes);
94 
95             PrintDocumentInfo info = new PrintDocumentInfo.Builder(mJobName)
96                     .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
97                     .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN)
98                     .build();
99             callback.onLayoutFinished(info, false);
100         }
101 
102         @Override
onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor, CancellationSignal cancellationSignal, WriteResultCallback callback)103         public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
104                 CancellationSignal cancellationSignal, WriteResultCallback callback) {
105             if (DEBUG) Log.d(TAG, "onWrite()");
106             mCancellationSignal = cancellationSignal;
107             new PdfDeliverTask(fileDescriptor, callback).execute();
108         }
109 
110         @Override
onFinish()111         public void onFinish() {
112             if (DEBUG) Log.d(TAG, "onFinish()");
113             finish();
114         }
115     }
116 
117     private class PdfDeliverTask extends AsyncTask<Void, Void, Void> {
118         ParcelFileDescriptor mDescriptor;
119         PrintDocumentAdapter.WriteResultCallback mCallback;
120 
PdfDeliverTask(ParcelFileDescriptor descriptor, PrintDocumentAdapter.WriteResultCallback callback)121         PdfDeliverTask(ParcelFileDescriptor descriptor,
122                        PrintDocumentAdapter.WriteResultCallback callback) {
123             mDescriptor = descriptor;
124             mCallback = callback;
125         }
126 
127         @Override
doInBackground(Void... voids)128         protected Void doInBackground(Void... voids) {
129             try (InputStream in = getContentResolver().openInputStream(mContentUri)) {
130                 if (in == null) {
131                     throw new IOException("Failed to open input stream");
132                 }
133                 try (OutputStream out = new FileOutputStream(mDescriptor.getFileDescriptor())) {
134                     byte[] buffer = new byte[10 * 1024];
135                     int length;
136                     while ((length = in.read(buffer)) >= 0 && !mCancellationSignal.isCanceled()) {
137                         out.write(buffer, 0, length);
138                     }
139                 }
140                 if (mCancellationSignal.isCanceled()) {
141                     mCallback.onWriteCancelled();
142                 } else {
143                     mCallback.onWriteFinished(new PageRange[] { PageRange.ALL_PAGES });
144                 }
145             } catch (IOException e) {
146                 Log.w(TAG, "Failed to deliver content", e);
147                 mCallback.onWriteFailed(e.getMessage());
148             }
149             return null;
150         }
151     }
152 }
153