278 lines
11 KiB
Java
Executable File
278 lines
11 KiB
Java
Executable File
/*
|
|
* 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.graphics.pdf.PdfRenderer;
|
|
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();
|
|
try (PdfRenderer renderer = new PdfRenderer(
|
|
ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY));
|
|
PdfRenderer.Page page = renderer.openPage(0)) {
|
|
if (mJobParams.portrait_mode) {
|
|
mJobParams.source_height = (float) page.getHeight() / 72;
|
|
mJobParams.source_width = (float) page.getWidth() / 72;
|
|
} else {
|
|
mJobParams.source_width = (float) page.getHeight() / 72;
|
|
mJobParams.source_height = (float) page.getWidth() / 72;
|
|
}
|
|
} catch (IOException e) {
|
|
Log.w(TAG, "Error while getting source width, height", e);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|