Improvements and fixes for Android
This commit is contained in:
parent
fc2bbeca53
commit
884e5c4862
@ -101,7 +101,10 @@
|
||||
|
||||
<framework src="com.android.support:support-v4:$ANDROID_SUPPORT_V4_VERSION"/>
|
||||
|
||||
<source-file src="src/android/AssetUtil.java"
|
||||
<source-file src="src/android/PrintAdapter.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/PrintContent.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/Printer.java"
|
||||
@ -112,9 +115,6 @@
|
||||
|
||||
<source-file src="src/android/PrintOptions.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/PrintPdfAdapter.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
</platform>
|
||||
|
||||
<!-- windows -->
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
package de.appplant.cordova.plugin.printer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@ -28,6 +27,8 @@ import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.print.PrintDocumentInfo;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.print.PrintHelper;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@ -39,23 +40,30 @@ import static android.print.PrintDocumentInfo.CONTENT_TYPE_DOCUMENT;
|
||||
/**
|
||||
* Document adapter to render and print PDF files.
|
||||
*/
|
||||
class PrintPdfAdapter extends PrintDocumentAdapter {
|
||||
class PrintAdapter extends PrintDocumentAdapter {
|
||||
|
||||
// The application context
|
||||
private @NonNull Context context;
|
||||
// The name of the print job
|
||||
private final @NonNull String jobName;
|
||||
|
||||
// The path to the PDF file
|
||||
private @NonNull String path;
|
||||
// The input stream to render
|
||||
private final @NonNull InputStream input;
|
||||
|
||||
// The callback to inform once the job is done
|
||||
private final @Nullable PrintHelper.OnPrintFinishCallback callback;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The context where to look for.
|
||||
* @param jobName The name of the print job.
|
||||
* @param input The input stream to render.
|
||||
* @param callback The callback to inform once the job is done.
|
||||
*/
|
||||
PrintPdfAdapter (@NonNull String path, @NonNull Context context)
|
||||
PrintAdapter (@NonNull String jobName, @NonNull InputStream input,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
this.path = path;
|
||||
this.context = context;
|
||||
this.jobName = jobName;
|
||||
this.input = input;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,11 +78,13 @@ class PrintPdfAdapter extends PrintDocumentAdapter {
|
||||
if (cancellationSignal.isCanceled())
|
||||
return;
|
||||
|
||||
pdi = new PrintDocumentInfo.Builder("test")
|
||||
pdi = new PrintDocumentInfo.Builder(jobName)
|
||||
.setContentType(CONTENT_TYPE_DOCUMENT)
|
||||
.build();
|
||||
|
||||
callback.onLayoutFinished(pdi, true);
|
||||
boolean changed = !newAttributes.equals(oldAttributes);
|
||||
|
||||
callback.onLayoutFinished(pdi, changed);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,18 +96,10 @@ class PrintPdfAdapter extends PrintDocumentAdapter {
|
||||
if (cancellationSignal.isCanceled())
|
||||
return;
|
||||
|
||||
AssetUtil io = new AssetUtil(context);
|
||||
InputStream in = io.open(path);
|
||||
|
||||
if (in == null) {
|
||||
callback.onWriteFailed("File not found: " + path);
|
||||
return;
|
||||
}
|
||||
|
||||
OutputStream out = new FileOutputStream(dest.getFileDescriptor());
|
||||
OutputStream output = new FileOutputStream(dest.getFileDescriptor());
|
||||
|
||||
try {
|
||||
AssetUtil.copy(in, out);
|
||||
PrintContent.copy(input, output);
|
||||
} catch (IOException e) {
|
||||
callback.onWriteFailed(e.getMessage());
|
||||
return;
|
||||
@ -105,4 +107,19 @@ class PrintPdfAdapter extends PrintDocumentAdapter {
|
||||
|
||||
callback.onWriteFinished(new PageRange[]{ PageRange.ALL_PAGES });
|
||||
}
|
||||
|
||||
/**
|
||||
* Close input stream once the printing is done.
|
||||
*/
|
||||
@Override
|
||||
public void onFinish ()
|
||||
{
|
||||
super.onFinish();
|
||||
|
||||
PrintContent.close(input);
|
||||
|
||||
if (callback != null) {
|
||||
callback.onFinish();
|
||||
}
|
||||
}
|
||||
}
|
@ -29,16 +29,18 @@ import android.support.annotation.Nullable;
|
||||
import android.util.Base64;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLConnection;
|
||||
|
||||
class AssetUtil {
|
||||
class PrintContent {
|
||||
|
||||
// List of supported content types
|
||||
enum ContentType { PLAIN, HTML, IMAGE, PDF, SELF }
|
||||
enum ContentType { PLAIN, HTML, IMAGE, PDF, UNSUPPORTED }
|
||||
|
||||
// Application context
|
||||
private final @NonNull Context context;
|
||||
@ -48,8 +50,8 @@ class AssetUtil {
|
||||
*
|
||||
* @param ctx The application context.
|
||||
*/
|
||||
AssetUtil (@NonNull Context ctx) {
|
||||
this.context = ctx;
|
||||
private PrintContent (@NonNull Context ctx) {
|
||||
context = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,21 +61,60 @@ class AssetUtil {
|
||||
*
|
||||
* @return The content type even the file does not exist.
|
||||
*/
|
||||
static @NonNull ContentType getContentType (@Nullable String path)
|
||||
@NonNull
|
||||
static ContentType getContentType (@Nullable String path,
|
||||
@NonNull Context context)
|
||||
{
|
||||
return new PrintContent(context).getContentType(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content type for the file referenced by its uri.
|
||||
*
|
||||
* @param path The path to check.
|
||||
*
|
||||
* @return The content type even the file does not exist.
|
||||
*/
|
||||
@NonNull
|
||||
private ContentType getContentType (@Nullable String path)
|
||||
{
|
||||
ContentType type = ContentType.PLAIN;
|
||||
|
||||
if (path == null || path.isEmpty())
|
||||
{
|
||||
type = ContentType.SELF;
|
||||
}
|
||||
else if (path.charAt(0) == '<')
|
||||
if (path == null || path.isEmpty() || path.charAt(0) == '<')
|
||||
{
|
||||
type = ContentType.HTML;
|
||||
}
|
||||
else if (path.matches("^[a-z]+://.+"))
|
||||
else if (path.matches("^[a-z0-9]+://.+"))
|
||||
{
|
||||
return path.endsWith(".pdf") ? ContentType.PDF : ContentType.IMAGE;
|
||||
String mime;
|
||||
|
||||
if (path.startsWith("base64:")) {
|
||||
try {
|
||||
mime = URLConnection.guessContentTypeFromStream(openBase64(path));
|
||||
} catch (IOException e) {
|
||||
return ContentType.UNSUPPORTED;
|
||||
}
|
||||
} else {
|
||||
mime = URLConnection.guessContentTypeFromName(path);
|
||||
}
|
||||
|
||||
switch (mime)
|
||||
{
|
||||
case "image/bmp":
|
||||
case "image/png":
|
||||
case "image/jpeg":
|
||||
case "image/jpeg2000":
|
||||
case "image/jp2":
|
||||
case "image/gif":
|
||||
case "image/x-icon":
|
||||
case "image/vnd.microsoft.icon":
|
||||
case "image/heif":
|
||||
return ContentType.IMAGE;
|
||||
case "application/pdf":
|
||||
return ContentType.PDF;
|
||||
default:
|
||||
return ContentType.UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
@ -83,10 +124,25 @@ class AssetUtil {
|
||||
* Opens a file://, res:// or base64:// Uri as a stream.
|
||||
*
|
||||
* @param path The file path to decode.
|
||||
* @param context The application context.
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
@Nullable InputStream open (@NonNull String path)
|
||||
@Nullable
|
||||
static InputStream open (@NonNull String path, @NonNull Context context)
|
||||
{
|
||||
return new PrintContent(context).open(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a file://, res:// or base64:// Uri as a stream.
|
||||
*
|
||||
* @param path The file path to decode.
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
@Nullable
|
||||
private InputStream open (@NonNull String path)
|
||||
{
|
||||
InputStream stream = null;
|
||||
|
||||
@ -114,10 +170,25 @@ class AssetUtil {
|
||||
* Decodes a file://, res:// or base64:// Uri to bitmap.
|
||||
*
|
||||
* @param path The file path to decode.
|
||||
* @param context The application context.
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
@Nullable Bitmap decode (@NonNull String path)
|
||||
@Nullable
|
||||
static Bitmap decode (@NonNull String path, @NonNull Context context)
|
||||
{
|
||||
return new PrintContent(context).decode(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a file://, res:// or base64:// Uri to bitmap.
|
||||
*
|
||||
* @param path The file path to decode.
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
@Nullable
|
||||
private Bitmap decode (@NonNull String path)
|
||||
{
|
||||
Bitmap bitmap;
|
||||
|
||||
@ -150,7 +221,8 @@ class AssetUtil {
|
||||
* @param input The readable input stream.
|
||||
* @param output The writable output stream.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws IOException If the input stream is not readable,
|
||||
* or the output stream is not writable.
|
||||
*/
|
||||
static void copy (@NonNull InputStream input,
|
||||
@NonNull OutputStream output) throws IOException
|
||||
@ -162,8 +234,22 @@ class AssetUtil {
|
||||
output.write(buf, 0, bytesRead);
|
||||
}
|
||||
|
||||
output.close();
|
||||
input.close();
|
||||
input.reset();
|
||||
close(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the stream.
|
||||
*
|
||||
* @param stream The stream to close.
|
||||
*/
|
||||
static void close (@NonNull Closeable stream)
|
||||
{
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,7 +259,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @Nullable InputStream openFile (@NonNull String path)
|
||||
@Nullable
|
||||
private InputStream openFile (@NonNull String path)
|
||||
{
|
||||
String absPath = path.substring(7);
|
||||
|
||||
@ -191,7 +278,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeFile (@NonNull String path)
|
||||
@Nullable
|
||||
private Bitmap decodeFile (@NonNull String path)
|
||||
{
|
||||
String absPath = path.substring(7);
|
||||
|
||||
@ -205,7 +293,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @Nullable InputStream openAsset (@NonNull String path)
|
||||
@Nullable
|
||||
private InputStream openAsset (@NonNull String path)
|
||||
{
|
||||
String resPath = path.replaceFirst("file:/", "www");
|
||||
|
||||
@ -223,7 +312,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeAsset (@NonNull String path)
|
||||
@Nullable
|
||||
private Bitmap decodeAsset (@NonNull String path)
|
||||
{
|
||||
InputStream stream = openAsset(path);
|
||||
Bitmap bitmap;
|
||||
@ -233,11 +323,7 @@ class AssetUtil {
|
||||
|
||||
bitmap = BitmapFactory.decodeStream(stream);
|
||||
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
close(stream);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
@ -249,7 +335,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @NonNull InputStream openResource (@NonNull String path)
|
||||
@NonNull
|
||||
private InputStream openResource (@NonNull String path)
|
||||
{
|
||||
String resPath = path.substring(6);
|
||||
int resId = getResId(resPath);
|
||||
@ -264,7 +351,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeResource (@NonNull String path)
|
||||
@Nullable
|
||||
private Bitmap decodeResource (@NonNull String path)
|
||||
{
|
||||
String data = path.substring(9);
|
||||
byte[] bytes = Base64.decode(data, 0);
|
||||
@ -279,7 +367,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @NonNull InputStream openBase64 (@NonNull String path)
|
||||
@NonNull
|
||||
private InputStream openBase64 (@NonNull String path)
|
||||
{
|
||||
String data = path.substring(9);
|
||||
byte[] bytes = Base64.decode(data, 0);
|
||||
@ -294,7 +383,8 @@ class AssetUtil {
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeBase64 (@NonNull String path)
|
||||
@Nullable
|
||||
private Bitmap decodeBase64 (@NonNull String path)
|
||||
{
|
||||
String data = path.substring(9);
|
||||
byte[] bytes = Base64.decode(data, 0);
|
||||
@ -333,10 +423,16 @@ class AssetUtil {
|
||||
return resId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the asset manager for the app.
|
||||
*/
|
||||
private AssetManager getAssets() {
|
||||
return context.getAssets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource bundle for the app.
|
||||
*/
|
||||
private Resources getResources() {
|
||||
return context.getResources();
|
||||
}
|
@ -31,12 +31,15 @@ import android.support.v4.print.PrintHelper;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import static android.content.Context.PRINT_SERVICE;
|
||||
import static de.appplant.cordova.plugin.printer.PrintContent.ContentType.UNSUPPORTED;
|
||||
|
||||
class PrintManager {
|
||||
|
||||
// The application context
|
||||
private @NonNull Context context;
|
||||
private final @NonNull Context context;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -61,15 +64,16 @@ class PrintManager {
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
supported = new AssetUtil(context).open(item) != null;
|
||||
supported = PrintContent.getContentType(item, context) != UNSUPPORTED;
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of all printable document types (utis).
|
||||
* List of all printable document types (utis).
|
||||
*/
|
||||
@NonNull
|
||||
static JSONArray getPrintableUTIs()
|
||||
{
|
||||
JSONArray utis = new JSONArray();
|
||||
@ -82,8 +86,6 @@ class PrintManager {
|
||||
utis.put("public.heif");
|
||||
utis.put("com.compuserve.gif");
|
||||
utis.put("com.microsoft.ico");
|
||||
utis.put("com.microsoft.bmp");
|
||||
utis.put("com.microsoft.bmp");
|
||||
|
||||
return utis;
|
||||
}
|
||||
@ -96,19 +98,20 @@ class PrintManager {
|
||||
* @param settings Additional settings how to render the content.
|
||||
* @param callback The function to invoke once the job is done.
|
||||
*/
|
||||
void print (@Nullable String content, JSONObject settings,
|
||||
void print (@Nullable String content, @NonNull JSONObject settings,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
switch (AssetUtil.getContentType(content))
|
||||
switch (PrintContent.getContentType(content, context))
|
||||
{
|
||||
case IMAGE:
|
||||
//noinspection ConstantConditions
|
||||
printImage(content, settings, callback);
|
||||
break;
|
||||
case PDF:
|
||||
//noinspection ConstantConditions
|
||||
printPdf(content, settings, callback);
|
||||
break;
|
||||
case HTML:
|
||||
case SELF:
|
||||
case PLAIN:
|
||||
// TODO
|
||||
}
|
||||
@ -121,15 +124,19 @@ class PrintManager {
|
||||
* @param settings Additional settings how to render the content.
|
||||
* @param callback The function to invoke once the job is done.
|
||||
*/
|
||||
private void printPdf (String path, JSONObject settings,
|
||||
private void printPdf (@NonNull String path, @NonNull JSONObject settings,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
InputStream stream = PrintContent.open(path, context);
|
||||
|
||||
if (stream == null) return;
|
||||
|
||||
PrintOptions options = new PrintOptions(settings);
|
||||
String jobName = options.getJobName();
|
||||
PrintPdfAdapter adapter = new PrintPdfAdapter(path, context);
|
||||
PrintAttributes attributes = options.toPrintAttributes();
|
||||
PrintAdapter adapter = new PrintAdapter(jobName, stream, callback);
|
||||
PrintAttributes attrs = options.toPrintAttributes();
|
||||
|
||||
getPrintService().print(jobName, adapter, attributes);
|
||||
getPrintService().print(jobName, adapter, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,11 +146,10 @@ class PrintManager {
|
||||
* @param settings Additional settings how to render the content.
|
||||
* @param callback The function to invoke once the job is done.
|
||||
*/
|
||||
private void printImage (String path, JSONObject settings,
|
||||
private void printImage (@NonNull String path, @NonNull JSONObject settings,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
AssetUtil decoder = new AssetUtil(context);
|
||||
Bitmap bitmap = decoder.decode(path);
|
||||
Bitmap bitmap = PrintContent.decode(path, context);
|
||||
|
||||
if (bitmap == null) return;
|
||||
|
||||
@ -159,7 +165,8 @@ class PrintManager {
|
||||
/**
|
||||
* Returns the print service of the app.
|
||||
*/
|
||||
private @NonNull android.print.PrintManager getPrintService()
|
||||
@NonNull
|
||||
private android.print.PrintManager getPrintService()
|
||||
{
|
||||
return (android.print.PrintManager) context.getSystemService(PRINT_SERVICE);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ import static android.support.v4.print.PrintHelper.SCALE_MODE_FIT;
|
||||
class PrintOptions {
|
||||
|
||||
// The print job settings
|
||||
private @NonNull JSONObject spec;
|
||||
private final @NonNull JSONObject spec;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -140,9 +140,9 @@ class PrintOptions {
|
||||
break;
|
||||
}
|
||||
|
||||
if (spec.has("graystyle"))
|
||||
if (spec.has("monochrome"))
|
||||
{
|
||||
if (spec.optBoolean("graystyle"))
|
||||
if (spec.optBoolean("monochrome"))
|
||||
{
|
||||
printer.setColorMode(PrintHelper.COLOR_MODE_MONOCHROME);
|
||||
}
|
||||
@ -152,9 +152,7 @@ class PrintOptions {
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.has("autoFit"))
|
||||
{
|
||||
if (spec.optBoolean("autoFit"))
|
||||
if (spec.optBoolean("autoFit", true))
|
||||
{
|
||||
printer.setScaleMode(SCALE_MODE_FIT);
|
||||
}
|
||||
@ -164,4 +162,3 @@ class PrintOptions {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
info.orientation = UIPrintInfoOrientationLandscape;
|
||||
}
|
||||
|
||||
if ([spec[@"graystyle"] boolValue])
|
||||
if ([spec[@"monochrome"] boolValue])
|
||||
{
|
||||
if ([spec[@"photo"] boolValue])
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ exports.onPrintTaskRequested = function (event) {
|
||||
args.setSource(exports._page);
|
||||
});
|
||||
|
||||
if (config.graystyle) {
|
||||
if (config.monochrome) {
|
||||
task.options.colorMode = Printing.PrintColorMode.grayscale;
|
||||
} else {
|
||||
task.options.colorMode = Printing.PrintColorMode.color;
|
||||
|
@ -26,7 +26,7 @@ exports._defaults = {
|
||||
// name: 'unknown',
|
||||
// duplex: 'none',
|
||||
// landscape: false,
|
||||
// graystyle: false,
|
||||
// monochrome: false,
|
||||
// border: true,
|
||||
// copies: 1,
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user