Merge branch 'kitkat'

Conflicts:
	CHANGELOG.md
	README.md
	plugin.xml
	src/android/Printer.java
	src/ios/APPPrinter.m
	www/printer.js
This commit is contained in:
Sebastián Katzer
2014-09-12 11:45:58 +02:00
8 changed files with 463 additions and 517 deletions

View File

@@ -17,85 +17,67 @@
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
*/
package de.appplant.cordova.plugin.printer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Picture;
import android.net.Uri;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.content.Context;
import android.os.Build;
import android.os.Looper;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintJob;
import android.print.PrintManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
@TargetApi(19)
public class Printer extends CordovaPlugin {
private CallbackContext ctx;
private WebView view;
private CallbackContext command;
private static final String DEFAULT_DOC_NAME = "unknown";
/**
* Auflistung von App-IDs, welche den Content ausdrucken können
* Executes the request.
*
* This method is called from the WebView thread.
* To do a non-trivial amount of work, use:
* cordova.getThreadPool().execute(runnable);
*
* To run on the UI thread, use:
* cordova.getActivity().runOnUiThread(runnable);
*
* @param action The action to execute.
* @param rawArgs The exec() arguments in JSON form.
* @param callbackContext The callback context used when calling back into JavaScript.
* @return Whether the action was valid.
*/
private String printAppIds[] = {
"kr.co.iconlab.BasicPrintingProfile", // Bluetooth Smart Printing
"com.blueslib.android.app", // Bluetooth SPP Printer API
"com.brother.mfc.brprint", // Brother iPrint&Scan
"com.brother.ptouch.sdk", // Brother Print Library
"jp.co.canon.bsd.android.aepp.activity", // Canon Easy-PhotoPrint
"com.pauloslf.cloudprint", // Cloud Print
"com.dlnapr1.printer", // CMC DLNA Print Client
"com.dell.mobileprint", // Dell Mobile Print
"com.printjinni.app.print", // PrintJinni
"epson.print", // Epson iPrint
"jp.co.fujixerox.prt.PrintUtil.PCL", // Fuji Xerox Print Utility
"jp.co.fujixerox.prt.PrintUtil.Karin", // Fuji Xeros Print&Scan (S)
"com.hp.android.print", // HP ePrint
"com.blackspruce.lpd", // Let's Print Droid
"com.threebirds.notesprint", // NotesPrint print your notes
"com.xerox.mobileprint", // Print Portal (Xerox)
"com.zebra.kdu", // Print Station (Zebra)
"net.jsecurity.printbot", // PrintBot
"com.dynamixsoftware.printhand", // PrintHand Mobile Print
"com.dynamixsoftware.printhand.premium", // PrintHand Mobile Print Premium
"com.sec.print.mobileprint", // Samsung Mobile Print
"com.rcreations.send2printer", // Send 2 Printer
"com.ivc.starprint", // StarPrint
"com.threebirds.easyviewer", // WiFi Print
"com.woosim.android.print", // Woosim BT printer
"com.woosim.bt.app", // WoosimPrinter
"com.zebra.android.zebrautilities", // Zebra Utilities
};
@Override
public boolean execute (String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
// Etwas soll ausgedruckt werden
if ("print".equals(action)) {
print(args, callbackContext);
public boolean execute (String action, JSONArray args,
CallbackContext callbackContext) throws JSONException {
command = callbackContext;
if (action.equalsIgnoreCase("isAvailable")) {
isAvailable();
return true;
}
// Es soll überprüft werden, ob ein Dienst zum Ausdrucken von Inhalten zur Verfügung steht
if ("isServiceAvailable".equals(action)) {
isServiceAvailable(callbackContext);
if (action.equalsIgnoreCase("print")) {
print(args);
return true;
}
@@ -105,246 +87,140 @@ public class Printer extends CordovaPlugin {
}
/**
* Überprüft, ob ein Drucker zur Verfügung steht.
* Informs if the device is able to print documents.
* A Internet connection is required to load the cloud print dialog.
*/
private void isServiceAvailable (CallbackContext ctx) {
JSONArray appIds = this.getInstalledAppIds();
Boolean available = appIds.length() > 0;
JSONArray args = new JSONArray();
PluginResult result;
private void isAvailable () {
Boolean supported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
PluginResult result = new PluginResult(PluginResult.Status.OK, supported);
args.put(available);
args.put(appIds);
result = new PluginResult(PluginResult.Status.OK, args);
ctx.sendPluginResult(result);
command.sendPluginResult(result);
}
/**
* Druckt den HTML Content aus.
* Loads the HTML content into the web view and invokes the print manager.
*
* @param args
* The exec arguments as JSON
*/
private void print (final JSONArray args, CallbackContext ctx) {
final Printer self = this;
this.ctx = ctx;
private void print (final JSONArray args) {
final String content = args.optString(0, "<html></html>");
final JSONObject props = args.optJSONObject(1);;
cordova.getActivity().runOnUiThread( new Runnable() {
public void run() {
JSONObject platformConfig = args.optJSONObject(1);
String appId = self.getPrintAppId(platformConfig);
if (appId == null) {
self.ctx.success(4);
return;
};
String content = args.optString(0, "<html></html>");
Intent controller = self.getPrintController(appId);
self.adjustSettingsForPrintController(controller);
self.loadContentIntoPrintController(content, controller);
self.startPrinterApp(controller);
}
});
}
/**
* Gibt die zu verwendende App-ID an.
*/
private String getPrintAppId (JSONObject platformConfig) {
String appId = platformConfig.optString("appId", null);
if (appId != null) {
return (this.isAppInstalled(appId)) ? appId : null;
} else {
return this.getFirstInstalledAppId();
}
}
/**
* Erstellt den Print-View.
*/
private Intent getPrintController (String appId) {
String intentId = "android.intent.action.SEND";
if (appId.equals("com.rcreations.send2printer")) {
intentId = "com.rcreations.send2printer.print";
} else if (appId.equals("com.dynamixsoftware.printershare")) {
intentId = "android.intent.action.VIEW";
} else if (appId.equals("com.hp.android.print")) {
intentId = "org.androidprinting.intent.action.PRINT";
}
Intent intent = new Intent(intentId);
if (appId != null)
intent.setPackage(appId);
return intent;
}
/**
* Stellt die Eigenschaften des Druckers ein.
*/
private void adjustSettingsForPrintController (Intent intent) {
String mimeType = "image/png";
String appId = intent.getPackage();
// Check for special cases that can receive HTML
if (appId.equals("com.rcreations.send2printer") || appId.equals("com.dynamixsoftware.printershare")) {
mimeType = "text/html";
}
intent.setType(mimeType);
}
/**
* Lädt den zu druckenden Content in ein WebView, welcher vom Drucker ausgedruckt werden soll.
*/
private void loadContentIntoPrintController (String content, Intent intent) {
String mimeType = intent.getType();
if (mimeType.equals("text/html")) {
loadContentAsHtmlIntoPrintController(content, intent);
} else {
loadContentAsBitmapIntoPrintController(content, intent);
}
}
/**
* Lädt den zu druckenden Content als HTML in ein WebView, welcher vom Drucker ausgedruckt werden soll.
*/
private void loadContentAsHtmlIntoPrintController (String content, Intent intent) {
intent.putExtra(Intent.EXTRA_TEXT, content);
}
/**
* Lädt den zu druckenden Content als BMP in ein WebView, welcher vom Drucker ausgedruckt werden soll.
*/
private void loadContentAsBitmapIntoPrintController (String content, final Intent intent) {
Activity ctx = cordova.getActivity();
final WebView page = new WebView(ctx);
final Printer self = this;
page.setVisibility(View.INVISIBLE);
page.getSettings().setJavaScriptEnabled(false);
page.getSettings().setDatabaseEnabled(true);
page.setWebViewClient( new WebViewClient() {
@Override
public void onPageFinished(final WebView page, String url) {
new Handler().postDelayed( new Runnable() {
@Override
public void run() {
Bitmap screenshot = self.takeScreenshot(page);
File tmpFile = self.saveScreenshotToTmpFile(screenshot);
ViewGroup vg = (ViewGroup)(page.getParent());
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tmpFile));
vg.removeView(page);
}
}, 1000);
public void run() {
initWebView(content, props);
loadContent(content);
}
});
//Set base URI to the assets/www folder
String baseURL = webView.getUrl();
baseURL = baseURL.substring(0, baseURL.lastIndexOf('/') + 1);
ctx.addContentView(page, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
page.loadDataWithBaseURL(baseURL, content, "text/html", "UTF-8", null);
}
/**
* Nimmt einen Screenshot der Seite auf.
* Loads the content into the web view.
*
* @param content
* Either an HTML string or URI
*/
private Bitmap takeScreenshot (WebView page) {
Picture picture = page.capturePicture();
Bitmap bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
private void loadContent(String content) {
if (content.startsWith("http") || content.startsWith("file:")) {
view.loadUrl(content);
} else {
//Set base URI to the assets/www folder
String baseURL = webView.getUrl();
baseURL = baseURL.substring(0, baseURL.lastIndexOf('/') + 1);
picture.draw(canvas);
return bitmap;
}
/**
* Speichert den Screenshot der Seite in einer tmp. Datei ab.
*/
private File saveScreenshotToTmpFile (Bitmap screenshot) {
try {
File tmpFile = File.createTempFile("screenshot", ".tmp");
FileOutputStream stream = new FileOutputStream(tmpFile);
screenshot.compress(Bitmap.CompressFormat.PNG, 100, stream);
stream.close();
return tmpFile;
} catch (IOException e) {
e.printStackTrace();
view.loadDataWithBaseURL(baseURL, content, "text/html", "UTF-8", null);
}
return null;
}
/**
* Öffnet die Printer App, damit der Content ausgedruckt werden kann.
* Configures the WebView components which will call the Google Cloud Print
* Service.
*
* @param content
* HTML encoded string
* @param props
* The JSON object with the containing page properties
*/
private void startPrinterApp (Intent intent) {
cordova.startActivityForResult(this, intent, 0);
private void initWebView (String content, JSONObject props) {
Activity ctx = cordova.getActivity();
view = new WebView(ctx);
view.getSettings().setDatabaseEnabled(true);
setWebViewClient(content, props);
}
/**
* Findet heraus, ob die Anwendung installiert ist.
* Creates the web view client which sets the print document.
*
* @param content
* HTML encoded string
* @param props
* The JSON object with the containing page properties
*/
private boolean isAppInstalled (String appId) {
PackageManager pm = cordova.getActivity().getPackageManager();
private void setWebViewClient (final String content, JSONObject props) {
final String docName = props.optString("name", DEFAULT_DOC_NAME);
final boolean landscape = props.optBoolean("landscape", false);
final boolean graystyle = props.optBoolean("graystyle", false);
try {
PackageInfo pi = pm.getPackageInfo(appId, 0);
if (pi != null){
return true;
view.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading (WebView view, String url) {
return false;
}
} catch (PackageManager.NameNotFoundException e) {}
return false;
@Override
public void onPageFinished (WebView webView, String url) {
// Get a PrintManager instance
PrintManager printManager = (PrintManager) cordova.getActivity()
.getSystemService(Context.PRINT_SERVICE);
// Get a print adapter instance
PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter();
// Get a print builder instance
PrintAttributes.Builder builder = new PrintAttributes.Builder();
// The page does itself set its own margins
builder.setMinMargins(PrintAttributes.Margins.NO_MARGINS);
builder.setColorMode(graystyle ? PrintAttributes.COLOR_MODE_MONOCHROME
: PrintAttributes.COLOR_MODE_COLOR);
builder.setMediaSize(landscape ? PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE
: PrintAttributes.MediaSize.UNKNOWN_PORTRAIT);
// Create a print job with name and adapter instance
PrintJob job = printManager.print(docName, printAdapter, builder.build());
invokeCallbackOnceCompletedOrCanceled(job);
view = null;
}
});
}
/**
* Die IDs aller verfügbaren Drucker-Apps.
* Invokes the callback once the print job is complete or was canceled.
*
* @param job
* The reference to the print job
*/
private JSONArray getInstalledAppIds () {
JSONArray appIds = new JSONArray();
private void invokeCallbackOnceCompletedOrCanceled (final PrintJob job) {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
Looper.prepare();
for (int i = 0; i < printAppIds.length; i++) {
String appId = printAppIds[i];
Boolean isInstalled = this.isAppInstalled(appId);
if (isInstalled){
appIds.put(appId);
for (;;) {
if (job.isCancelled() || job.isCompleted() || job.isFailed()) {
command.success();
break;
}
}
}
}
return appIds;
}
/**
* Die erste ID in der Liste, deren App installiert ist.
*/
private String getFirstInstalledAppId () {
for (int i = 0; i < printAppIds.length; i++) {
String appId = printAppIds[i];
Boolean isInstalled = this.isAppInstalled(appId);
if (isInstalled){
return appId;
}
}
return null;
});
}
}

View File

@@ -28,6 +28,6 @@
// Prints the content
- (void) print:(CDVInvokedUrlCommand*)command;
// Find out whether printing is supported on this platform
- (void) isServiceAvailable:(CDVInvokedUrlCommand*)command;
- (void) isAvailable:(CDVInvokedUrlCommand*)command;
@end

View File

@@ -21,21 +21,13 @@
#import "APPPrinter.h"
@interface APPPrinter (Private)
@interface APPPrinter ()
// Retrieves an instance of shared print controller
- (UIPrintInteractionController*) printController;
// Adjusts the settings for the print controller
- (UIPrintInteractionController*) adjustSettingsForPrintController:(UIPrintInteractionController*)controller;
// Loads the content into the print controller
- (void) loadContent:(NSString*)content intoPrintController:(UIPrintInteractionController*)controller;
// Opens the print controller so that the user can choose between available iPrinters
- (void) informAboutResult:(int)code callbackId:(NSString*)callbackId;
// Checks either the printing service is avaible or not
- (BOOL) isPrintingAvailable;
@property (retain) NSString* callbackId;
@end
@implementation APPPrinter
/*
@@ -44,16 +36,18 @@
* @param {Function} callback
* A callback function to be called with the result
*/
- (void) isServiceAvailable:(CDVInvokedUrlCommand*)command
- (void) isAvailable:(CDVInvokedUrlCommand*)command
{
CDVPluginResult* pluginResult;
BOOL isAvailable = [self isPrintingAvailable];
[self.commandDelegate runInBackground:^{
CDVPluginResult* pluginResult;
BOOL isAvailable = [self isPrintingAvailable];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsBool:isAvailable];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsBool:isAvailable];
[self.commandDelegate sendPluginResult:pluginResult
callbackId:command.callbackId];
[self.commandDelegate sendPluginResult:pluginResult
callbackId:command.callbackId];
}];
}
/**
@@ -68,17 +62,17 @@
return;
}
NSArray* arguments = [command arguments];
NSString* content = [arguments objectAtIndex:0];
_callbackId = command.callbackId;
NSArray* arguments = [command arguments];
NSString* content = [arguments objectAtIndex:0];
NSMutableDictionary* settings = [arguments objectAtIndex:1];
UIPrintInteractionController* controller = [self printController];
[self adjustSettingsForPrintController:controller];
[self adjustPrintController:controller withSettings:settings];
[self loadContent:content intoPrintController:controller];
[self openPrintController:controller];
[self commandDelegate];
[self presentPrintController:controller];
}
/**
@@ -100,19 +94,54 @@
* @return {UIPrintInteractionController} controller
* The modified print controller instance
*/
- (UIPrintInteractionController*) adjustSettingsForPrintController:(UIPrintInteractionController*)controller
- (UIPrintInteractionController*) adjustPrintController:(UIPrintInteractionController*)controller
withSettings:(NSMutableDictionary*)settings
{
UIPrintInfo* printInfo = [UIPrintInfo printInfo];
UIPrintInfo* printInfo = [UIPrintInfo printInfo];
UIPrintInfoOrientation orientation = UIPrintInfoOrientationPortrait;
UIPrintInfoOutputType outputType = UIPrintInfoOutputGeneral;
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.orientation = UIPrintInfoOrientationPortrait;
if ([[settings objectForKey:@"landscape"] boolValue]) {
orientation = UIPrintInfoOrientationLandscape;
}
if ([[settings objectForKey:@"graystyle"] boolValue]) {
outputType = UIPrintInfoOutputGrayscale;
}
printInfo.outputType = outputType;
printInfo.orientation = orientation;
printInfo.jobName = [settings objectForKey:@"name"];
printInfo.duplex = [[settings objectForKey:@"duplex"] boolValue];
printInfo.printerID = [settings objectForKey:@"printerId"];
controller.printInfo = printInfo;
controller.showsPageRange = YES;
controller.showsPageRange = NO;
return controller;
}
/**
* Adjusts the web view and page renderer.
*/
- (void) adjustWebView:(UIWebView*)page
andPrintPageRenderer:(UIPrintPageRenderer*)renderer
{
UIViewPrintFormatter* formatter = [page viewPrintFormatter];
// margin not required - done in web page
formatter.contentInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
renderer.headerHeight = -30.0f;
renderer.footerHeight = -30.0f;
[renderer addPrintFormatter:formatter startingAtPageAtIndex:0];
page.scalesPageToFit = YES;
page.dataDetectorTypes = UIDataDetectorTypeNone;
page.userInteractionEnabled = NO;
page.autoresizingMask = (UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight);
}
/**
* Loads the content into the print controller.
*
@@ -123,24 +152,25 @@
*/
- (void) loadContent:(NSString*)content intoPrintController:(UIPrintInteractionController*)controller
{
// Set the base URL to be the www directory.
NSString* wwwFilePath = [[NSBundle mainBundle] pathForResource:@"www"
ofType:nil];
NSURL* baseURL = [NSURL fileURLWithPath:wwwFilePath];
// Load page into a webview and use its formatter to print the page
UIWebView* webPage = [[UIWebView alloc] init];
[webPage loadHTMLString:content baseURL:baseURL];
// Get formatter for web (note: margin not required - done in web page)
UIViewPrintFormatter* formatter = [webPage viewPrintFormatter];
formatter.contentInsets = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
UIWebView* page = [[UIWebView alloc] init];
UIPrintPageRenderer* renderer = [[UIPrintPageRenderer alloc] init];
renderer.headerHeight = -30.0f;
renderer.footerHeight = -30.0f;
[renderer addPrintFormatter:formatter startingAtPageAtIndex:0];
[self adjustWebView:page andPrintPageRenderer:renderer];
if ([NSURL URLWithString:content]) {
NSURL *url = [NSURL URLWithString:content];
[page loadRequest:[NSURLRequest requestWithURL:url]];
}
else {
// Set the base URL to be the www directory.
NSString* wwwFilePath = [[NSBundle mainBundle] pathForResource:@"www"
ofType:nil];
NSURL* baseURL = [NSURL fileURLWithPath:wwwFilePath];
[page loadHTMLString:content baseURL:baseURL];
}
controller.printPageRenderer = renderer;
}
@@ -152,9 +182,16 @@
* @param {UIPrintInteractionController} controller
* The prepared print controller with a content
*/
- (void) openPrintController:(UIPrintInteractionController*)controller
- (void) presentPrintController:(UIPrintInteractionController*)controller
{
[controller presentAnimated:YES completionHandler:NULL];
[controller presentAnimated:YES completionHandler:
^(UIPrintInteractionController *ctrl, BOOL ok, NSError *e) {
CDVPluginResult* pluginResult =
[CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:pluginResult
callbackId:_callbackId];
}];
}
/**
@@ -174,4 +211,4 @@
isPrintingAvailable];
}
@end
@end

View File

@@ -1,50 +0,0 @@
/*
Copyright 2013-2014 appPlant UG
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
*/
PrinterProxy = {
isServiceAvailable: function (successCallback, failCallback, args) {
args[0](true);
},
print: function (successCallback, failCallback, args) {
window.printContent = args[0];
Windows.Graphics.Printing.PrintManager.showPrintUIAsync();
},
printTaskRequested: function (printEvent) {
printEvent.request.createPrintTask("Print", function (args) {
var documentFragment = document.createDocumentFragment();
var content = document.createElement("html");
content.innerHTML = window.printContent;
documentFragment.appendChild(content);
args.setSource(MSApp.getHtmlPrintDocumentSource(documentFragment));
});
}
};
var printManager = Windows.Graphics.Printing.PrintManager.getForCurrentView();
printManager.onprinttaskrequested = PrinterProxy.printTaskRequested;
require("cordova/windows8/commandProxy").add("Printer", PrinterProxy);