/* * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2016 Mopria Alliance, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bips.ipp; import android.content.Context; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.ParcelFileDescriptor; import android.print.PrintAttributes; import android.print.PrintDocumentInfo; import android.print.PrintJobInfo; import android.printservice.PrintJob; import android.util.Log; import android.view.Gravity; import com.android.bips.jni.BackendConstants; import com.android.bips.jni.LocalJobParams; import com.android.bips.jni.LocalPrinterCapabilities; import com.android.bips.jni.MediaSizes; import com.android.bips.util.FileUtils; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * A background task that starts sending a print job. The result of this task is an integer * defined by {@link Backend} ERROR_* codes or a non-negative code for success. */ class StartJobTask extends AsyncTask { private static final String TAG = StartJobTask.class.getSimpleName(); private static final boolean DEBUG = false; private static final String MIME_TYPE_PDF = "application/pdf"; // see wprint_df_types.h for enum values private static final int MEDIA_TYPE_PLAIN = 0; private static final int MEDIA_TYPE_AUTO = 98; // Unused but present // private static final int MEDIA_TYPE_PHOTO = 1; // private static final int MEDIA_TYPE_PHOTO_GLOSSY = 2; private static final int SIDES_SIMPLEX = 0; private static final int SIDES_DUPLEX_LONG_EDGE = 1; private static final int SIDES_DUPLEX_SHORT_EDGE = 2; private static final int RESOLUTION_300_DPI = 300; private static final int COLOR_SPACE_MONOCHROME = 0; private static final int COLOR_SPACE_COLOR = 1; private static final int BORDERLESS_OFF = 0; private static final int BORDERLESS_ON = 1; private final Context mContext; private final Backend mBackend; private final Uri mDestination; private final LocalPrinterCapabilities mCapabilities; private final LocalJobParams mJobParams; private final ParcelFileDescriptor mSourceFileDescriptor; private final String mJobId; private final PrintJobInfo mJobInfo; private final PrintDocumentInfo mDocInfo; private final MediaSizes mMediaSizes; StartJobTask(Context context, Backend backend, Uri destination, PrintJob printJob, LocalPrinterCapabilities capabilities) { mContext = context; mBackend = backend; mDestination = destination; mCapabilities = capabilities; mJobParams = new LocalJobParams(); mJobId = printJob.getId().toString(); mJobInfo = printJob.getInfo(); mDocInfo = printJob.getDocument().getInfo(); mSourceFileDescriptor = printJob.getDocument().getData(); mMediaSizes = MediaSizes.getInstance(mContext); } private void populateJobParams() { PrintAttributes.MediaSize mediaSize = mJobInfo.getAttributes().getMediaSize(); mJobParams.borderless = isBorderless() ? BORDERLESS_ON : BORDERLESS_OFF; mJobParams.duplex = getSides(); mJobParams.num_copies = mJobInfo.getCopies(); mJobParams.pdf_render_resolution = RESOLUTION_300_DPI; mJobParams.fit_to_page = !getFillPage(); mJobParams.fill_page = getFillPage(); mJobParams.job_name = mJobInfo.getLabel(); mJobParams.job_originating_user_name = Build.MODEL; mJobParams.auto_rotate = false; mJobParams.portrait_mode = mediaSize == null || mediaSize.isPortrait(); mJobParams.landscape_mode = !mJobParams.portrait_mode; mJobParams.media_size = mMediaSizes.toMediaCode(mediaSize); mJobParams.media_type = getMediaType(); mJobParams.color_space = getColorSpace(); mJobParams.document_category = getDocumentCategory(); mJobParams.job_margin_top = Math.max(mJobParams.job_margin_top, 0.0f); mJobParams.job_margin_left = Math.max(mJobParams.job_margin_left, 0.0f); mJobParams.job_margin_right = Math.max(mJobParams.job_margin_right, 0.0f); mJobParams.job_margin_bottom = Math.max(mJobParams.job_margin_bottom, 0.0f); mJobParams.alignment = Gravity.CENTER; } @Override protected Integer doInBackground(Void... voids) { if (DEBUG) Log.d(TAG, "doInBackground() job=" + mJobParams + ", cap=" + mCapabilities); File tempFolder = new File(mContext.getFilesDir(), Backend.TEMP_JOB_FOLDER); if (!FileUtils.makeDirectory(tempFolder)) { Log.w(TAG, "makeDirectory failure"); return Backend.ERROR_FILE; } File pdfFile = new File(tempFolder, mJobId + ".pdf"); try { try { FileUtils.copy(new ParcelFileDescriptor.AutoCloseInputStream(mSourceFileDescriptor), new BufferedOutputStream(new FileOutputStream(pdfFile))); } catch (IOException e) { Log.w(TAG, "Error while copying to " + pdfFile, e); return Backend.ERROR_FILE; } String[] files = new String[]{pdfFile.toString()}; // Address, without port. String address = mDestination.getHost() + mDestination.getPath(); if (isCancelled()) { return Backend.ERROR_CANCEL; } // Get default job parameters int result = mBackend.nativeGetDefaultJobParameters(mJobParams); if (result != 0) { if (DEBUG) Log.w(TAG, "nativeGetDefaultJobParameters failure: " + result); return Backend.ERROR_UNKNOWN; } if (isCancelled()) { return Backend.ERROR_CANCEL; } // Fill in job parameters from capabilities and print job info. populateJobParams(); // Finalize job parameters mBackend.nativeGetFinalJobParameters(mJobParams, mCapabilities); if (isCancelled()) { return Backend.ERROR_CANCEL; } if (DEBUG) { Log.d(TAG, "nativeStartJob address=" + address + " port=" + mDestination.getPort() + " mime=" + MIME_TYPE_PDF + " files=" + files[0] + " job=" + mJobParams); } // Initiate job result = mBackend.nativeStartJob(Backend.getIp(address), mDestination.getPort(), MIME_TYPE_PDF, mJobParams, mCapabilities, files, null, mDestination.getScheme()); if (result < 0) { Log.w(TAG, "nativeStartJob failure: " + result); return Backend.ERROR_UNKNOWN; } pdfFile = null; return result; } finally { if (pdfFile != null) { pdfFile.delete(); } } } private boolean isBorderless() { return mCapabilities.borderless && mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO; } private int getSides() { // Never duplex photo media; may damage printers if (mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO) { return SIDES_SIMPLEX; } switch (mJobInfo.getAttributes().getDuplexMode()) { case PrintAttributes.DUPLEX_MODE_LONG_EDGE: return SIDES_DUPLEX_LONG_EDGE; case PrintAttributes.DUPLEX_MODE_SHORT_EDGE: return SIDES_DUPLEX_SHORT_EDGE; case PrintAttributes.DUPLEX_MODE_NONE: default: return SIDES_SIMPLEX; } } private boolean getFillPage() { switch (mDocInfo.getContentType()) { case PrintDocumentInfo.CONTENT_TYPE_PHOTO: return true; case PrintDocumentInfo.CONTENT_TYPE_UNKNOWN: case PrintDocumentInfo.CONTENT_TYPE_DOCUMENT: default: return false; } } private int getMediaType() { int mediaType = MEDIA_TYPE_PLAIN; for (int supportedType : mCapabilities.supportedMediaTypes) { if (supportedType == MEDIA_TYPE_AUTO) { // if auto media is supported, use that and break out of the loop mediaType = MEDIA_TYPE_AUTO; break; } else if (mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO && supportedType > mediaType) { // Select the best (highest #) supported type for photos mediaType = supportedType; } } return mediaType; } private int getColorSpace() { switch (mJobInfo.getAttributes().getColorMode()) { case PrintAttributes.COLOR_MODE_COLOR: return COLOR_SPACE_COLOR; case PrintAttributes.COLOR_MODE_MONOCHROME: default: return COLOR_SPACE_MONOCHROME; } } private String getDocumentCategory() { switch (mDocInfo.getContentType()) { case PrintDocumentInfo.CONTENT_TYPE_PHOTO: return BackendConstants.PRINT_DOCUMENT_CATEGORY__PHOTO; case PrintDocumentInfo.CONTENT_TYPE_DOCUMENT: default: return BackendConstants.PRINT_DOCUMENT_CATEGORY__DOCUMENT; } } }