diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7578f04..ad45646 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,9 @@
#### 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)
+- [__change__:] `isAvailable` returns false if no enabled service can be found (Android)
+- [feature:] New `pick` interface to pick a printer for future usage
+- [enhancement:] `isAvailable` returns count of available services (Android)
- [enhancement:] `print` returns bool value to indicate the result
- [enhancement:] Added missing `duplex` support (Android)
- [__change__:] `duplex` requires a string (`none`, `long` or `short`)
diff --git a/plugin.xml b/plugin.xml
index 49d3212..6ccfbec 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -73,7 +73,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/android/layout/printer_list_item.xml b/res/android/layout/printer_list_item.xml
new file mode 100644
index 0000000..aec511a
--- /dev/null
+++ b/res/android/layout/printer_list_item.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/android/layout/select_printer_activity.xml b/res/android/layout/select_printer_activity.xml
new file mode 100644
index 0000000..7897d66
--- /dev/null
+++ b/res/android/layout/select_printer_activity.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/Printer.java b/src/android/Printer.java
index 0df5f68..ec5d603 100644
--- a/src/android/Printer.java
+++ b/src/android/Printer.java
@@ -22,13 +22,12 @@
package de.appplant.cordova.plugin.printer;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintJob;
-import android.print.PrintManager;
+import android.print.PrinterId;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
@@ -41,13 +40,17 @@ 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;
+import de.appplant.cordova.plugin.printer.ext.PrintManager;
+import de.appplant.cordova.plugin.printer.ext.PrintServiceInfo;
+import de.appplant.cordova.plugin.printer.reflect.Meta;
+import de.appplant.cordova.plugin.printer.ui.SelectPrinterActivity;
+
+import static de.appplant.cordova.plugin.printer.ui.SelectPrinterActivity.EXTRA_PRINTER_ID;
+
/**
* Plugin to print HTML documents. Therefore it creates an invisible web view
* that loads the markup data. Once the page has been fully rendered it takes
@@ -102,6 +105,11 @@ public class Printer extends CordovaPlugin {
return true;
}
+ if (action.equalsIgnoreCase("pick")) {
+ pick();
+ return true;
+ }
+
if (action.equalsIgnoreCase("print")) {
print(args);
return true;
@@ -118,13 +126,14 @@ public class Printer extends CordovaPlugin {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
- List ids = getEnabledPrintServiceIds();
- Boolean available = ids.size() > 1;
+ PrintManager pm = new PrintManager(cordova.getActivity());
+ List services = pm.getEnabledPrintServices();
+ Boolean available = services.size() > 0;
PluginResult res1 = new PluginResult(
PluginResult.Status.OK, available);
PluginResult res2 = new PluginResult(
- PluginResult.Status.OK, new JSONArray(ids));
+ PluginResult.Status.OK, services.size());
PluginResult res = new PluginResult(
PluginResult.Status.OK, Arrays.asList(res1, res2));
@@ -152,6 +161,17 @@ public class Printer extends CordovaPlugin {
});
}
+ /**
+ * Presents a list with all enabled print services and invokes the
+ * callback with the selected one.
+ */
+ private void pick () {
+ Intent intent = new Intent(
+ cordova.getActivity(), SelectPrinterActivity.class);
+
+ cordova.startActivityForResult(this, intent, 0);
+ }
+
/**
* Loads the content into the web view.
*
@@ -190,10 +210,10 @@ public class Printer extends CordovaPlugin {
view.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
if (Build.VERSION.SDK_INT >= 21) {
- Method setMixedContentModeMethod = getMethod(settings.getClass(),
- "setMixedContentMode", int.class);
+ Method setMixedContentModeMethod = Meta.getMethod(
+ settings.getClass(), "setMixedContentMode", int.class);
- invokeMethod(settings, setMixedContentModeMethod, 2);
+ Meta.invokeMethod(settings, setMixedContentModeMethod, 2);
}
setWebViewClient(props);
@@ -219,7 +239,7 @@ public class Printer extends CordovaPlugin {
@Override
public void onPageFinished (WebView webView, String url) {
- PrintManager printManager = getPrintMgr();
+ PrintManager pm = new PrintManager(cordova.getActivity());
PrintAttributes.Builder builder = new PrintAttributes.Builder();
PrintDocumentAdapter adapter = getAdapter(webView, docName);
@@ -235,14 +255,14 @@ public class Printer extends CordovaPlugin {
if (!duplex.equals("none") && Build.VERSION.SDK_INT >= 23) {
boolean longEdge = duplex.equals("long");
- Method setDuplexModeMethod = getMethod(builder.getClass(),
- "setDuplexMode", int.class);
+ Method setDuplexModeMethod = Meta.getMethod(
+ builder.getClass(), "setDuplexMode", int.class);
- invokeMethod(builder, setDuplexModeMethod,
+ Meta.invokeMethod(builder, setDuplexModeMethod,
longEdge ? 2 : 4);
}
- job = printManager.print(docName, adapter, builder.build());
+ job = pm.getInstance().print(docName, adapter, builder.build());
view = null;
}
});
@@ -265,14 +285,24 @@ public class Printer extends CordovaPlugin {
command.sendPluginResult(res);
}
- /**
- * Get a PrintManager instance.
- *
- * @return A PrintManager instance.
- */
- private PrintManager getPrintMgr () {
- return (PrintManager) cordova.getActivity()
- .getSystemService(Context.PRINT_SERVICE);
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+
+ if (command == null)
+ return;
+
+ String url = null;
+
+ if (intent != null) {
+ PrinterId printerId = intent.getParcelableExtra(EXTRA_PRINTER_ID);
+ url = printerId.toString();
+ }
+
+ PluginResult res = new PluginResult(
+ PluginResult.Status.OK, url);
+
+ command.sendPluginResult(res);
}
/**
@@ -290,99 +320,14 @@ public class Printer extends CordovaPlugin {
*/
private PrintDocumentAdapter getAdapter (WebView webView, String docName) {
if (Build.VERSION.SDK_INT >= 21) {
- Method createPrintDocumentAdapterMethod = getMethod(
+ Method createPrintDocumentAdapterMethod = Meta.getMethod(
WebView.class, "createPrintDocumentAdapter", String.class);
- return (PrintDocumentAdapter) invokeMethod(
+ return (PrintDocumentAdapter) Meta.invokeMethod(
webView, createPrintDocumentAdapterMethod, docName);
} else {
- Method createPrintDocumentAdapterMethod = getMethod(
- WebView.class, "createPrintDocumentAdapter");
-
- return (PrintDocumentAdapter) invokeMethod(
- webView, createPrintDocumentAdapterMethod);
+ return (PrintDocumentAdapter) Meta.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;
- }
}
diff --git a/src/android/ext/PrintManager.java b/src/android/ext/PrintManager.java
new file mode 100644
index 0000000..b275033
--- /dev/null
+++ b/src/android/ext/PrintManager.java
@@ -0,0 +1,115 @@
+/*
+ Copyright 2013-2016 appPlant GmbH
+
+ 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.
+ */
+
+package de.appplant.cordova.plugin.printer.ext;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import de.appplant.cordova.plugin.printer.reflect.Meta;
+
+public final class PrintManager {
+
+ /**
+ * The application context.
+ */
+ private Context ctx;
+
+ /**
+ * Constructor
+ *
+ * @param context The context where to look for.
+ */
+ public PrintManager (Context context) {
+ this.ctx = context;
+ }
+
+ /**
+ * Get an instance from PrintManager service.
+ *
+ * @return A PrintManager instance.
+ */
+ public final android.print.PrintManager getInstance () {
+ return (android.print.PrintManager)
+ ctx.getSystemService(Context.PRINT_SERVICE);
+ }
+
+ /**
+ * Gets the list of installed print services.
+ *
+ * @return The found service list or an empty list.
+ */
+ public final List getInstalledPrintServices () {
+ List printers = (List) Meta.invokeMethod(getInstance(),
+ "getInstalledPrintServices");
+
+ ArrayList services =
+ new ArrayList();
+
+ if (printers == null)
+ return Collections.emptyList();
+
+ for (Object printer : printers) {
+ services.add(new PrintServiceInfo(printer));
+ }
+
+ return services;
+ }
+
+ /**
+ * Gets the list of enabled print services.
+ *
+ * @return The found service list or an empty list.
+ */
+ public final List getEnabledPrintServices () {
+ List printers = (List) Meta.invokeMethod(getInstance(),
+ "getEnabledPrintServices");
+
+ ArrayList services =
+ new ArrayList();
+
+ if (printers == null)
+ return Collections.emptyList();
+
+ for (Object printer : printers) {
+ services.add(new PrintServiceInfo(printer));
+ }
+
+ return services;
+ }
+
+ /**
+ * Creates an session object to discover all printer services. To do so
+ * you need to register a listener object and start the discovery process.
+ *
+ * @return An instance of class PrinterDiscoverySession.
+ */
+ public final PrinterDiscoverySession createPrinterDiscoverySession () {
+ Object session = Meta.invokeMethod(getInstance(),
+ "createPrinterDiscoverySession");
+
+ return new PrinterDiscoverySession(session);
+ }
+
+}
\ No newline at end of file
diff --git a/src/android/ext/PrintServiceInfo.java b/src/android/ext/PrintServiceInfo.java
new file mode 100644
index 0000000..be57764
--- /dev/null
+++ b/src/android/ext/PrintServiceInfo.java
@@ -0,0 +1,62 @@
+/*
+ Copyright 2013-2016 appPlant GmbH
+
+ 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.
+ */
+
+package de.appplant.cordova.plugin.printer.ext;
+
+import android.content.pm.ResolveInfo;
+
+import de.appplant.cordova.plugin.printer.reflect.Meta;
+
+public final class PrintServiceInfo {
+
+ /**
+ * The wrapped object of type android.printservice.PrintServiceInfo
+ */
+ private Object obj;
+
+ /**
+ * Wraps the object of the hidden class
+ * android.printservice.PrintServiceInfo.
+ *
+ * @param wrappedObj The object to wrap.
+ */
+ PrintServiceInfo (Object wrappedObj) {
+ obj = wrappedObj;
+ }
+
+ /**
+ * The add printers activity name.
+ *
+ * @return The add printers activity name.
+ */
+ public final String getAddPrintersActivityName() {
+ return (String) Meta.invokeMethod(obj, "getAddPrintersActivityName");
+ }
+ /**
+ * The service {@link ResolveInfo}.
+ *
+ * @return The info.
+ */
+ public final ResolveInfo getResolveInfo() {
+ return (ResolveInfo) Meta.invokeMethod(obj, "getResolveInfo");
+ }
+
+}
\ No newline at end of file
diff --git a/src/android/ext/PrinterDiscoverySession.java b/src/android/ext/PrinterDiscoverySession.java
new file mode 100644
index 0000000..b04da82
--- /dev/null
+++ b/src/android/ext/PrinterDiscoverySession.java
@@ -0,0 +1,178 @@
+/*
+ Copyright 2013-2016 appPlant GmbH
+
+ 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.
+ */
+
+package de.appplant.cordova.plugin.printer.ext;
+
+import android.print.PrinterInfo;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.List;
+
+import de.appplant.cordova.plugin.printer.reflect.Meta;
+
+public final class PrinterDiscoverySession {
+
+ /**
+ * Required interface of the listener.
+ */
+ public interface OnPrintersChangeListener {
+ void onPrintersChanged(List printerInfos);
+ }
+
+ /**
+ * Required to be able to register a listener of the expected type.
+ */
+ private class OnPrintersChangeProxy implements InvocationHandler {
+ @Override
+ public Object invoke (Object o, Method method, Object[] objects)
+ throws Throwable {
+
+ if (method.getName().equals("onPrintersChanged")) {
+ onPrintersChanged();
+ return null;
+ } else throw new Exception();
+ }
+
+ /**
+ * Delegate the event to the listener.
+ */
+ public void onPrintersChanged() {
+ notifyOnPrintersChanged();
+ }
+ }
+
+ /**
+ * The wrapped session of type
+ * android.print.PrinterDiscoverySession
+ */
+ private Object session;
+
+ /**
+ * Registered listener object.
+ */
+ private OnPrintersChangeListener listener = null;
+
+ /**
+ * Constructor
+ *
+ * @param session An instance of type
+ * android.print.PrinterDiscoverySession
+ */
+ public PrinterDiscoverySession (Object session) {
+ this.session = session;
+ }
+
+ /**
+ * Start discovering available printers.
+ */
+ public final void startPrinterDiscovery () {
+ Method method = Meta.getMethod(session.getClass(),
+ "startPrinterDiscovery", List.class);
+
+ Meta.invokeMethod(session, method, Collections.emptyList());
+ }
+
+ /**
+ * Stop discovering printers.
+ */
+ public final void stopPrinterDiscovery() {
+ Meta.invokeMethod(session, "stopPrinterDiscovery");
+ }
+
+ /**
+ * If session is discovering printers.
+ */
+ @SuppressWarnings("ConstantConditions")
+ public final boolean isPrinterDiscoveryStarted() {
+ return (Boolean) Meta.invokeMethod(session,
+ "isPrinterDiscoveryStarted");
+ }
+
+ /**
+ * Register the listener.
+ *
+ * @param listener Use null to unregister the listener.
+ */
+ public final void setOnPrintersChangeListener(
+ OnPrintersChangeListener listener) {
+
+ Class> interfaceCls = null;
+ Object proxy = null;
+
+ this.listener = listener;
+
+ try {
+ interfaceCls = Class.forName(
+ "android.print.PrinterDiscoverySession$OnPrintersChangeListener");
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ if (interfaceCls == null)
+ return;
+
+ Method method = Meta.getMethod(session.getClass(),
+ "setOnPrintersChangeListener", interfaceCls);
+
+ if (listener != null) {
+ Class>[] interfaces = {interfaceCls};
+
+ proxy = Proxy.newProxyInstance(
+ interfaceCls.getClassLoader(),
+ interfaces,
+ new OnPrintersChangeProxy()
+ );
+ }
+
+ Meta.invokeMethod(session, method, proxy);
+ }
+
+ /**
+ * Destroy the session if not already done.
+ */
+ public final void destroy() {
+ Meta.invokeMethod(session, "destroy");
+ }
+
+ /**
+ * Get a list of all yet discovered printers.
+ *
+ * @return List of their basic infos
+ */
+ @SuppressWarnings("unchecked")
+ public final List getPrinters() {
+ Method method = Meta.getMethod(session.getClass(), "getPrinters");
+
+ return (List) Meta.invokeMethod(session, method);
+ }
+
+ /**
+ * Notifies the listener about the occurred event.
+ */
+ private void notifyOnPrintersChanged() {
+ if (listener != null) {
+ listener.onPrintersChanged(getPrinters());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/android/reflect/Meta.java b/src/android/reflect/Meta.java
new file mode 100644
index 0000000..71a5767
--- /dev/null
+++ b/src/android/reflect/Meta.java
@@ -0,0 +1,123 @@
+/*
+ Copyright 2013-2016 appPlant GmbH
+
+ 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.
+ */
+
+package de.appplant.cordova.plugin.printer.reflect;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * This is an activity for selecting a printer.
+ */
+public abstract class Meta {
+ /**
+ * 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.
+ */
+ public static 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.
+ */
+ public static 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;
+ }
+
+ /**
+ * Invokes the method on the given object.
+ *
+ * @param obj
+ * An object which class defines the method.
+ * @param methodName
+ * The name of method to invoke.
+ * @return
+ * The returned object or null.
+ */
+ public static Object invokeMethod (Object obj, String methodName) {
+ Method method = getMethod(obj.getClass(), methodName);
+
+ if (method == null)
+ return null;
+
+ try {
+ return method.invoke(obj);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * Return a resource identifier for the given resource name.
+ *
+ * @param context The applications context.
+ * @param type Resource type to find (id or layout or string ...)
+ * @param name The name of the desired resource.
+ *
+ * @return The associated resource identifier or 0 if not found.
+ */
+ public static int getResId (Context context, String type, String name) {
+ Resources res = context.getResources();
+ String pkgName = context.getPackageName();
+
+ int resId;
+ resId = res.getIdentifier(name, type, pkgName);
+
+ return resId;
+ }
+}
diff --git a/src/android/ui/SelectPrinterActivity.java b/src/android/ui/SelectPrinterActivity.java
new file mode 100644
index 0000000..18bd7d2
--- /dev/null
+++ b/src/android/ui/SelectPrinterActivity.java
@@ -0,0 +1,339 @@
+/*
+ Copyright 2013-2016 appPlant GmbH
+
+ 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.
+ */
+
+package de.appplant.cordova.plugin.printer.ui;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.appplant.cordova.plugin.printer.ext.PrintManager;
+import de.appplant.cordova.plugin.printer.ext.PrinterDiscoverySession;
+import de.appplant.cordova.plugin.printer.ext.PrinterDiscoverySession.OnPrintersChangeListener;
+import de.appplant.cordova.plugin.printer.reflect.Meta;
+
+@SuppressLint("SetTextI18n")
+public final class SelectPrinterActivity extends Activity {
+
+ /**
+ * The extra string to identify the data from the intents bundle.
+ */
+ public static final String EXTRA_PRINTER_ID =
+ "INTENT_EXTRA_PRINTER_ID";
+
+ /**
+ * Reference to the main view which lists all discovered printers.
+ */
+ private ListView listView;
+
+ /**
+ * Session for printer discovering within the network.
+ */
+ private PrinterDiscoverySession discoverySession;
+
+ /**
+ * Called when the activity is starting.
+ *
+ * @param savedInstanceState If the activity is being re-initialized
+ * after previously being shut down then this
+ * Bundle contains the data it most recently
+ * supplied in.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(Meta.getResId(
+ this, "layout", "select_printer_activity"));
+
+ discoverySession = new PrintManager(this).createPrinterDiscoverySession();
+ listView = (ListView) findViewById(android.R.id.list);
+
+ initListView();
+ startPrinterDiscovery();
+ updateEmptyView();
+ }
+
+ /**
+ * Perform any final cleanup before an activity is destroyed.
+ */
+ @Override
+ protected void onDestroy() {
+ discoverySession.destroy();
+ super.onDestroy();
+ }
+
+ /**
+ * Sends an intent with the specified printer back to the owning activity
+ * and finishes that activity.
+ *
+ * @param printerId The printerId object of the printer containing
+ * everything necessary to identify and contact him.
+ */
+ private void onPrinterSelected (PrinterId printerId) {
+ Intent intent = new Intent();
+
+ intent.putExtra(EXTRA_PRINTER_ID, printerId);
+ setResult(RESULT_OK, intent);
+
+ finish();
+ }
+
+ /**
+ * Assigns the adapter and the click listener to the list.
+ */
+ private void initListView() {
+ listView.setAdapter(new ListViewAdapter());
+
+ listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ if (!listView.getAdapter().isEnabled(position)) {
+ return;
+ }
+
+ PrinterInfo printer = (PrinterInfo)
+ listView.getAdapter().getItem(position);
+
+ onPrinterSelected(printer.getId());
+ }
+ });
+ }
+
+ /**
+ * Start printer discovery if there are installed services found on the
+ * device.
+ */
+ private void startPrinterDiscovery() {
+ PrintManager pm = new PrintManager(this);
+
+ if (pm.getInstalledPrintServices().isEmpty())
+ return;
+
+ discoverySession.startPrinterDiscovery();
+ }
+
+ /**
+ * Shows or hides the empty-view of the list view depend on if the
+ * adapter contains any printers or not.
+ */
+ private void updateEmptyView() {
+ TextView titleView = (TextView) findViewById(android.R.id.title);
+ View progressBar = findViewById(android.R.id.progress);
+
+ if (listView.getEmptyView() == null) {
+ View emptyView = findViewById(android.R.id.empty);
+ listView.setEmptyView(emptyView);
+ }
+
+ if (discoverySession.isPrinterDiscoveryStarted()) {
+ titleView.setText("Searching for printers");
+ progressBar.setVisibility(View.VISIBLE);
+ } else {
+ titleView.setText("No printers found");
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+
+ private final class ListViewAdapter extends BaseAdapter {
+
+ /**
+ * Wait lock for synchronization.
+ */
+ private final Object lock = new Object();
+
+ /**
+ * List of all received printer to display in the list.
+ */
+ private final List printers = new ArrayList();
+
+ /**
+ * Constructor registers a listener to monitor about newly discovered
+ * printers.
+ */
+ ListViewAdapter() {
+ discoverySession.setOnPrintersChangeListener(new OnPrintersChangeListener() {
+ @Override
+ public void onPrintersChanged (List printerInfos) {
+ printers.addAll(printerInfos);
+ notifyDataSetChanged();
+ }
+ });
+ }
+
+ /**
+ * How many items are in the data set represented by this Adapter.
+ *
+ * @return Count of items.
+ */
+ @Override
+ public int getCount() {
+ synchronized (lock) {
+ return printers.size();
+ }
+ }
+
+ /**
+ * Get the data item associated with the specified position in the
+ * data set.
+ *
+ * @param position Position of the item whose data we want within the
+ * adapter's data set.
+ *
+ * @return The data at the specified position.
+ */
+ @Override
+ public Object getItem (int position) {
+ synchronized (lock) {
+ return printers.get(position);
+ }
+ }
+
+ /**
+ * Get the row id associated with the specified position in the list.
+ *
+ * @param position The position of the item within the adapter's data
+ * set whose row id we want.
+ *
+ * @return The id of the item at the specified position.
+ */
+ @Override
+ public long getItemId (int position) {
+ return position;
+ }
+
+ /**
+ * Gets a View that displays in the drop down popup the data at the
+ * specified position in the data set.
+ *
+ * @param pos The index of the item whose view we want.
+ * @param view The old view to reuse, if possible. Note: You should
+ * check that this view is non-null and of an appropriate
+ * type before using. If it is not possible to convert
+ * this view to display the correct data.
+ * @param parent The parent that this view will eventually be
+ * attached to.
+ *
+ * @return A View corresponding to the data at the specified position.
+ */
+ @Override
+ public View getDropDownView (int pos, View view, ViewGroup parent) {
+ return getView(pos, view, parent);
+ }
+
+ /**
+ * Get a View that displays the data at the specified position in the
+ * data set.
+ *
+ * @param pos The position of the item within the adapter's data set
+ * of the item whose view we want.
+ * @param view The old view to reuse, if possible. Note: You should
+ * check that this view is non-null and of an appropriate
+ * type before using. If it is not possible to convert
+ * this view to display the correct data.
+ * @param parent The parent that this view will eventually be
+ * attached to.
+ *
+ * @return A View corresponding to the data at the specified position.
+ */
+ @Override
+ public View getView (int pos, View view, ViewGroup parent) {
+ PrinterInfo printer = (PrinterInfo) getItem(pos);
+ CharSequence title = printer.getName();
+ CharSequence subtitle = null;
+ Drawable icon = null;
+
+ if (view == null) {
+ view = getLayoutInflater().inflate(
+ Meta.getResId(getApplicationContext(), "layout", "printer_list_item"),
+ parent, false);
+ }
+
+ try {
+ PackageManager pm = getPackageManager();
+ PrinterId pId = printer.getId();
+ Object cmpName = Meta.invokeMethod(pId, "getServiceName");
+ Object pkgName = Meta.invokeMethod(cmpName, "getPackageName");
+
+ PackageInfo packageInfo = pm.getPackageInfo(
+ (String) pkgName, 0);
+
+ subtitle = packageInfo.applicationInfo.loadLabel(pm);
+ icon = packageInfo.applicationInfo.loadIcon(pm);
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+
+ TextView titleView = (TextView) view.findViewById(android.R.id.title);
+ titleView.setText(title);
+
+ TextView subtitleView = (TextView) view.findViewById(android.R.id.hint);
+ if (!TextUtils.isEmpty(subtitle)) {
+ subtitleView.setText(subtitle);
+ subtitleView.setVisibility(View.VISIBLE);
+ } else {
+ subtitleView.setText(null);
+ subtitleView.setVisibility(View.GONE);
+ }
+
+ ImageView iconView = (ImageView) view.findViewById(android.R.id.icon);
+ if (icon != null) {
+ iconView.setImageDrawable(icon);
+ iconView.setVisibility(View.VISIBLE);
+ } else {
+ iconView.setVisibility(View.GONE);
+ }
+
+ return view;
+ }
+
+ /**
+ * Indicate whether the specified printer by position is available.
+ *
+ * @param position The position of the printer in the list.
+ *
+ * @return A truthy value means that the printer is available.
+ */
+ @Override
+ public boolean isEnabled (int position) {
+ PrinterInfo printer = (PrinterInfo) getItem(position);
+ return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+ }
+ }
+}
diff --git a/src/ios/APPPrinter.m b/src/ios/APPPrinter.m
index 207a44b..a68d0af 100755
--- a/src/ios/APPPrinter.m
+++ b/src/ios/APPPrinter.m
@@ -46,7 +46,8 @@
[self.commandDelegate runInBackground:^{
CDVPluginResult* pluginResult;
BOOL isAvailable = [self isPrintingAvailable];
- NSArray *multipart = @[[NSNumber numberWithBool:isAvailable], @[]];
+ NSArray *multipart = @[[NSNumber numberWithBool:isAvailable],
+ [NSNumber numberWithInt:-1]];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsMultipart:multipart];