| /* |
| * 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<Void, Void, Integer> { |
| 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; |
| } |
| } |
| } |