Initial reimplementation for Android
This commit is contained in:
parent
652c6c62c2
commit
6c16959267
43
plugin.xml
43
plugin.xml
@ -26,7 +26,7 @@
|
||||
<plugin id="cordova-plugin-printer"
|
||||
xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
version="0.8.0.beta">
|
||||
version="0.8.0.alpha">
|
||||
|
||||
<name>Printer</name>
|
||||
|
||||
@ -97,37 +97,24 @@
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest/application">
|
||||
<activity
|
||||
android:name="de.appplant.cordova.plugin.printer.ui.SelectPrinterActivity"
|
||||
android:label="Printer"
|
||||
android:exported="false">
|
||||
</activity>
|
||||
</config-file>
|
||||
<preference name="ANDROID_SUPPORT_V4_VERSION" default="28.+"/>
|
||||
|
||||
<framework src="com.android.support:support-v4:$ANDROID_SUPPORT_V4_VERSION"/>
|
||||
|
||||
<source-file src="src/android/AssetUtil.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/Options.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/PdfAdapter.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/Printer.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
|
||||
<source-file src="src/android/ui/SelectPrinterActivity.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer/ui" />
|
||||
|
||||
<source-file src="src/android/reflect/Meta.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer/reflect" />
|
||||
|
||||
<source-file src="src/android/ext/PrinterDiscoverySession.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer/ext" />
|
||||
|
||||
<source-file src="src/android/ext/PrintManager.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer/ext" />
|
||||
|
||||
<source-file src="src/android/ext/PrintServiceInfo.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer/ext" />
|
||||
|
||||
<resource-file src="res/android/layout/printer_list_item.xml"
|
||||
target="res/layout/printer_list_item.xml" />
|
||||
|
||||
<resource-file src="res/android/layout/select_printer_activity.xml"
|
||||
target="res/layout/select_printer_activity.xml" />
|
||||
<source-file src="src/android/PrintManager.java"
|
||||
target-dir="src/de/appplant/cordova/plugin/printer" />
|
||||
</platform>
|
||||
|
||||
<!-- windows -->
|
||||
|
@ -1,80 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
* Copyright (c) 2013-2016 by appPlant GmbH. All rights reserved.
|
||||
*
|
||||
* @APPPLANT_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apache License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPPLANT_LICENSE_HEADER_END@
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="32dip"
|
||||
android:layout_height="32dip"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="8dip"
|
||||
android:duplicateParentState="true"
|
||||
android:contentDescription="@null"
|
||||
android:visibility="invisible">
|
||||
</ImageView>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:duplicateParentState="true">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textIsSelectable="false"
|
||||
android:gravity="top|start"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:duplicateParentState="true">
|
||||
</TextView>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/hint"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textIsSelectable="false"
|
||||
android:visibility="gone"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:duplicateParentState="true">
|
||||
</TextView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
* Copyright (c) 2013-2016 by appPlant GmbH. All rights reserved.
|
||||
*
|
||||
* @APPPLANT_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apache License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. Please obtain a copy of the License at
|
||||
* http://opensource.org/licenses/Apache-2.0/ and read it before using this
|
||||
* file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPPLANT_LICENSE_HEADER_END@
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<ListView
|
||||
android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
android:cacheColorHint="@android:color/transparent"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="true" >
|
||||
</ListView>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@android:id/empty"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:text="Searching for printers">
|
||||
</TextView>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@android:id/progress"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:indeterminate="true"
|
||||
style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal">
|
||||
</ProgressBar>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
343
src/android/AssetUtil.java
Normal file
343
src/android/AssetUtil.java
Normal file
@ -0,0 +1,343 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Base64;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
class AssetUtil {
|
||||
|
||||
// List of supported content types
|
||||
enum ContentType { PLAIN, HTML, IMAGE, PDF, SELF }
|
||||
|
||||
// Application context
|
||||
private final @NonNull Context context;
|
||||
|
||||
/**
|
||||
* Initializes the asset utils.
|
||||
*
|
||||
* @param ctx The application context.
|
||||
*/
|
||||
AssetUtil (@NonNull Context ctx) {
|
||||
this.context = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static @NonNull ContentType getContentType (@Nullable String path)
|
||||
{
|
||||
ContentType type = ContentType.PLAIN;
|
||||
|
||||
if (path == null || path.isEmpty())
|
||||
{
|
||||
type = ContentType.SELF;
|
||||
}
|
||||
else if (path.charAt(0) == '<')
|
||||
{
|
||||
type = ContentType.HTML;
|
||||
}
|
||||
else if (path.matches("^[a-z]+://.+"))
|
||||
{
|
||||
return path.endsWith(".pdf") ? ContentType.PDF : ContentType.IMAGE;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 InputStream open (@NonNull String path)
|
||||
{
|
||||
InputStream stream = null;
|
||||
|
||||
if (path.startsWith("res:"))
|
||||
{
|
||||
stream = openResource(path);
|
||||
}
|
||||
else if (path.startsWith("file:///"))
|
||||
{
|
||||
stream = openFile(path);
|
||||
}
|
||||
else if (path.startsWith("file://"))
|
||||
{
|
||||
stream = openAsset(path);
|
||||
}
|
||||
else if (path.startsWith("base64:"))
|
||||
{
|
||||
stream = openBase64(path);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Bitmap decode (@NonNull String path)
|
||||
{
|
||||
Bitmap bitmap;
|
||||
|
||||
if (path.startsWith("res:"))
|
||||
{
|
||||
bitmap = decodeResource(path);
|
||||
}
|
||||
else if (path.startsWith("file:///"))
|
||||
{
|
||||
bitmap = decodeFile(path);
|
||||
}
|
||||
else if (path.startsWith("file://"))
|
||||
{
|
||||
bitmap = decodeAsset(path);
|
||||
}
|
||||
else if (path.startsWith("base64:"))
|
||||
{
|
||||
bitmap = decodeBase64(path);
|
||||
}
|
||||
else {
|
||||
bitmap = BitmapFactory.decodeFile(path);
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies content of input stream to output stream.
|
||||
*
|
||||
* @param input The readable input stream.
|
||||
* @param output The writable output stream.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
static void copy (@NonNull InputStream input,
|
||||
@NonNull OutputStream output) throws IOException
|
||||
{
|
||||
byte[] buf = new byte[1024];
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = input.read(buf)) > 0) {
|
||||
output.write(buf, 0, bytesRead);
|
||||
}
|
||||
|
||||
output.close();
|
||||
input.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an file given as a file:/// path.
|
||||
*
|
||||
* @param path The path to the file.
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @Nullable InputStream openFile (@NonNull String path)
|
||||
{
|
||||
String absPath = path.substring(7);
|
||||
|
||||
try {
|
||||
return new FileInputStream(absPath);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an file given as a file:/// path to a bitmap.
|
||||
*
|
||||
* @param path The path to the file.
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeFile (@NonNull String path)
|
||||
{
|
||||
String absPath = path.substring(7);
|
||||
|
||||
return BitmapFactory.decodeFile(absPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens an asset file given as a file:// path.
|
||||
*
|
||||
* @param path The path to the asset.
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @Nullable InputStream openAsset (@NonNull String path)
|
||||
{
|
||||
String resPath = path.replaceFirst("file:/", "www");
|
||||
|
||||
try {
|
||||
return getAssets().open(resPath);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an asset file given as a file:// path to a bitmap.
|
||||
*
|
||||
* @param path The path to the asset.
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeAsset (@NonNull String path)
|
||||
{
|
||||
InputStream stream = openAsset(path);
|
||||
Bitmap bitmap;
|
||||
|
||||
if (stream == null)
|
||||
return null;
|
||||
|
||||
bitmap = BitmapFactory.decodeStream(stream);
|
||||
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a resource file given as a res:// path.
|
||||
*
|
||||
* @param path The path to the resource.
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @NonNull InputStream openResource (@NonNull String path)
|
||||
{
|
||||
String resPath = path.substring(6);
|
||||
int resId = getResId(resPath);
|
||||
|
||||
return getResources().openRawResource(resId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a resource given as a res:// path to a bitmap.
|
||||
*
|
||||
* @param path The path to the resource.
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeResource (@NonNull String path)
|
||||
{
|
||||
String data = path.substring(9);
|
||||
byte[] bytes = Base64.decode(data, 0);
|
||||
|
||||
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a resource file given as a res:// path.
|
||||
*
|
||||
* @param path The path to the resource.
|
||||
*
|
||||
* @return An open IO stream or null if the file does not exist.
|
||||
*/
|
||||
private @NonNull InputStream openBase64 (@NonNull String path)
|
||||
{
|
||||
String data = path.substring(9);
|
||||
byte[] bytes = Base64.decode(data, 0);
|
||||
|
||||
return new ByteArrayInputStream(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a resource given as a base64:// string to a bitmap.
|
||||
*
|
||||
* @param path The given relative path.
|
||||
*
|
||||
* @return A bitmap or null if the path is not valid
|
||||
*/
|
||||
private @Nullable Bitmap decodeBase64 (@NonNull String path)
|
||||
{
|
||||
String data = path.substring(9);
|
||||
byte[] bytes = Base64.decode(data, 0);
|
||||
|
||||
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource ID for the given resource path.
|
||||
*
|
||||
* @return The resource ID for the given resource.
|
||||
*/
|
||||
private int getResId (@NonNull String resPath)
|
||||
{
|
||||
Resources res = getResources();
|
||||
String pkgName = context.getPackageName();
|
||||
String dirName = "drawable";
|
||||
String fileName = resPath;
|
||||
|
||||
if (resPath.contains("/")) {
|
||||
dirName = resPath.substring(0, resPath.lastIndexOf('/'));
|
||||
fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||
int resId = res.getIdentifier(resName, dirName, pkgName);
|
||||
|
||||
if (resId == 0) {
|
||||
resId = res.getIdentifier(resName, "mipmap", pkgName);
|
||||
}
|
||||
|
||||
if (resId == 0) {
|
||||
resId = res.getIdentifier(resName, "drawable", pkgName);
|
||||
}
|
||||
|
||||
return resId;
|
||||
}
|
||||
|
||||
private AssetManager getAssets() {
|
||||
return context.getAssets();
|
||||
}
|
||||
|
||||
private Resources getResources() {
|
||||
return context.getResources();
|
||||
}
|
||||
}
|
167
src/android/Options.java
Normal file
167
src/android/Options.java
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import android.print.PrintAttributes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.print.PrintHelper;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.print.PrintAttributes.DUPLEX_MODE_LONG_EDGE;
|
||||
import static android.print.PrintAttributes.DUPLEX_MODE_NONE;
|
||||
import static android.print.PrintAttributes.DUPLEX_MODE_SHORT_EDGE;
|
||||
import static android.print.PrintAttributes.Margins.NO_MARGINS;
|
||||
import static android.print.PrintAttributes.MediaSize.UNKNOWN_LANDSCAPE;
|
||||
import static android.print.PrintAttributes.MediaSize.UNKNOWN_PORTRAIT;
|
||||
import static android.support.v4.print.PrintHelper.ORIENTATION_LANDSCAPE;
|
||||
import static android.support.v4.print.PrintHelper.ORIENTATION_PORTRAIT;
|
||||
import static android.support.v4.print.PrintHelper.SCALE_MODE_FILL;
|
||||
import static android.support.v4.print.PrintHelper.SCALE_MODE_FIT;
|
||||
|
||||
/**
|
||||
* Wrapper for the print job settings.
|
||||
*/
|
||||
class Options {
|
||||
|
||||
// The print job settings
|
||||
private @NonNull JSONObject spec;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param spec The print job settings.
|
||||
*/
|
||||
Options (@NonNull JSONObject spec)
|
||||
{
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name for the print job.
|
||||
*/
|
||||
@NonNull String getJobName()
|
||||
{
|
||||
String jobName = spec.optString("name");
|
||||
|
||||
if (jobName == null || jobName.isEmpty())
|
||||
{
|
||||
jobName = "Printer Plugin Job #" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
return jobName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the options into a PrintAttributes object.
|
||||
*/
|
||||
@NonNull PrintAttributes toPrintAttributes()
|
||||
{
|
||||
PrintAttributes.Builder builder = new PrintAttributes.Builder();
|
||||
|
||||
switch (spec.optString("orientation"))
|
||||
{
|
||||
case "landscape":
|
||||
builder.setMediaSize(UNKNOWN_LANDSCAPE);
|
||||
break;
|
||||
case "portrait":
|
||||
builder.setMediaSize(UNKNOWN_PORTRAIT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (spec.has("graystyle"))
|
||||
{
|
||||
if (spec.optBoolean("graystyle"))
|
||||
{
|
||||
builder.setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.setColorMode(PrintAttributes.COLOR_MODE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
if (!spec.optBoolean("border", true))
|
||||
{
|
||||
builder.setMinMargins(NO_MARGINS);
|
||||
}
|
||||
|
||||
if (SDK_INT >= 23)
|
||||
{
|
||||
switch (spec.optString("duplex"))
|
||||
{
|
||||
case "long":
|
||||
builder.setDuplexMode(DUPLEX_MODE_LONG_EDGE);
|
||||
break;
|
||||
case "short":
|
||||
builder.setDuplexMode(DUPLEX_MODE_SHORT_EDGE);
|
||||
break;
|
||||
case "none":
|
||||
builder.setDuplexMode(DUPLEX_MODE_NONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tweaks the printer helper depending on the job spec.
|
||||
*
|
||||
* @param printer The printer to decorate.
|
||||
*/
|
||||
void decoratePrintHelper (@NonNull PrintHelper printer)
|
||||
{
|
||||
switch (spec.optString("orientation"))
|
||||
{
|
||||
case "landscape":
|
||||
printer.setOrientation(ORIENTATION_LANDSCAPE);
|
||||
break;
|
||||
case "portrait":
|
||||
printer.setOrientation(ORIENTATION_PORTRAIT);
|
||||
break;
|
||||
}
|
||||
|
||||
if (spec.has("graystyle"))
|
||||
{
|
||||
if (spec.optBoolean("graystyle"))
|
||||
{
|
||||
printer.setColorMode(PrintHelper.COLOR_MODE_MONOCHROME);
|
||||
}
|
||||
else
|
||||
{
|
||||
printer.setColorMode(PrintHelper.COLOR_MODE_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
if (spec.has("autoFit"))
|
||||
{
|
||||
if (spec.optBoolean("autoFit"))
|
||||
{
|
||||
printer.setScaleMode(SCALE_MODE_FIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
printer.setScaleMode(SCALE_MODE_FILL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
src/android/PdfAdapter.java
Normal file
108
src/android/PdfAdapter.java
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.print.PageRange;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.print.PrintDocumentInfo;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import static android.print.PrintDocumentInfo.CONTENT_TYPE_DOCUMENT;
|
||||
|
||||
/**
|
||||
* Document adapter to render and print PDF files.
|
||||
*/
|
||||
class PdfAdapter extends PrintDocumentAdapter {
|
||||
|
||||
// The application context
|
||||
private @NonNull Context context;
|
||||
|
||||
// The path to the PDF file
|
||||
private @NonNull String path;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The context where to look for.
|
||||
*/
|
||||
PdfAdapter (@NonNull String path, @NonNull Context context)
|
||||
{
|
||||
this.path = path;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayout (PrintAttributes oldAttributes,
|
||||
PrintAttributes newAttributes,
|
||||
CancellationSignal cancellationSignal,
|
||||
LayoutResultCallback callback,
|
||||
Bundle bundle)
|
||||
{
|
||||
PrintDocumentInfo pdi;
|
||||
|
||||
if (cancellationSignal.isCanceled())
|
||||
return;
|
||||
|
||||
pdi = new PrintDocumentInfo.Builder("test")
|
||||
.setContentType(CONTENT_TYPE_DOCUMENT)
|
||||
.build();
|
||||
|
||||
callback.onLayoutFinished(pdi, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWrite (PageRange[] range,
|
||||
ParcelFileDescriptor dest,
|
||||
CancellationSignal cancellationSignal,
|
||||
WriteResultCallback callback)
|
||||
{
|
||||
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());
|
||||
|
||||
try {
|
||||
AssetUtil.copy(in, out);
|
||||
} catch (IOException e) {
|
||||
callback.onWriteFailed(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
callback.onWriteFinished(new PageRange[]{ PageRange.ALL_PAGES });
|
||||
}
|
||||
}
|
167
src/android/PrintManager.java
Normal file
167
src/android/PrintManager.java
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
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;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.print.PrintHelper;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import static android.content.Context.PRINT_SERVICE;
|
||||
|
||||
class PrintManager {
|
||||
|
||||
// The application context
|
||||
private @NonNull Context context;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The context where to look for.
|
||||
*/
|
||||
PrintManager (@NonNull Context context)
|
||||
{
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the print framework is able to render the referenced file.
|
||||
*
|
||||
* @param item Any kind of URL like file://, file:///, res:// or base64://
|
||||
*
|
||||
* @return true if its able to render the content of the file.
|
||||
*/
|
||||
boolean canPrintItem (@Nullable String item)
|
||||
{
|
||||
boolean supported = PrintHelper.systemSupportsPrint();
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
supported = new AssetUtil(context).open(item) != null;
|
||||
}
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of all printable document types (utis).
|
||||
*/
|
||||
static JSONArray getPrintableUTIs()
|
||||
{
|
||||
JSONArray utis = new JSONArray();
|
||||
|
||||
utis.put("com.adobe.pdf");
|
||||
utis.put("com.microsoft.bmp");
|
||||
utis.put("public.jpeg");
|
||||
utis.put("public.jpeg-2000");
|
||||
utis.put("public.png");
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the provided content to the printing controller and opens
|
||||
* them.
|
||||
*
|
||||
* @param content The content or file to print.
|
||||
* @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,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
switch (AssetUtil.getContentType(content))
|
||||
{
|
||||
case IMAGE:
|
||||
printImage(content, settings, callback);
|
||||
break;
|
||||
case PDF:
|
||||
printPdf(content, settings, callback);
|
||||
break;
|
||||
case HTML:
|
||||
case SELF:
|
||||
case PLAIN:
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the provided PDF document.
|
||||
*
|
||||
* @param path The path to the file to print.
|
||||
* @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,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
Options options = new Options(settings);
|
||||
String jobName = options.getJobName();
|
||||
PrintDocumentAdapter adapter = new PdfAdapter(path, context);
|
||||
PrintAttributes attributes = options.toPrintAttributes();
|
||||
|
||||
getPrintService().print(jobName, adapter, attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the specified image by file uri.
|
||||
*
|
||||
* @param path The path to the file to print.
|
||||
* @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,
|
||||
@Nullable PrintHelper.OnPrintFinishCallback callback)
|
||||
{
|
||||
AssetUtil decoder = new AssetUtil(context);
|
||||
Bitmap bitmap = decoder.decode(path);
|
||||
|
||||
if (bitmap == null) return;
|
||||
|
||||
Options options = new Options(settings);
|
||||
PrintHelper printer = new PrintHelper(context);
|
||||
String jobName = options.getJobName();
|
||||
|
||||
options.decoratePrintHelper(printer);
|
||||
|
||||
printer.printBitmap(jobName, bitmap, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the print service of the app.
|
||||
*/
|
||||
private @NonNull android.print.PrintManager getPrintService()
|
||||
{
|
||||
return (android.print.PrintManager) context.getSystemService(PRINT_SERVICE);
|
||||
}
|
||||
}
|
@ -21,83 +21,20 @@
|
||||
|
||||
package de.appplant.cordova.plugin.printer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.print.PrintAttributes;
|
||||
import android.print.PrintDocumentAdapter;
|
||||
import android.print.PrintJob;
|
||||
import android.print.PrinterId;
|
||||
import android.view.View;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import de.appplant.cordova.plugin.printer.ext.PrintManager;
|
||||
import de.appplant.cordova.plugin.printer.ext.PrintManager.OnPrintJobStateChangeListener;
|
||||
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 android.print.PrintJobInfo.STATE_STARTED;
|
||||
import static de.appplant.cordova.plugin.printer.ui.SelectPrinterActivity.ACTION_SELECT_PRINTER;
|
||||
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
|
||||
* the print adapter of that web view and initializes a print job.
|
||||
*/
|
||||
public class Printer extends CordovaPlugin {
|
||||
CordovaWebView _webView;
|
||||
|
||||
/**
|
||||
* The web view that loads all the content.
|
||||
*/
|
||||
private WebView view;
|
||||
|
||||
/**
|
||||
* Reference is necessary to invoke the callback in the onresume event.
|
||||
*/
|
||||
private CallbackContext command;
|
||||
|
||||
/**
|
||||
* Instance of the print manager to listen for job status changes.
|
||||
*/
|
||||
private PrintManager pm;
|
||||
|
||||
/**
|
||||
* Invokes the callback once the job has reached a final state.
|
||||
*/
|
||||
private OnPrintJobStateChangeListener listener = new OnPrintJobStateChangeListener() {
|
||||
/**
|
||||
* Callback notifying that a print job state changed.
|
||||
*
|
||||
* @param job The print job.
|
||||
*/
|
||||
@Override
|
||||
public void onPrintJobStateChanged(PrintJob job) {
|
||||
notifyAboutPrintJobResult(job);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default name of the printed document (PDF-Printer).
|
||||
*/
|
||||
private static final String DEFAULT_DOC_NAME = "unknown";
|
||||
|
||||
/**
|
||||
* Executes the request.
|
||||
@ -112,298 +49,85 @@ public class Printer extends CordovaPlugin {
|
||||
* @param action The action to execute.
|
||||
* @param args The exec() arguments in JSON form.
|
||||
* @param callback The callback context used when calling back into JavaScript.
|
||||
*
|
||||
* @return Whether the action was valid.
|
||||
*/
|
||||
@Override
|
||||
public boolean execute (String action, JSONArray args,
|
||||
CallbackContext callback) throws JSONException {
|
||||
CallbackContext callback)
|
||||
{
|
||||
boolean valid = true;
|
||||
|
||||
command = callback;
|
||||
|
||||
if (action.equalsIgnoreCase("check")) {
|
||||
check();
|
||||
return true;
|
||||
if (action.equalsIgnoreCase("check"))
|
||||
{
|
||||
check(args.optString(0), callback);
|
||||
}
|
||||
else if (action.equalsIgnoreCase("utis"))
|
||||
{
|
||||
utis(callback);
|
||||
}
|
||||
else if (action.equalsIgnoreCase("print"))
|
||||
{
|
||||
print(args.optString(0), args.optJSONObject(1), callback);
|
||||
}
|
||||
else {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (action.equalsIgnoreCase("pick")) {
|
||||
pick();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (action.equalsIgnoreCase("print")) {
|
||||
print(args);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs if the device is able to print documents.
|
||||
* A Internet connection is required to load the cloud print dialog.
|
||||
*/
|
||||
private void check () {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<PrintServiceInfo> services = pm.getEnabledPrintServices();
|
||||
Boolean available = services.size() > 0;
|
||||
|
||||
PluginResult res1 = new PluginResult(
|
||||
PluginResult.Status.OK, available);
|
||||
PluginResult res2 = new PluginResult(
|
||||
PluginResult.Status.OK, services.size());
|
||||
PluginResult res = new PluginResult(
|
||||
PluginResult.Status.OK, Arrays.asList(res1, res2));
|
||||
|
||||
command.sendPluginResult(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the HTML content into the web view and invokes the print manager.
|
||||
* If the print framework is able to render the referenced file.
|
||||
*
|
||||
* @param args
|
||||
* The exec arguments as JSON
|
||||
* @param item Any kind of URL like file://, file:///, res:// or base64://
|
||||
* @param callback The plugin function to invoke with the result.
|
||||
*/
|
||||
private void print (final JSONArray args) {
|
||||
final String content = args.optString(0);
|
||||
final JSONObject props = args.optJSONObject(1);
|
||||
|
||||
cordova.getActivity().runOnUiThread( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if( content.isEmpty() ) {
|
||||
do_print((WebView)_webView.getView(), props);
|
||||
} else {
|
||||
initWebView(props);
|
||||
loadContent(content);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param content
|
||||
* Either an HTML string or URI
|
||||
*/
|
||||
private void loadContent(String content) {
|
||||
if (content.startsWith("http") || content.startsWith("file:")) {
|
||||
view.loadUrl(content);
|
||||
} else {
|
||||
String baseURL = webView.getUrl();
|
||||
baseURL = baseURL.substring(0, baseURL.lastIndexOf('/') + 1);
|
||||
|
||||
// Set base URI to the assets/www folder
|
||||
view.loadDataWithBaseURL(
|
||||
baseURL, content, "text/html", "UTF-8", null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the WebView components which will call the Google Cloud Print
|
||||
* Service.
|
||||
*
|
||||
* @param props
|
||||
* The JSON object with the containing page properties
|
||||
*/
|
||||
private void initWebView (JSONObject props) {
|
||||
Activity ctx = cordova.getActivity();
|
||||
view = new WebView(ctx);
|
||||
WebSettings settings = view.getSettings();
|
||||
final boolean jsEnabled = props.optBoolean("javascript", false);
|
||||
|
||||
settings.setDatabaseEnabled(true);
|
||||
settings.setGeolocationEnabled(true);
|
||||
settings.setSaveFormData(true);
|
||||
settings.setUseWideViewPort(true);
|
||||
if (jsEnabled) {
|
||||
settings.setJavaScriptEnabled(jsEnabled);
|
||||
}
|
||||
view.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
Method setMixedContentModeMethod = Meta.getMethod(
|
||||
settings.getClass(), "setMixedContentMode", int.class);
|
||||
|
||||
Meta.invokeMethod(settings, setMixedContentModeMethod, 2);
|
||||
}
|
||||
|
||||
setWebViewClient(props);
|
||||
}
|
||||
|
||||
private void do_print(WebView webView, 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);
|
||||
final String duplex = props.optString("duplex", "none");
|
||||
|
||||
PrintAttributes.Builder builder = new PrintAttributes.Builder();
|
||||
PrintDocumentAdapter adapter = getAdapter(webView, docName);
|
||||
|
||||
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);
|
||||
|
||||
if (!duplex.equals("none") && Build.VERSION.SDK_INT >= 23) {
|
||||
boolean longEdge = duplex.equals("long");
|
||||
Method setDuplexModeMethod = Meta.getMethod(
|
||||
builder.getClass(), "setDuplexMode", int.class);
|
||||
|
||||
Meta.invokeMethod(builder, setDuplexModeMethod,
|
||||
longEdge ? 2 : 4);
|
||||
}
|
||||
|
||||
pm.getInstance().print(docName, adapter, builder.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the web view client which sets the print document.
|
||||
*
|
||||
* @param props
|
||||
* The JSON object with the containing page properties
|
||||
*/
|
||||
private void setWebViewClient (JSONObject props) {
|
||||
view.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading (WebView view, String url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished (WebView webView, String url) {
|
||||
do_print(webView, props);
|
||||
view = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the callback send with `print` to inform about the result.
|
||||
*
|
||||
* @param job The print job.
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void notifyAboutPrintJobResult(PrintJob job) {
|
||||
|
||||
if (job == null || command == null ||
|
||||
job.getInfo().getState() <= STATE_STARTED) {
|
||||
return;
|
||||
}
|
||||
private void check (@Nullable String item, CallbackContext callback)
|
||||
{
|
||||
cordova.getThreadPool().execute(() -> {
|
||||
PrintManager pm = new PrintManager(cordova.getContext());
|
||||
boolean printable = pm.canPrintItem(item);
|
||||
|
||||
PluginResult res = new PluginResult(
|
||||
PluginResult.Status.OK, job.isCompleted());
|
||||
PluginResult.Status.OK, printable);
|
||||
|
||||
command.sendPluginResult(res);
|
||||
callback.sendPluginResult(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* List of all printable document types (utis).
|
||||
*
|
||||
* @param webView
|
||||
* The web view which content to print out.
|
||||
* @param docName
|
||||
* The name of the printed document.
|
||||
* @return
|
||||
* The created adapter.
|
||||
* @param callback The plugin function to invoke with the result.
|
||||
*/
|
||||
private PrintDocumentAdapter getAdapter (WebView webView, String docName) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
Method createPrintDocumentAdapterMethod = Meta.getMethod(
|
||||
WebView.class, "createPrintDocumentAdapter", String.class);
|
||||
private void utis (CallbackContext callback)
|
||||
{
|
||||
cordova.getThreadPool().execute(() -> {
|
||||
JSONArray utis = PrintManager.getPrintableUTIs();
|
||||
|
||||
return (PrintDocumentAdapter) Meta.invokeMethod(
|
||||
webView, createPrintDocumentAdapterMethod, docName);
|
||||
} else {
|
||||
return (PrintDocumentAdapter) Meta.invokeMethod(webView,
|
||||
"createPrintDocumentAdapter");
|
||||
}
|
||||
PluginResult res = new PluginResult(
|
||||
PluginResult.Status.OK, utis);
|
||||
|
||||
callback.sendPluginResult(res);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after plugin construction and fields have been initialized.
|
||||
*/
|
||||
@Override
|
||||
protected void pluginInitialize() {
|
||||
super.pluginInitialize();
|
||||
pm = new PrintManager(cordova.getActivity());
|
||||
pm.setOnPrintJobStateChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
_webView = webView;
|
||||
}
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if(pm != null && listener != null && command != null && view != null) {
|
||||
pm.unsetOnPrintJobStateChangeListener();
|
||||
|
||||
pm = null;
|
||||
listener = null;
|
||||
command = null;
|
||||
view = null;
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the callback from `pick` method.
|
||||
* Sends the provided content to the printing controller and opens
|
||||
* them.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to
|
||||
* startActivityForResult(), allowing you to
|
||||
* identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child
|
||||
* activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the
|
||||
* caller (various data can be
|
||||
* @param content The content or file to print.
|
||||
* @param settings Additional settings how to render the content.
|
||||
* @param callback The plugin function to invoke with the result.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
|
||||
if (command == null || intent == null) {
|
||||
return;
|
||||
private void print (@Nullable String content, JSONObject settings,
|
||||
CallbackContext callback)
|
||||
{
|
||||
cordova.getThreadPool().execute(() -> {
|
||||
PrintManager pm = new PrintManager(cordova.getContext());
|
||||
pm.print(content, settings, callback::success);
|
||||
});
|
||||
}
|
||||
|
||||
if (!intent.getAction().equals(ACTION_SELECT_PRINTER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PrinterId printer = intent.getParcelableExtra(EXTRA_PRINTER_ID);
|
||||
|
||||
PluginResult res = new PluginResult(PluginResult.Status.OK,
|
||||
printer != null ? printer.getLocalId() : null);
|
||||
|
||||
command.sendPluginResult(res);
|
||||
}
|
||||
}
|
||||
|
@ -1,257 +0,0 @@
|
||||
/*
|
||||
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 android.os.Build;
|
||||
import android.print.PrintJob;
|
||||
import android.print.PrintJobId;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import de.appplant.cordova.plugin.printer.reflect.Meta;
|
||||
|
||||
public final class PrintManager {
|
||||
|
||||
public interface OnPrintJobStateChangeListener {
|
||||
/**
|
||||
* Callback notifying that a print job state changed.
|
||||
*
|
||||
* @param job The print job.
|
||||
*/
|
||||
void onPrintJobStateChanged(PrintJob job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Required to be able to register a listener of hidden interface.
|
||||
*/
|
||||
private class OnPrintJobStateChangeProxy implements InvocationHandler {
|
||||
@Override
|
||||
public Object invoke (Object o, Method method, Object[] objects)
|
||||
throws Throwable {
|
||||
|
||||
if (method.getName().equals("hashCode")) {
|
||||
return listener.get().hashCode();
|
||||
}
|
||||
|
||||
if (method.getName().equals("onPrintJobStateChanged")) {
|
||||
notifyOnPrintJobStateChanged((PrintJobId) objects[0]);
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The application context.
|
||||
*/
|
||||
private WeakReference<Context> ctx;
|
||||
|
||||
/**
|
||||
* The registered listener for the state change event.
|
||||
*/
|
||||
private WeakReference<OnPrintJobStateChangeListener> listener;
|
||||
|
||||
/**
|
||||
* The proxy wrapper of the listener.
|
||||
*/
|
||||
private Object proxy;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param context The context where to look for.
|
||||
*/
|
||||
public PrintManager (Context context) {
|
||||
this.ctx = new WeakReference<Context>(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an instance from PrintManager service.
|
||||
*
|
||||
* @return A PrintManager instance.
|
||||
*/
|
||||
public final android.print.PrintManager getInstance () {
|
||||
return (android.print.PrintManager)
|
||||
ctx.get().getSystemService(Context.PRINT_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of installed print services.
|
||||
*
|
||||
* @return The found service list or an empty list.
|
||||
*/
|
||||
public final List<PrintServiceInfo> getInstalledPrintServices () {
|
||||
List printers;
|
||||
|
||||
if (Build.VERSION.SDK_INT < 24) {
|
||||
printers = (List) Meta.invokeMethod(getInstance(),
|
||||
"getInstalledPrintServices");
|
||||
} else {
|
||||
Method getPrintServicesMethod = Meta.getMethod(
|
||||
getInstance().getClass(), "getPrintServices", int.class);
|
||||
|
||||
printers = (List) Meta.invokeMethod(getInstance(),
|
||||
getPrintServicesMethod, 3);
|
||||
}
|
||||
|
||||
ArrayList<PrintServiceInfo> services =
|
||||
new ArrayList<PrintServiceInfo>();
|
||||
|
||||
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<PrintServiceInfo> getEnabledPrintServices () {
|
||||
List printers;
|
||||
|
||||
if (Build.VERSION.SDK_INT < 24) {
|
||||
printers = (List) Meta.invokeMethod(getInstance(),
|
||||
"getEnabledPrintServices");
|
||||
} else {
|
||||
Method getPrintServicesMethod = Meta.getMethod(
|
||||
getInstance().getClass(), "getPrintServices", int.class);
|
||||
|
||||
printers = (List) Meta.invokeMethod(getInstance(),
|
||||
getPrintServicesMethod, 1);
|
||||
}
|
||||
|
||||
ArrayList<PrintServiceInfo> services =
|
||||
new ArrayList<PrintServiceInfo>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for observing the state of print jobs.
|
||||
*
|
||||
* @param listener The listener to add.
|
||||
*/
|
||||
public void setOnPrintJobStateChangeListener(
|
||||
OnPrintJobStateChangeListener listener) {
|
||||
|
||||
if (this.listener == listener)
|
||||
return;
|
||||
|
||||
if (listener == null) {
|
||||
unsetOnPrintJobStateChangeListener();
|
||||
return;
|
||||
}
|
||||
|
||||
Class<?> interfaceCls = Meta.getClass(
|
||||
"android.print.PrintManager$PrintJobStateChangeListener");
|
||||
|
||||
if (interfaceCls == null)
|
||||
return;
|
||||
|
||||
this.listener =
|
||||
new WeakReference<OnPrintJobStateChangeListener>(listener);
|
||||
|
||||
Method method = Meta.getMethod(getInstance().getClass(),
|
||||
"addPrintJobStateChangeListener", interfaceCls);
|
||||
|
||||
Class<?>[] interfaces = {interfaceCls};
|
||||
|
||||
proxy = Proxy.newProxyInstance(
|
||||
interfaceCls.getClassLoader(),
|
||||
interfaces,
|
||||
new OnPrintJobStateChangeProxy()
|
||||
);
|
||||
|
||||
Meta.invokeMethod(getInstance(), method, proxy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the listener from the observing the state of print jobs.
|
||||
*/
|
||||
public void unsetOnPrintJobStateChangeListener() {
|
||||
Class<?> interfaceCls = Meta.getClass(
|
||||
"android.print.PrintManager$PrintJobStateChangeListener");
|
||||
|
||||
if (interfaceCls == null || proxy == null)
|
||||
return;
|
||||
|
||||
Method method = Meta.getMethod(getInstance().getClass(),
|
||||
"removePrintJobStateChangeListener", interfaceCls);
|
||||
|
||||
Meta.invokeMethod(getInstance(), method, proxy);
|
||||
|
||||
proxy = null;
|
||||
listener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback notifying that a print job state changed.
|
||||
*
|
||||
* @param printJobId The print job id.
|
||||
*/
|
||||
private void notifyOnPrintJobStateChanged(PrintJobId printJobId) {
|
||||
if (listener != null && listener.get() != null) {
|
||||
Method method = Meta.getMethod(getInstance().getClass(),
|
||||
"getPrintJob", PrintJobId.class);
|
||||
|
||||
PrintJob job = (PrintJob) Meta.invokeMethod(getInstance(),
|
||||
method, printJobId);
|
||||
|
||||
listener.get().onPrintJobStateChanged(job);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
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 accessibility service id.
|
||||
*
|
||||
* @return @return The id.
|
||||
*/
|
||||
public final String getId() {
|
||||
return (String) Meta.invokeMethod(obj, "getId");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
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<PrinterInfo> 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")) {
|
||||
notifyOnPrintersChanged();
|
||||
return null;
|
||||
} else throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
private 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) {
|
||||
|
||||
Object proxy = null;
|
||||
Class<?> interfaceCls = Meta.getClass(
|
||||
"android.print.PrinterDiscoverySession$OnPrintersChangeListener");
|
||||
|
||||
if (interfaceCls == null)
|
||||
return;
|
||||
|
||||
this.listener = listener;
|
||||
|
||||
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() {
|
||||
stopPrinterDiscovery();
|
||||
setOnPrintersChangeListener(null);
|
||||
Meta.invokeMethod(session, "destroy");
|
||||
session = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all yet discovered printers.
|
||||
*
|
||||
* @return List of their basic infos
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<PrinterInfo> getPrinters() {
|
||||
Method method = Meta.getMethod(session.getClass(), "getPrinters");
|
||||
|
||||
return (List<PrinterInfo>) Meta.invokeMethod(session, method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the listener about the occurred event.
|
||||
*/
|
||||
private void notifyOnPrintersChanged() {
|
||||
if (listener != null) {
|
||||
listener.onPrintersChanged(getPrinters());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Tries to find the class for the given name.
|
||||
*
|
||||
* @param fullName
|
||||
* The full class name including the package scope.
|
||||
* @return
|
||||
* The found class or null.
|
||||
*/
|
||||
public static Class<?> getClass (String fullName) {
|
||||
try {
|
||||
return Class.forName(fullName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
e.printStackTrace();
|
||||
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);
|
||||
|
||||
if (resId == 0) {
|
||||
resId = Resources.getSystem().getIdentifier(name, type, "android");
|
||||
}
|
||||
|
||||
return resId;
|
||||
}
|
||||
}
|
@ -1,361 +0,0 @@
|
||||
/*
|
||||
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.Build;
|
||||
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";
|
||||
|
||||
/**
|
||||
* The action of the intent.
|
||||
*/
|
||||
public static final String ACTION_SELECT_PRINTER =
|
||||
"ACTION_SELECT_PRINTER";
|
||||
|
||||
/**
|
||||
* Reference to the main view which lists all discovered printers.
|
||||
*/
|
||||
private ListView listView;
|
||||
|
||||
/**
|
||||
* Session for printer discovering within the network.
|
||||
*/
|
||||
private PrinterDiscoverySession session;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
setTheme(Meta.getResId(
|
||||
this, "style", "Theme.Material.Settings"));
|
||||
}
|
||||
|
||||
setContentView(Meta.getResId(
|
||||
this, "layout", "select_printer_activity"));
|
||||
|
||||
session = 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() {
|
||||
session.destroy();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent intent with empty result.
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
onPrinterSelected(null);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
intent.setAction(ACTION_SELECT_PRINTER);
|
||||
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;
|
||||
|
||||
session.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 (session.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<PrinterInfo> printers = new ArrayList<PrinterInfo>();
|
||||
|
||||
/**
|
||||
* Constructor registers a listener to monitor about newly discovered
|
||||
* printers.
|
||||
*/
|
||||
ListViewAdapter() {
|
||||
session.setOnPrintersChangeListener(new OnPrintersChangeListener() {
|
||||
@Override
|
||||
public void onPrintersChanged (List<PrinterInfo> 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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user