Home | History | Annotate | Download | only in ipp
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  * Copyright (C) 2016 Mopria Alliance, Inc.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.bips.ipp;
     19 
     20 import android.content.Context;
     21 import android.net.Uri;
     22 import android.os.AsyncTask;
     23 import android.os.Build;
     24 import android.os.ParcelFileDescriptor;
     25 import android.print.PrintAttributes;
     26 import android.print.PrintDocumentInfo;
     27 import android.print.PrintJobInfo;
     28 import android.printservice.PrintJob;
     29 import android.util.Log;
     30 import android.view.Gravity;
     31 
     32 import com.android.bips.jni.BackendConstants;
     33 import com.android.bips.jni.LocalJobParams;
     34 import com.android.bips.jni.LocalPrinterCapabilities;
     35 import com.android.bips.jni.MediaSizes;
     36 import com.android.bips.util.FileUtils;
     37 
     38 import java.io.BufferedOutputStream;
     39 import java.io.File;
     40 import java.io.FileOutputStream;
     41 import java.io.IOException;
     42 
     43 /**
     44  * A background task that starts sending a print job. The result of this task is an integer
     45  * defined by {@link Backend} ERROR_* codes or a non-negative code for success.
     46  */
     47 class StartJobTask extends AsyncTask<Void, Void, Integer> {
     48     private static final String TAG = StartJobTask.class.getSimpleName();
     49     private static final boolean DEBUG = false;
     50 
     51     private static final String MIME_TYPE_PDF = "application/pdf";
     52 
     53     private static final int MEDIA_TYPE_PLAIN = 0;
     54     // Unused but present
     55     //    private static final int MEDIA_TYPE_PHOTO = 1;
     56     //    private static final int MEDIA_TYPE_PHOTO_GLOSSY = 2;
     57 
     58     private static final int SIDES_SIMPLEX = 0;
     59     private static final int SIDES_DUPLEX_LONG_EDGE = 1;
     60     private static final int SIDES_DUPLEX_SHORT_EDGE = 2;
     61 
     62     private static final int RESOLUTION_300_DPI = 300;
     63 
     64     private static final int COLOR_SPACE_MONOCHROME = 0;
     65     private static final int COLOR_SPACE_COLOR = 1;
     66 
     67     private static final int BORDERLESS_OFF = 0;
     68     private static final int BORDERLESS_ON = 1;
     69 
     70     private final Context mContext;
     71     private final Backend mBackend;
     72     private final Uri mDestination;
     73     private final LocalPrinterCapabilities mCapabilities;
     74     private final LocalJobParams mJobParams;
     75     private final ParcelFileDescriptor mSourceFileDescriptor;
     76     private final String mJobId;
     77     private final PrintJobInfo mJobInfo;
     78     private final PrintDocumentInfo mDocInfo;
     79     private final MediaSizes mMediaSizes;
     80 
     81     public StartJobTask(Context context, Backend backend, Uri destination, PrintJob printJob,
     82             LocalPrinterCapabilities capabilities) {
     83         mContext = context;
     84         mBackend = backend;
     85         mDestination = destination;
     86         mCapabilities = capabilities;
     87         mJobParams = new LocalJobParams();
     88         mJobId = printJob.getId().toString();
     89         mJobInfo = printJob.getInfo();
     90         mDocInfo = printJob.getDocument().getInfo();
     91         mSourceFileDescriptor = printJob.getDocument().getData();
     92         mMediaSizes = MediaSizes.getInstance(mContext);
     93     }
     94 
     95     private void populateJobParams() {
     96         PrintAttributes.MediaSize mediaSize = mJobInfo.getAttributes().getMediaSize();
     97 
     98         mJobParams.borderless = isBorderless() ? BORDERLESS_ON : BORDERLESS_OFF;
     99         mJobParams.duplex = getSides();
    100         mJobParams.num_copies = mJobInfo.getCopies();
    101         mJobParams.pdf_render_resolution = RESOLUTION_300_DPI;
    102         mJobParams.fit_to_page = !getFillPage();
    103         mJobParams.fill_page = getFillPage();
    104         mJobParams.job_name = mJobInfo.getLabel();
    105         mJobParams.job_originating_user_name = Build.MODEL;
    106         mJobParams.auto_rotate = false;
    107         mJobParams.portrait_mode = mediaSize == null || mediaSize.isPortrait();
    108         mJobParams.landscape_mode = !mJobParams.portrait_mode;
    109         mJobParams.media_size = mMediaSizes.toMediaCode(mediaSize);
    110         mJobParams.media_type = getMediaType();
    111         mJobParams.color_space = getColorSpace();
    112         mJobParams.document_category = getDocumentCategory();
    113 
    114         mJobParams.job_margin_top = Math.max(mJobParams.job_margin_top, 0.0f);
    115         mJobParams.job_margin_left = Math.max(mJobParams.job_margin_left, 0.0f);
    116         mJobParams.job_margin_right = Math.max(mJobParams.job_margin_right, 0.0f);
    117         mJobParams.job_margin_bottom = Math.max(mJobParams.job_margin_bottom, 0.0f);
    118 
    119         mJobParams.alignment = Gravity.CENTER;
    120     }
    121 
    122     @Override
    123     protected Integer doInBackground(Void... voids) {
    124         if (DEBUG) Log.d(TAG, "doInBackground() job=" + mJobParams + ", cap=" + mCapabilities);
    125         File tempFolder = new File(mContext.getFilesDir(), Backend.TEMP_JOB_FOLDER);
    126         if (!FileUtils.makeDirectory(tempFolder)) {
    127             Log.w(TAG, "makeDirectory failure");
    128             return Backend.ERROR_FILE;
    129         }
    130 
    131         File pdfFile = new File(tempFolder, mJobId + ".pdf");
    132         try {
    133             try {
    134                 FileUtils.copy(new ParcelFileDescriptor.AutoCloseInputStream(mSourceFileDescriptor),
    135                         new BufferedOutputStream(new FileOutputStream(pdfFile)));
    136             } catch (IOException e) {
    137                 Log.w(TAG, "Error while copying to " + pdfFile, e);
    138                 return Backend.ERROR_FILE;
    139             }
    140             String files[] = new String[]{pdfFile.toString()};
    141 
    142             // Address, without port.
    143             String address = mDestination.getHost() + mDestination.getPath();
    144 
    145             if (isCancelled()) return Backend.ERROR_CANCEL;
    146 
    147             // Get default job parameters
    148             int result = mBackend.nativeGetDefaultJobParameters(mJobParams);
    149             if (result != 0) {
    150                 if (DEBUG) Log.w(TAG, "nativeGetDefaultJobParameters failure: " + result);
    151                 return Backend.ERROR_UNKNOWN;
    152             }
    153 
    154             if (isCancelled()) return Backend.ERROR_CANCEL;
    155 
    156             // Fill in job parameters from capabilities and print job info.
    157             populateJobParams();
    158 
    159             // Finalize job parameters
    160             mBackend.nativeGetFinalJobParameters(mJobParams, mCapabilities);
    161 
    162             if (isCancelled()) return Backend.ERROR_CANCEL;
    163             if (DEBUG) {
    164                 Log.d(TAG, "nativeStartJob address=" + address +
    165                         " port=" + mDestination.getPort() + " mime=" + MIME_TYPE_PDF +
    166                         " files=" + files[0] + " job=" + mJobParams);
    167             }
    168             // Initiate job
    169             result = mBackend.nativeStartJob(Backend.getIp(address), mDestination.getPort(),
    170                     MIME_TYPE_PDF, mJobParams, mCapabilities, files, null, mDestination.getScheme());
    171             if (result < 0) {
    172                 Log.w(TAG, "nativeStartJob failure: " + result);
    173                 return Backend.ERROR_UNKNOWN;
    174             }
    175 
    176             pdfFile = null;
    177             return result;
    178         } finally {
    179             if (pdfFile != null) {
    180                 pdfFile.delete();
    181             }
    182         }
    183     }
    184 
    185     private boolean isBorderless() {
    186         return mCapabilities.borderless &&
    187                 mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO;
    188     }
    189 
    190     private int getSides() {
    191         // Never duplex photo media; may damage printers
    192         if (mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO) {
    193             return SIDES_SIMPLEX;
    194         }
    195 
    196         switch (mJobInfo.getAttributes().getDuplexMode()) {
    197             case PrintAttributes.DUPLEX_MODE_LONG_EDGE:
    198                 return SIDES_DUPLEX_LONG_EDGE;
    199             case PrintAttributes.DUPLEX_MODE_SHORT_EDGE:
    200                 return SIDES_DUPLEX_SHORT_EDGE;
    201             case PrintAttributes.DUPLEX_MODE_NONE:
    202             default:
    203                 return SIDES_SIMPLEX;
    204         }
    205     }
    206 
    207     private boolean getFillPage() {
    208         switch (mDocInfo.getContentType()) {
    209             case PrintDocumentInfo.CONTENT_TYPE_PHOTO:
    210                 return true;
    211             case PrintDocumentInfo.CONTENT_TYPE_UNKNOWN:
    212             case PrintDocumentInfo.CONTENT_TYPE_DOCUMENT:
    213             default:
    214                 return false;
    215         }
    216     }
    217 
    218     private int getMediaType() {
    219         int mediaType = MEDIA_TYPE_PLAIN;
    220 
    221         if (mDocInfo.getContentType() == PrintDocumentInfo.CONTENT_TYPE_PHOTO) {
    222             // Select the best (highest #) supported type for photos
    223             for (int supportedType : mCapabilities.supportedMediaTypes) {
    224                 if (supportedType > mediaType) {
    225                     mediaType = supportedType;
    226                 }
    227             }
    228         }
    229         return mediaType;
    230     }
    231 
    232     private int getColorSpace() {
    233         switch (mJobInfo.getAttributes().getColorMode()) {
    234             case PrintAttributes.COLOR_MODE_COLOR:
    235                 return COLOR_SPACE_COLOR;
    236             case PrintAttributes.COLOR_MODE_MONOCHROME:
    237             default:
    238                 return COLOR_SPACE_MONOCHROME;
    239         }
    240     }
    241 
    242     private String getDocumentCategory() {
    243         switch (mDocInfo.getContentType()) {
    244             case PrintDocumentInfo.CONTENT_TYPE_PHOTO:
    245                 return BackendConstants.PRINT_DOCUMENT_CATEGORY__PHOTO;
    246 
    247             case PrintDocumentInfo.CONTENT_TYPE_DOCUMENT:
    248             default:
    249                 return BackendConstants.PRINT_DOCUMENT_CATEGORY__DOCUMENT;
    250         }
    251     }
    252 }