1b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav/*
2b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * Copyright (C) 2013 The Android Open Source Project
3b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav *
4b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * Licensed under the Apache License, Version 2.0 (the "License");
5b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * you may not use this file except in compliance with the License.
6b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * You may obtain a copy of the License at
7b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav *
8b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav *      http://www.apache.org/licenses/LICENSE-2.0
9b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav *
10b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * Unless required by applicable law or agreed to in writing, software
11b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * distributed under the License is distributed on an "AS IS" BASIS,
12b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * See the License for the specific language governing permissions and
14b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * limitations under the License.
15b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav */
16b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
17b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavpackage android.support.v4.print;
18b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
19b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.content.Context;
20b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.graphics.Bitmap;
21b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.graphics.BitmapFactory;
22b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.graphics.Matrix;
23b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.graphics.RectF;
24b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.graphics.pdf.PdfDocument.Page;
25b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.net.Uri;
26b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.os.Bundle;
27b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.os.CancellationSignal;
28b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.os.ParcelFileDescriptor;
29b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.print.PageRange;
30b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.print.PrintAttributes;
31b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.print.PrintDocumentAdapter;
32b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.print.PrintDocumentInfo;
33b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.print.PrintManager;
34b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.print.pdf.PrintedPdfDocument;
35b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport android.util.Log;
36b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
37b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport java.io.FileNotFoundException;
38b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport java.io.FileOutputStream;
39b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport java.io.IOException;
40b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavimport java.io.InputStream;
41b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
42b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav/**
43b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav * Kitkat specific PrintManager API implementation.
44b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav */
45b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslavpublic class PrintHelperKitkat {
46b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    private static final String LOG_TAG = "PrintHelperKitkat";
47b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    // will be <= 300 dpi on A4 (8.3×11.7) paper (worst case of 150 dpi)
48b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    private final static int MAX_PRINT_SIZE = 3500;
49b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    final Context mContext;
50b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
51b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * image will be scaled but leave white space
52b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
53b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    public static final int SCALE_MODE_FIT = 1;
54b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
55b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * image will fill the paper and be cropped (default)
56b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
57b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    public static final int SCALE_MODE_FILL = 2;
58b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
5909ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    /**
6009ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * this is a black and white image
6109ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     */
6209ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    public static final int COLOR_MODE_MONOCHROME = 1;
6309ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov
6409ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    /**
6509ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * this is a color image (default)
6609ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     */
6709ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    public static final int COLOR_MODE_COLOR = 2;
6809ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov
69b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    int mScaleMode = SCALE_MODE_FILL;
70b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
7109ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    int mColorMode = COLOR_MODE_COLOR;
7209ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov
73b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    PrintHelperKitkat(Context context) {
74b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        mContext = context;
75b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
76b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
77b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
78b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Selects whether the image will fill the paper and be cropped
79b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * {@link #SCALE_MODE_FIT}
80b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * or whether the image will be scaled but leave white space
81b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * {@link #SCALE_MODE_FILL}.
82b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     *
83b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param scaleMode {@link #SCALE_MODE_FIT} or
84b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     *                  {@link #SCALE_MODE_FILL}
85b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
86b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    public void setScaleMode(int scaleMode) {
87b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        mScaleMode = scaleMode;
88b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
89b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
90b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
91b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Returns the scale mode with which the image will fill the paper.
92b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     *
93b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @return The scale Mode: {@link #SCALE_MODE_FIT} or
94b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * {@link #SCALE_MODE_FILL}
95b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
96b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    public int getScaleMode() {
97b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        return mScaleMode;
98b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
99b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
100b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
10109ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * Sets whether the image will be printed in color (default)
10209ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * {@link #COLOR_MODE_COLOR} or in back and white
10309ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * {@link #COLOR_MODE_MONOCHROME}.
10409ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     *
10509ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * @param colorMode The color mode which is one of
10609ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * {@link #COLOR_MODE_COLOR} and {@link #COLOR_MODE_MONOCHROME}.
10709ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     */
10809ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    public void setColorMode(int colorMode) {
10909ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov        mColorMode = colorMode;
11009ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    }
11109ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov
11209ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    /**
11309ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * Gets the color mode with which the image will be printed.
11409ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     *
11509ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * @return The color mode which is one of {@link #COLOR_MODE_COLOR}
11609ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     * and {@link #COLOR_MODE_MONOCHROME}.
11709ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov     */
11809ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    public int getColorMode() {
11909ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov        return mColorMode;
12009ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    }
12109ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov
12209ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov    /**
123b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Prints a bitmap.
124b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     *
125b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param jobName The print job name.
126b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param bitmap  The bitmap to print.
127b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
128b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    public void printBitmap(final String jobName, final Bitmap bitmap) {
129b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        if (bitmap == null) {
130b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            return;
131b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
132b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        final int fittingMode = mScaleMode; // grab the fitting mode at time of call
133b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
134b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        PrintAttributes.MediaSize mediaSize = PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
135b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        if (bitmap.getWidth() > bitmap.getHeight()) {
136b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            mediaSize = PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
137b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
13809ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov        PrintAttributes attr = new PrintAttributes.Builder()
13909ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov                .setMediaSize(mediaSize)
14009ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov                .setColorMode(mColorMode)
14109ad64345349f27bdeb53c536f178e46bb7ce5acSvetoslav Ganov                .build();
142b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
143b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        printManager.print(jobName,
144b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                new PrintDocumentAdapter() {
145b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    private PrintAttributes mAttributes;
146b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
147b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    @Override
148b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    public void onLayout(PrintAttributes oldPrintAttributes,
149b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                         PrintAttributes newPrintAttributes,
150b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                         CancellationSignal cancellationSignal,
151b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                         LayoutResultCallback layoutResultCallback,
152b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                         Bundle bundle) {
153b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
154b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        mAttributes = newPrintAttributes;
155b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
156b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        PrintDocumentInfo info = new PrintDocumentInfo.Builder(jobName)
157b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                .setContentType(PrintDocumentInfo.CONTENT_TYPE_PHOTO)
158b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                .setPageCount(1)
159b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                .build();
160b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        boolean changed = !newPrintAttributes.equals(oldPrintAttributes);
161b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        layoutResultCallback.onLayoutFinished(info, changed);
162b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    }
163b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
164b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    @Override
165b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    public void onWrite(PageRange[] pageRanges, ParcelFileDescriptor fileDescriptor,
166b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                        CancellationSignal cancellationSignal,
167b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                        WriteResultCallback writeResultCallback) {
168b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        PrintedPdfDocument pdfDocument = new PrintedPdfDocument(mContext,
169b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                mAttributes);
170b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        try {
171b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            Page page = pdfDocument.startPage(1);
172b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
173b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            RectF content = new RectF(page.getInfo().getContentRect());
174b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            Matrix matrix = new Matrix();
175b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
176b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            // Compute and apply scale to fill the page.
177b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            float scale = content.width() / bitmap.getWidth();
178b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            if (fittingMode == SCALE_MODE_FILL) {
179b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                scale = Math.max(scale, content.height() / bitmap.getHeight());
180b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            } else {
181b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                scale = Math.min(scale, content.height() / bitmap.getHeight());
182b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            }
183b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            matrix.postScale(scale, scale);
184b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
185b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            // Center the content.
186b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            final float translateX = (content.width()
187b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                    - bitmap.getWidth() * scale) / 2;
188b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            final float translateY = (content.height()
189b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                    - bitmap.getHeight() * scale) / 2;
190b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            matrix.postTranslate(translateX, translateY);
191b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
192b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            // Draw the bitmap.
193b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            page.getCanvas().drawBitmap(bitmap, matrix, null);
194b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
195b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            // Finish the page.
196b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            pdfDocument.finishPage(page);
197b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
198b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            try {
199b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                // Write the document.
200b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                pdfDocument.writeTo(new FileOutputStream(
201b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                        fileDescriptor.getFileDescriptor()));
202b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                // Done.
203b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                writeResultCallback.onWriteFinished(
204b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                        new PageRange[]{PageRange.ALL_PAGES});
205b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            } catch (IOException ioe) {
206b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                // Failed.
207b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                Log.e(LOG_TAG, "Error writing printed content", ioe);
208b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                writeResultCallback.onWriteFailed(null);
209b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            }
210b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        } finally {
211b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            if (pdfDocument != null) {
212b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                pdfDocument.close();
213b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            }
214b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            if (fileDescriptor != null) {
215b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                try {
216b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                    fileDescriptor.close();
217b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                } catch (IOException ioe) {
218b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                    /* ignore */
219b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                                }
220b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                            }
221b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                        }
222b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                    }
223b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav                }, attr);
224b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
225b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
226b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
227b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Prints an image located at the Uri. Image types supported are those of
228b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * <code>BitmapFactory.decodeStream</code> (JPEG, GIF, PNG, BMP, WEBP)
229b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     *
230b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param jobName   The print job name.
231b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param imageFile The <code>Uri</code> pointing to an image to print.
232b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @throws FileNotFoundException if <code>Uri</code> is not pointing to a valid image.
233b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
234b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    public void printBitmap(String jobName, Uri imageFile) throws FileNotFoundException {
235b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        Bitmap bitmap = loadConstrainedBitmap(imageFile, MAX_PRINT_SIZE);
236b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        printBitmap(jobName, bitmap);
237b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
238b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
239b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
240b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Loads a bitmap while limiting its size
241b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     *
242b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param uri           location of a valid image
243b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @param maxSideLength the maximum length of a size
244b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @return the Bitmap
245b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * @throws FileNotFoundException if the Uri does not point to an image
246b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
247b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    private Bitmap loadConstrainedBitmap(Uri uri, int maxSideLength) throws FileNotFoundException {
248b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        if (maxSideLength <= 0 || uri == null || mContext == null) {
249b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            throw new IllegalArgumentException("bad argument to getScaledBitmap");
250b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
251b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        // Get width and height of stored bitmap
252b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        BitmapFactory.Options opt = new BitmapFactory.Options();
253b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        opt.inJustDecodeBounds = true;
254b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        loadBitmap(uri, opt);
255b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
256b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        int w = opt.outWidth;
257b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        int h = opt.outHeight;
258b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
259b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        // If bitmap cannot be decoded, return null
260b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        if (w <= 0 || h <= 0) {
261b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            return null;
262b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
263b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
264b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        // Find best downsampling size
265b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        int imageSide = Math.max(w, h);
266b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
267b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        int sampleSize = 1;
268b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        while (imageSide > maxSideLength) {
269b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            imageSide >>>= 1;
270b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            sampleSize <<= 1;
271b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
272b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
273b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        // Make sure sample size is reasonable
274b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        if (sampleSize <= 0 || 0 >= (int) (Math.min(w, h) / sampleSize)) {
275b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            return null;
276b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
277b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        BitmapFactory.Options options = new BitmapFactory.Options();
278b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        options.inMutable = true;
279b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        options.inSampleSize = sampleSize;
280b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        return loadBitmap(uri, options);
281b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
282b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav
283b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    /**
284b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Returns the bitmap from the given uri loaded using the given options.
285b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     * Returns null on failure.
286b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav     */
287b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    private Bitmap loadBitmap(Uri uri, BitmapFactory.Options o) throws FileNotFoundException {
288b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        if (uri == null || mContext == null) {
289b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            throw new IllegalArgumentException("bad argument to loadBitmap");
290b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
291b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        InputStream is = null;
292b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        try {
293b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            is = mContext.getContentResolver().openInputStream(uri);
294b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            return BitmapFactory.decodeStream(is, null, o);
295b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        } finally {
296545f1bf8b47c12d2453143706a2fd76d9eef2368Svetoslav Ganov            if (is != null) {
297545f1bf8b47c12d2453143706a2fd76d9eef2368Svetoslav Ganov                try {
298545f1bf8b47c12d2453143706a2fd76d9eef2368Svetoslav Ganov                    is.close();
299545f1bf8b47c12d2453143706a2fd76d9eef2368Svetoslav Ganov                } catch (IOException t) {
300545f1bf8b47c12d2453143706a2fd76d9eef2368Svetoslav Ganov                    Log.w(LOG_TAG, "close fail ", t);
301545f1bf8b47c12d2453143706a2fd76d9eef2368Svetoslav Ganov                }
302b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav            }
303b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav        }
304b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav    }
305b363776d911abae9d067b9ef77fccc1c3c56e652Svetoslav}