@nitra/zebra 6.2.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NitraZebra.podspec +16 -13
- package/Package.swift +28 -0
- package/README.md +112 -0
- package/android/build.gradle +16 -14
- package/android/src/main/AndroidManifest.xml +4 -21
- package/android/src/main/java/dev/nitra/zebra/Zebra.java +227 -0
- package/android/src/main/java/dev/nitra/zebra/ZebraPlugin.java +128 -0
- package/dist/index.d.ts +26 -0
- package/dist/plugin.js +1080 -1295
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/ZebraPlugin/Zebra.swift +8 -0
- package/ios/Sources/ZebraPlugin/ZebraPlugin.swift +23 -0
- package/ios/Tests/ZebraPluginTests/ZebraTests.swift +15 -0
- package/package.json +49 -54
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/.project +0 -28
- package/android/capacitor.settings.gradle +0 -3
- package/android/gradle.properties +0 -19
- package/android/proguard-rules.pro +0 -21
- package/android/src/main/java/com/nitra/zebra_printer_plugin/ZebraPrinter.java +0 -1418
- package/android/src/main/res/xml/capacitor_plugins.xml +0 -4
- package/android/variables.gradle +0 -14
- package/ios/Info.plist +0 -36
- package/ios/Plugin/ZebraPrinterPlugin.m +0 -16
- package/ios/Plugin/ZebraPrinterPlugin.swift +0 -465
- /package/android/{.gradle/8.9/dependencies-accessors/gc.properties → src/main/res/.gitkeep} +0 -0
package/NitraZebra.podspec
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'NitraZebra'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.license = package['license']
|
|
10
|
+
s.homepage = package['repository']['url']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
|
|
13
|
+
s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
|
|
14
|
+
s.ios.deployment_target = '15.0'
|
|
15
|
+
s.dependency 'Capacitor'
|
|
16
|
+
s.swift_version = '5.1'
|
|
14
17
|
end
|
package/Package.swift
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// swift-tools-version: 5.9
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "NitraZebra",
|
|
6
|
+
platforms: [.iOS(.v15)],
|
|
7
|
+
products: [
|
|
8
|
+
.library(
|
|
9
|
+
name: "NitraZebra",
|
|
10
|
+
targets: ["ZebraPlugin"])
|
|
11
|
+
],
|
|
12
|
+
dependencies: [
|
|
13
|
+
.package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
|
|
14
|
+
],
|
|
15
|
+
targets: [
|
|
16
|
+
.target(
|
|
17
|
+
name: "ZebraPlugin",
|
|
18
|
+
dependencies: [
|
|
19
|
+
.product(name: "Capacitor", package: "capacitor-swift-pm"),
|
|
20
|
+
.product(name: "Cordova", package: "capacitor-swift-pm")
|
|
21
|
+
],
|
|
22
|
+
path: "ios/Sources/ZebraPlugin"),
|
|
23
|
+
.testTarget(
|
|
24
|
+
name: "ZebraPluginTests",
|
|
25
|
+
dependencies: ["ZebraPlugin"],
|
|
26
|
+
path: "ios/Tests/ZebraPluginTests")
|
|
27
|
+
]
|
|
28
|
+
)
|
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @nitra/zebra
|
|
2
|
+
|
|
3
|
+
Capacitor-плагін для друку ZPL на принтерах Zebra з підтримкою веб (Web Serial API), iOS та Android.
|
|
4
|
+
|
|
5
|
+
## Вимоги
|
|
6
|
+
|
|
7
|
+
- **Capacitor** 7.0.0 або новіший
|
|
8
|
+
- **Веб**: браузер з підтримкою [Web Serial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API) (Chrome, Edge, Opera)
|
|
9
|
+
- **Android**: minSdk 23, targetSdk 35
|
|
10
|
+
- **iOS**: 14.0+
|
|
11
|
+
|
|
12
|
+
## Встановлення
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @nitra/zebra
|
|
16
|
+
npx cap sync
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## API
|
|
20
|
+
|
|
21
|
+
### `print(zpl: string | PrintOptions): Promise<{ sent?: boolean }>`
|
|
22
|
+
|
|
23
|
+
Відправляє ZPL-команду на принтер Zebra.
|
|
24
|
+
|
|
25
|
+
| Платформа | Поведінка |
|
|
26
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| **Web** | Запитує порт через Web Serial API, відкриває з'єднання (9600 baud) і відправляє рядок ZPL на вибраний пристрій. |
|
|
28
|
+
| **iOS** | Нативна реалізація (через плагін). |
|
|
29
|
+
| **Android** | Відправка ZPL по **Bluetooth Classic (SPP)**. Потрібно спочатку викликати `setPrinterAddress({ address: "MAC" })` або передати `address` (Bluetooth MAC) у виклику. |
|
|
30
|
+
|
|
31
|
+
**Параметри (об'єкт для Android):**
|
|
32
|
+
|
|
33
|
+
- `zpl` — рядок ZPL-команд (наприклад, `^XA^FO50,50^A0N,28,28^FDHello^FS^XZ`).
|
|
34
|
+
- `address` — (Android, опційно) Bluetooth MAC-адреса принтера (наприклад `00:11:22:33:44:55`); якщо не передано, використовується збережена через `setPrinterAddress()`.
|
|
35
|
+
- `port` — ігнорується (для сумісності API).
|
|
36
|
+
|
|
37
|
+
**Приклад:**
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
import { Zebra } from "@nitra/zebra";
|
|
41
|
+
|
|
42
|
+
const zpl = "^XA^FO50,50^A0N,28,28^FDHello World^FS^XZ";
|
|
43
|
+
await Zebra.print(zpl);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `setPrinterAddress(options: { address: string, port?: number }): Promise<{ address }>` (Android)
|
|
47
|
+
|
|
48
|
+
Зберігає Bluetooth MAC-адресу принтера Zebra для подальших викликів `print(zpl)` без передачі адреси.
|
|
49
|
+
|
|
50
|
+
**Параметри:**
|
|
51
|
+
|
|
52
|
+
- `address` — Bluetooth MAC-адреса принтера (наприклад `00:11:22:33:44:55`).
|
|
53
|
+
- `port` — ігнорується (для сумісності API).
|
|
54
|
+
|
|
55
|
+
**Приклад (Android):**
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
await Zebra.setPrinterAddress({ address: "00:11:22:33:44:55" });
|
|
59
|
+
await Zebra.print(zpl);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### `getPairedDevices(): Promise<{ devices: { address: string, name: string }[] }>` (Android)
|
|
63
|
+
|
|
64
|
+
Повертає список спарених Bluetooth-пристроїв. Корисно для вибору принтера в налаштуваннях. На Android 12+ потрібен дозвіл **BLUETOOTH_CONNECT** (запитати до виклику).
|
|
65
|
+
|
|
66
|
+
**Приклад (Android):**
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
const { devices } = await Zebra.getPairedDevices();
|
|
70
|
+
// devices: [{ address: "00:11:22:33:44:55", name: "Zebra ZD420" }, ...]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Веб (Web Serial API)
|
|
74
|
+
|
|
75
|
+
На веб-платформі плагін використовує Web Serial API:
|
|
76
|
+
|
|
77
|
+
1. При виклику `print()` користувачу показується діалог вибору послідовного порту (принтер, підключений через USB).
|
|
78
|
+
2. Відкривається з'єднання з параметрами: 9600 baud, 8 data bits, no parity, 1 stop bit.
|
|
79
|
+
3. ZPL-рядок відправляється на пристрій.
|
|
80
|
+
|
|
81
|
+
**Обмеження:** Web Serial API працює лише в підтримуваних браузерах (Chrome, Edge, Opera) і зазвичай лише через HTTPS або localhost.
|
|
82
|
+
|
|
83
|
+
## Android (Bluetooth Classic)
|
|
84
|
+
|
|
85
|
+
На Android плагін відправляє ZPL на принтер Zebra по **Bluetooth Classic** (профіль SPP):
|
|
86
|
+
|
|
87
|
+
1. Спаріть принтер у налаштуваннях Bluetooth пристрою.
|
|
88
|
+
2. Викличте `getPairedDevices()` щоб отримати список пристроїв (опційно) або введіть MAC-адресу вручну.
|
|
89
|
+
3. Викличте `setPrinterAddress({ address: "MAC_ПРИНТЕРА" })` один раз (наприклад, з налаштувань) або передайте `address` у виклику `print({ zpl, address })`.
|
|
90
|
+
4. Викличте `print(zpl)` або `print({ zpl })` — ZPL буде відправлено на принтер по Bluetooth.
|
|
91
|
+
|
|
92
|
+
Потрібні дозволи **BLUETOOTH**, **BLUETOOTH_ADMIN** (до API 30), **BLUETOOTH_CONNECT** (API 31+). На Android 12+ дозвіл `BLUETOOTH_CONNECT` потрібно запитати під час виконання (наприклад перед `getPairedDevices()` або `print()`).
|
|
93
|
+
|
|
94
|
+
## Структура пакету
|
|
95
|
+
|
|
96
|
+
- `dist/` — зібраний JS-плагін (rolldown)
|
|
97
|
+
- `src/` — вихідний код: `index.js`, `definitions.js`, `web.js` (Web Serial реалізація)
|
|
98
|
+
- `ios/` — нативний iOS-плагін (Swift)
|
|
99
|
+
- `android/` — нативний Android-плагін (Java)
|
|
100
|
+
|
|
101
|
+
## Скрипти
|
|
102
|
+
|
|
103
|
+
| Команда | Опис |
|
|
104
|
+
| ---------------- | ----------------------------------------------- |
|
|
105
|
+
| `npm run build` | Збірка плагіну (rolldown) |
|
|
106
|
+
| `npm run verify` | Перевірка iOS, Android та веб-збірки |
|
|
107
|
+
| `npm run fmt` | Форматування коду (ESLint, Prettier, SwiftLint) |
|
|
108
|
+
| `npm run lint` | Перевірка стилю коду |
|
|
109
|
+
|
|
110
|
+
## Ліцензія
|
|
111
|
+
|
|
112
|
+
MIT
|
package/android/build.gradle
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
buildscript {
|
|
2
9
|
repositories {
|
|
3
10
|
google()
|
|
4
11
|
mavenCentral()
|
|
5
|
-
maven {
|
|
6
|
-
url "https://plugins.gradle.org/m2/"
|
|
7
|
-
}
|
|
8
12
|
}
|
|
9
13
|
dependencies {
|
|
10
|
-
classpath 'com.android.tools.build:gradle:8.
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
11
15
|
}
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
apply plugin: 'com.android.library'
|
|
15
|
-
apply from: 'variables.gradle'
|
|
16
19
|
|
|
17
20
|
android {
|
|
18
|
-
namespace "
|
|
19
|
-
|
|
21
|
+
namespace = "dev.nitra.zebra"
|
|
22
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
20
23
|
defaultConfig {
|
|
21
|
-
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion :
|
|
22
|
-
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion :
|
|
24
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
25
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
23
26
|
versionCode 1
|
|
24
27
|
versionName "1.0"
|
|
25
28
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
@@ -31,11 +34,11 @@ android {
|
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
lintOptions {
|
|
34
|
-
abortOnError false
|
|
37
|
+
abortOnError = false
|
|
35
38
|
}
|
|
36
39
|
compileOptions {
|
|
37
|
-
sourceCompatibility JavaVersion.
|
|
38
|
-
targetCompatibility JavaVersion.
|
|
40
|
+
sourceCompatibility JavaVersion.VERSION_21
|
|
41
|
+
targetCompatibility JavaVersion.VERSION_21
|
|
39
42
|
}
|
|
40
43
|
}
|
|
41
44
|
|
|
@@ -44,13 +47,12 @@ repositories {
|
|
|
44
47
|
mavenCentral()
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
|
|
47
51
|
dependencies {
|
|
48
52
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
49
53
|
implementation project(':capacitor-android')
|
|
50
54
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
51
|
-
implementation "androidx.core:core:$androidxCoreVersion"
|
|
52
55
|
testImplementation "junit:junit:$junitVersion"
|
|
53
56
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
54
57
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
55
58
|
}
|
|
56
|
-
|
|
@@ -1,22 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
<!-- Bluetooth permissions -->
|
|
6
|
-
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
7
|
-
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
|
3
|
+
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
|
8
4
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<!-- Location permissions (required for BLE scanning on Android) -->
|
|
12
|
-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
13
|
-
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
14
|
-
|
|
15
|
-
<!-- Wi-Fi info (SSID) on Android 13+ -->
|
|
16
|
-
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
|
|
17
|
-
|
|
18
|
-
<!-- Feature declarations -->
|
|
19
|
-
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
|
|
20
|
-
<uses-feature android:name="android.hardware.bluetooth" android:required="true" />
|
|
21
|
-
|
|
22
|
-
</manifest>
|
|
5
|
+
</manifest>
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
package dev.nitra.zebra;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.bluetooth.BluetoothAdapter;
|
|
5
|
+
import android.bluetooth.BluetoothDevice;
|
|
6
|
+
import android.bluetooth.BluetoothSocket;
|
|
7
|
+
import android.content.Context;
|
|
8
|
+
import android.content.SharedPreferences;
|
|
9
|
+
import android.content.pm.PackageManager;
|
|
10
|
+
import android.os.Build;
|
|
11
|
+
|
|
12
|
+
import androidx.core.app.ActivityCompat;
|
|
13
|
+
|
|
14
|
+
import com.getcapacitor.Logger;
|
|
15
|
+
|
|
16
|
+
import java.io.IOException;
|
|
17
|
+
import java.io.OutputStream;
|
|
18
|
+
import java.nio.charset.StandardCharsets;
|
|
19
|
+
import java.util.ArrayList;
|
|
20
|
+
import java.util.List;
|
|
21
|
+
import java.util.Set;
|
|
22
|
+
import java.util.UUID;
|
|
23
|
+
import java.util.concurrent.ExecutorService;
|
|
24
|
+
import java.util.concurrent.Executors;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Реалізація друку ZPL на принтері Zebra через Bluetooth Classic (SPP).
|
|
28
|
+
* Зберігає MAC-адресу принтера та відправляє ZPL по SPP.
|
|
29
|
+
*/
|
|
30
|
+
public class Zebra {
|
|
31
|
+
|
|
32
|
+
/** UUID профілю SPP (Serial Port Profile) для Bluetooth Classic. */
|
|
33
|
+
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
|
|
34
|
+
|
|
35
|
+
private static final String PREFS_NAME = "ZebraPrinter";
|
|
36
|
+
private static final String KEY_ADDRESS = "printer_address";
|
|
37
|
+
|
|
38
|
+
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Зберігає Bluetooth MAC-адресу принтера для подальшого використання в print().
|
|
42
|
+
*
|
|
43
|
+
* @param address MAC-адреса пристрою (наприклад "00:11:22:33:44:55")
|
|
44
|
+
*/
|
|
45
|
+
public void setPrinterAddress(Context context, String address, int port) {
|
|
46
|
+
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
47
|
+
String normalized = address != null ? address.trim() : "";
|
|
48
|
+
prefs.edit().putString(KEY_ADDRESS, normalized).apply();
|
|
49
|
+
Logger.info("Zebra", "Printer address saved: " + normalized);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Повертає збережену MAC-адресу принтера або null.
|
|
54
|
+
*/
|
|
55
|
+
public String getStoredAddress(Context context) {
|
|
56
|
+
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
|
57
|
+
return prefs.getString(KEY_ADDRESS, null);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Повертає список спарених Bluetooth-пристроїв (для вибору принтера).
|
|
62
|
+
*/
|
|
63
|
+
public List<BluetoothDeviceInfo> getPairedDevices(Context context) {
|
|
64
|
+
List<BluetoothDeviceInfo> result = new ArrayList<>();
|
|
65
|
+
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
66
|
+
if (adapter == null) {
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
if (!adapter.isEnabled()) {
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
Set<BluetoothDevice> paired = adapter.getBondedDevices();
|
|
76
|
+
if (paired == null) {
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
for (BluetoothDevice device : paired) {
|
|
80
|
+
String name = device.getName();
|
|
81
|
+
String address = device.getAddress();
|
|
82
|
+
if (address != null && !address.isEmpty()) {
|
|
83
|
+
result.add(new BluetoothDeviceInfo(
|
|
84
|
+
address,
|
|
85
|
+
name != null ? name : ""
|
|
86
|
+
));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Відправляє ZPL на принтер Zebra по Bluetooth Classic (SPP).
|
|
94
|
+
*
|
|
95
|
+
* @param address MAC-адреса принтера (якщо null, використовується збережена адреса)
|
|
96
|
+
* @param port ігнорується (для сумісності API)
|
|
97
|
+
* @param zpl рядок ZPL для друку
|
|
98
|
+
* @param callback результат успіху або помилки
|
|
99
|
+
*/
|
|
100
|
+
public void printZpl(Context context, String address, int port, String zpl, PrintCallback callback) {
|
|
101
|
+
final String addr = (address != null && !address.isEmpty()) ? address.trim() : getStoredAddress(context);
|
|
102
|
+
|
|
103
|
+
if (addr == null || addr.isEmpty()) {
|
|
104
|
+
callback.onError("ADDRESS_MISSING", new IllegalArgumentException("Адреса принтера не вказана. Викличте setPrinterAddress() з Bluetooth MAC або передайте address у print()."));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
String zplTrimmed = zpl != null ? zpl.trim() : "";
|
|
109
|
+
if (zplTrimmed.isEmpty()) {
|
|
110
|
+
callback.onError("ZPL_EMPTY", new IllegalArgumentException("ZPL команда порожня."));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
executor.execute(() -> {
|
|
115
|
+
BluetoothSocket socket = null;
|
|
116
|
+
OutputStream out = null;
|
|
117
|
+
try {
|
|
118
|
+
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
119
|
+
if (adapter == null) {
|
|
120
|
+
callback.onError("BLUETOOTH_NOT_SUPPORTED", new IllegalStateException("Bluetooth не підтримується на цьому пристрої."));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (!adapter.isEnabled()) {
|
|
124
|
+
callback.onError("BLUETOOTH_DISABLED", new IllegalStateException("Увімкніть Bluetooth в налаштуваннях."));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
|
129
|
+
callback.onError("PERMISSION_DENIED", new SecurityException("Відсутній дозвіл BLUETOOTH_CONNECT"));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
BluetoothDevice device;
|
|
134
|
+
try {
|
|
135
|
+
device = adapter.getRemoteDevice(addr);
|
|
136
|
+
} catch (IllegalArgumentException e) {
|
|
137
|
+
callback.onError("INVALID_ADDRESS", new IllegalArgumentException("Некоректна MAC-адреса: " + addr));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
socket = device.createRfcommSocketToServiceRecord(SPP_UUID);
|
|
143
|
+
socket.connect();
|
|
144
|
+
} catch (IOException e) {
|
|
145
|
+
// Частина пристроїв (зокрема Zebra) потребує fallback через RFCOMM канал 1
|
|
146
|
+
Logger.info("Zebra", "SPP UUID connect failed, trying fallback RFCOMM channel 1");
|
|
147
|
+
if (socket != null) {
|
|
148
|
+
try {
|
|
149
|
+
socket.close();
|
|
150
|
+
} catch (IOException ignored) {
|
|
151
|
+
}
|
|
152
|
+
socket = null;
|
|
153
|
+
}
|
|
154
|
+
socket = createRfcommSocket(device, 1);
|
|
155
|
+
socket.connect();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
out = socket.getOutputStream();
|
|
159
|
+
byte[] data = zplTrimmed.getBytes(StandardCharsets.UTF_8);
|
|
160
|
+
out.write(data);
|
|
161
|
+
out.flush();
|
|
162
|
+
|
|
163
|
+
Logger.info("Zebra", "ZPL sent successfully (" + data.length + " bytes) to " + addr);
|
|
164
|
+
callback.onSuccess(addr, data.length);
|
|
165
|
+
} catch (IOException e) {
|
|
166
|
+
Logger.error("Zebra", "Print failed: " + e.getMessage(), e);
|
|
167
|
+
callback.onError("CONNECTION_FAILED", e);
|
|
168
|
+
} finally {
|
|
169
|
+
if (out != null) {
|
|
170
|
+
try {
|
|
171
|
+
out.close();
|
|
172
|
+
} catch (IOException ignored) {
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (socket != null) {
|
|
176
|
+
try {
|
|
177
|
+
socket.close();
|
|
178
|
+
} catch (IOException ignored) {
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@SuppressWarnings("JavaReflectionMemberAccess")
|
|
186
|
+
private static BluetoothSocket createRfcommSocket(BluetoothDevice device, int channel) throws IOException {
|
|
187
|
+
try {
|
|
188
|
+
return (BluetoothSocket) device.getClass()
|
|
189
|
+
.getMethod("createRfcommSocket", int.class)
|
|
190
|
+
.invoke(device, channel);
|
|
191
|
+
} catch (Exception e) {
|
|
192
|
+
throw new IOException("Fallback createRfcommSocket failed: " + e.getMessage(), e);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Інформація про Bluetooth-пристрій (для списку спарених).
|
|
198
|
+
*/
|
|
199
|
+
public static class BluetoothDeviceInfo {
|
|
200
|
+
public final String address;
|
|
201
|
+
public final String name;
|
|
202
|
+
|
|
203
|
+
public BluetoothDeviceInfo(String address, String name) {
|
|
204
|
+
this.address = address;
|
|
205
|
+
this.name = name;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Колбек результату друку.
|
|
211
|
+
*/
|
|
212
|
+
public interface PrintCallback {
|
|
213
|
+
/**
|
|
214
|
+
* Успішне відправлення даних.
|
|
215
|
+
* @param address MAC-адреса, на яку відправлено.
|
|
216
|
+
* @param bytesSent кількість відправлених байт.
|
|
217
|
+
*/
|
|
218
|
+
void onSuccess(String address, int bytesSent);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Помилка друку.
|
|
222
|
+
* @param code Короткий код помилки.
|
|
223
|
+
* @param error Об'єкт виключення з деталями.
|
|
224
|
+
*/
|
|
225
|
+
void onError(String code, Throwable error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
package dev.nitra.zebra;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import com.getcapacitor.JSObject;
|
|
5
|
+
import com.getcapacitor.PermissionState;
|
|
6
|
+
import com.getcapacitor.Plugin;
|
|
7
|
+
import com.getcapacitor.PluginCall;
|
|
8
|
+
import com.getcapacitor.PluginMethod;
|
|
9
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
10
|
+
import com.getcapacitor.annotation.Permission;
|
|
11
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
12
|
+
|
|
13
|
+
import org.json.JSONArray;
|
|
14
|
+
import org.json.JSONObject;
|
|
15
|
+
|
|
16
|
+
import java.util.List;
|
|
17
|
+
|
|
18
|
+
@CapacitorPlugin(
|
|
19
|
+
name = "Zebra",
|
|
20
|
+
permissions = {
|
|
21
|
+
@Permission(
|
|
22
|
+
strings = { Manifest.permission.BLUETOOTH_CONNECT },
|
|
23
|
+
alias = "bt_connect"
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
public class ZebraPlugin extends Plugin {
|
|
28
|
+
|
|
29
|
+
private final Zebra implementation = new Zebra();
|
|
30
|
+
|
|
31
|
+
@PluginMethod
|
|
32
|
+
public void setPrinterAddress(PluginCall call) {
|
|
33
|
+
String address = call.getString("address");
|
|
34
|
+
Integer port = call.getInt("port", 0);
|
|
35
|
+
|
|
36
|
+
if (address == null || address.trim().isEmpty()) {
|
|
37
|
+
call.reject("Параметр 'address' обов'язковий (Bluetooth MAC-адреса принтера).");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
implementation.setPrinterAddress(getContext(), address.trim(), port != null ? port : 0);
|
|
42
|
+
JSObject ret = new JSObject();
|
|
43
|
+
ret.put("address", address.trim());
|
|
44
|
+
call.resolve(ret);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@PluginMethod
|
|
48
|
+
public void getPairedDevices(PluginCall call) {
|
|
49
|
+
if (getPermissionState("bt_connect") != PermissionState.GRANTED) {
|
|
50
|
+
requestPermissionForAlias("bt_connect", call, "getPairedDevicesCallback");
|
|
51
|
+
} else {
|
|
52
|
+
loadPairedDevices(call);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@PermissionCallback
|
|
57
|
+
private void getPairedDevicesCallback(PluginCall call) {
|
|
58
|
+
if (getPermissionState("bt_connect") != PermissionState.GRANTED) {
|
|
59
|
+
call.reject("Permission is required to access Bluetooth devices.");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
loadPairedDevices(call);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
void loadPairedDevices(PluginCall call) {
|
|
66
|
+
List<Zebra.BluetoothDeviceInfo> devices = implementation.getPairedDevices(getContext());
|
|
67
|
+
JSONArray arr = new JSONArray();
|
|
68
|
+
for (Zebra.BluetoothDeviceInfo d : devices) {
|
|
69
|
+
JSONObject o = new JSONObject();
|
|
70
|
+
try {
|
|
71
|
+
o.put("address", d.address);
|
|
72
|
+
o.put("name", d.name);
|
|
73
|
+
} catch (Exception ignored) {
|
|
74
|
+
}
|
|
75
|
+
arr.put(o);
|
|
76
|
+
}
|
|
77
|
+
JSObject ret = new JSObject();
|
|
78
|
+
ret.put("devices", arr);
|
|
79
|
+
call.resolve(ret);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@PluginMethod
|
|
83
|
+
public void print(PluginCall call) {
|
|
84
|
+
// Підтримка лише print({ zpl, address?, port? })
|
|
85
|
+
String zpl = call.getString("zpl");
|
|
86
|
+
String address = call.getString("address");
|
|
87
|
+
Integer port = call.getInt("port", 0);
|
|
88
|
+
|
|
89
|
+
implementation.printZpl(
|
|
90
|
+
getContext(),
|
|
91
|
+
address,
|
|
92
|
+
port != null ? port : 0,
|
|
93
|
+
zpl != null ? zpl : "",
|
|
94
|
+
new Zebra.PrintCallback() {
|
|
95
|
+
@Override
|
|
96
|
+
public void onSuccess(String address, int bytesSent) {
|
|
97
|
+
runOnMainThread(() -> {
|
|
98
|
+
JSObject ret = new JSObject();
|
|
99
|
+
ret.put("success", true);
|
|
100
|
+
ret.put("address", address);
|
|
101
|
+
ret.put("bytesSent", bytesSent);
|
|
102
|
+
call.resolve(ret);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@Override
|
|
107
|
+
public void onError(String code, Throwable error) {
|
|
108
|
+
runOnMainThread(() -> {
|
|
109
|
+
JSObject ret = new JSObject();
|
|
110
|
+
ret.put("success", false);
|
|
111
|
+
ret.put("code", code);
|
|
112
|
+
ret.put("message", error.getMessage());
|
|
113
|
+
// Також робимо reject для стандартної обробки помилок Capacitor
|
|
114
|
+
call.reject(error.getMessage(), code, new Exception(error), ret);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private void runOnMainThread(Runnable runnable) {
|
|
122
|
+
if (getActivity() != null) {
|
|
123
|
+
getActivity().runOnUiThread(runnable);
|
|
124
|
+
} else {
|
|
125
|
+
runnable.run();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
type PairedDevice = {
|
|
3
|
+
address: string;
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
type PrintResult = {
|
|
7
|
+
success?: boolean;
|
|
8
|
+
address?: string;
|
|
9
|
+
bytesSent?: number;
|
|
10
|
+
devices?: PairedDevice[];
|
|
11
|
+
};
|
|
12
|
+
interface ZebraPlugin {
|
|
13
|
+
print(zpl: string): Promise<PrintResult>;
|
|
14
|
+
setPrinterAddress(options: {
|
|
15
|
+
address: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
address: string;
|
|
19
|
+
}>;
|
|
20
|
+
getPairedDevices(): Promise<{
|
|
21
|
+
devices: PairedDevice[];
|
|
22
|
+
}>;
|
|
23
|
+
}
|
|
24
|
+
declare const Zebra: ZebraPlugin;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { PairedDevice, PrintResult, Zebra, ZebraPlugin };
|