diff --git a/CHANGELOG.md b/CHANGELOG.md
index f833d9a..4fd0962 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,12 @@
## ChangeLog
+#### Version 0.7.2 (not yet released)
+- [__change__:] Changed plugin ID to `cordova-plugin-printer`
+- [__change__:] Plugin requires Android KitKat or newer
+- [__change__:] `isAvailable` returns false if no enabled print services can be found (Android)
+- [enhancement:] `isAvailable` returns additional list of available print services (Android)
+- [enhancement:] Support `duplex` attribute (Android)
+
+
#### Version 0.7.1 (23.04.2015)
- [bugfix:] `isAvailable` does not block the main thread anymore.
- [bugfix:] iPad+iOS8 incompatibility (Thanks to __zmagyar__)
diff --git a/plugin.xml b/plugin.xml
index 55cec76..49d3212 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -42,7 +42,8 @@
-
+
+
diff --git a/src/android/Printer.java b/src/android/Printer.java
index 9879af4..fb4d646 100644
--- a/src/android/Printer.java
+++ b/src/android/Printer.java
@@ -21,14 +21,6 @@
package de.appplant.cordova.plugin.printer;
-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.Context;
import android.os.Build;
@@ -39,7 +31,20 @@ import android.print.PrintManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-@TargetApi(19)
+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 java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
public class Printer extends CordovaPlugin {
private WebView view;
@@ -65,23 +70,20 @@ public class Printer extends CordovaPlugin {
*/
@Override
public boolean execute (String action, JSONArray args,
- CallbackContext callback) throws JSONException {
+ CallbackContext callback) throws JSONException {
command = callback;
if (action.equalsIgnoreCase("isAvailable")) {
isAvailable();
-
return true;
}
if (action.equalsIgnoreCase("print")) {
print(args);
-
return true;
}
- // Returning false results in a "MethodNotFound" error.
return false;
}
@@ -93,10 +95,17 @@ public class Printer extends CordovaPlugin {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
- Boolean supported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
- PluginResult result = new PluginResult(PluginResult.Status.OK, supported);
+ List ids = getEnabledPrintServiceIds();
+ Boolean available = ids.size() > 1;
- command.sendPluginResult(result);
+ PluginResult res1 = new PluginResult(
+ PluginResult.Status.OK, available);
+ PluginResult res2 = new PluginResult(
+ PluginResult.Status.OK, new JSONArray(ids));
+ PluginResult res = new PluginResult(
+ PluginResult.Status.OK, Arrays.asList(res1, res2));
+
+ command.sendPluginResult(res);
}
});
}
@@ -130,11 +139,12 @@ public class Printer extends CordovaPlugin {
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);
- view.loadDataWithBaseURL(baseURL, content, "text/html", "UTF-8", null);
+ // Set base URI to the assets/www folder
+ view.loadDataWithBaseURL(
+ baseURL, content, "text/html", "UTF-8", null);
}
}
@@ -161,9 +171,10 @@ public class Printer extends CordovaPlugin {
* The JSON object with the containing page properties
*/
private void setWebViewClient (JSONObject props) {
- final String docName = props.optString("name", DEFAULT_DOC_NAME);
+ final String docName = props.optString("name", DEFAULT_DOC_NAME);
final boolean landscape = props.optBoolean("landscape", false);
final boolean graystyle = props.optBoolean("graystyle", false);
+ final String duplex = props.optString("duplex", "none");
view.setWebViewClient(new WebViewClient() {
@Override
@@ -173,27 +184,31 @@ public class Printer extends CordovaPlugin {
@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
+ PrintManager printManager = getPrintMgr();
PrintAttributes.Builder builder = new PrintAttributes.Builder();
+ PrintDocumentAdapter adapter = getAdapter(webView, docName);
- // The page does itself set its own margins
builder.setMinMargins(PrintAttributes.Margins.NO_MARGINS);
- builder.setColorMode(graystyle ? PrintAttributes.COLOR_MODE_MONOCHROME
+ builder.setColorMode(graystyle
+ ? PrintAttributes.COLOR_MODE_MONOCHROME
: PrintAttributes.COLOR_MODE_COLOR);
- builder.setMediaSize(landscape ? PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE
+ 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());
+ if (!duplex.equals("none") && Build.VERSION.SDK_INT >= 23) {
+ boolean longEdge = duplex.equals("long");
+ Method setDuplexModeMethod = getMethod(builder.getClass(),
+ "setDuplexMode", int.class);
+
+ invokeMethod(builder, setDuplexModeMethod,
+ longEdge ? 2 : 4);
+ }
+
+ PrintJob job = printManager.print(
+ docName, adapter, builder.build());
invokeCallbackOnceCompletedOrCanceled(job);
@@ -221,4 +236,125 @@ public class Printer extends CordovaPlugin {
}
});
}
+
+ /**
+ * Get a PrintManager instance.
+ *
+ * @return A PrintManager instance.
+ */
+ private PrintManager getPrintMgr () {
+ return (PrintManager) cordova.getActivity()
+ .getSystemService(Context.PRINT_SERVICE);
+ }
+
+ /**
+ * Create the print document adapter for the web view component. On
+ * devices older then SDK 21 it will use the deprecated method
+ * `createPrintDocumentAdapter` without arguments and on newer devices
+ * the recommended way.
+ *
+ * @param webView
+ * The web view which content to print out.
+ * @param docName
+ * The name of the printed document.
+ * @return
+ * The created adapter.
+ */
+ private PrintDocumentAdapter getAdapter (WebView webView, String docName) {
+ if (Build.VERSION.SDK_INT >= 21) {
+ Method createPrintDocumentAdapterMethod = getMethod(
+ WebView.class, "createPrintDocumentAdapter", String.class);
+
+ return (PrintDocumentAdapter) invokeMethod(
+ webView, createPrintDocumentAdapterMethod, docName);
+ } else {
+ Method createPrintDocumentAdapterMethod = getMethod(
+ WebView.class, "createPrintDocumentAdapter");
+
+ return (PrintDocumentAdapter) invokeMethod(
+ webView, createPrintDocumentAdapterMethod);
+ }
+ }
+
+ /**
+ * Get a list of ids of all installed and enabled print services. For
+ * that it uses reflections to call public but hidden methods from the
+ * PrintManager.
+ *
+ * @return A list of found print service ids.
+ */
+ private List getEnabledPrintServiceIds () {
+ try {
+ PrintManager printMgr = getPrintMgr();
+ Class> printerCls = Class.forName(
+ "android.printservice.PrintServiceInfo");
+ Method getPrinterMethod = getMethod(printMgr.getClass(),
+ "getEnabledPrintServices");
+ Method getIdMethod = getMethod(printerCls,
+ "getId");
+
+ List printers = (List) invokeMethod(printMgr, getPrinterMethod);
+ ArrayList printerIds = new ArrayList();
+
+ printerIds.add("android.print.pdf");
+
+ if (printers == null)
+ return printerIds;
+
+ for (Object printer : printers) {
+ String printerId = (String) invokeMethod(printer, getIdMethod);
+ printerIds.add(printerId);
+ }
+
+ return printerIds;
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ return Collections.emptyList();
+ }
+
+ /**
+ * Finds the method with given name and set of arguments.
+ *
+ * @param cls
+ * The class in where to look for the method declaration.
+ * @param name
+ * The name of the method.
+ * @param params
+ * The arguments of the method.
+ * @return
+ * The found method or null.
+ */
+ private Method getMethod (Class> cls, String name, Class>... params) {
+ try {
+ return cls.getDeclaredMethod(name, params);
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Invokes the method on the given object with the specified arguments.
+ *
+ * @param obj
+ * An object which class defines the method.
+ * @param method
+ * The method to invoke.
+ * @param args
+ * Set of arguments.
+ * @return
+ * The returned object or null.
+ */
+ private Object invokeMethod (Object obj, Method method, Object... args) {
+ try {
+ return method.invoke(obj, args);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
}