@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.
Files changed (32) hide show
  1. package/NitraZebra.podspec +16 -13
  2. package/Package.swift +28 -0
  3. package/README.md +112 -0
  4. package/android/build.gradle +16 -14
  5. package/android/src/main/AndroidManifest.xml +4 -21
  6. package/android/src/main/java/dev/nitra/zebra/Zebra.java +227 -0
  7. package/android/src/main/java/dev/nitra/zebra/ZebraPlugin.java +128 -0
  8. package/dist/index.d.ts +26 -0
  9. package/dist/plugin.js +1080 -1295
  10. package/dist/plugin.js.map +1 -1
  11. package/ios/Sources/ZebraPlugin/Zebra.swift +8 -0
  12. package/ios/Sources/ZebraPlugin/ZebraPlugin.swift +23 -0
  13. package/ios/Tests/ZebraPluginTests/ZebraTests.swift +15 -0
  14. package/package.json +49 -54
  15. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  16. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  17. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  18. package/android/.gradle/8.9/gc.properties +0 -0
  19. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  20. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  21. package/android/.gradle/vcs-1/gc.properties +0 -0
  22. package/android/.project +0 -28
  23. package/android/capacitor.settings.gradle +0 -3
  24. package/android/gradle.properties +0 -19
  25. package/android/proguard-rules.pro +0 -21
  26. package/android/src/main/java/com/nitra/zebra_printer_plugin/ZebraPrinter.java +0 -1418
  27. package/android/src/main/res/xml/capacitor_plugins.xml +0 -4
  28. package/android/variables.gradle +0 -14
  29. package/ios/Info.plist +0 -36
  30. package/ios/Plugin/ZebraPrinterPlugin.m +0 -16
  31. package/ios/Plugin/ZebraPrinterPlugin.swift +0 -465
  32. /package/android/{.gradle/8.9/dependencies-accessors/gc.properties → src/main/res/.gitkeep} +0 -0
@@ -1,14 +1,17 @@
1
- Pod::Spec.new do |spec|
2
- spec.name = 'NitraZebra'
3
- spec.version = '6.1.5'
4
- spec.summary = 'Nitra Zebra Components for Capacitor'
5
- spec.license = 'ISC'
6
- spec.homepage = 'https://github.com/nitra/zebra'
7
- spec.author = 'v@nitra.ai'
8
- spec.source = { :git => 'https://github.com/nitra/zebra.git', :tag => 'v6.1.5' }
9
- spec.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
10
- spec.ios.deployment_target = '14.0'
11
- spec.dependency 'Capacitor', '~> 7.4.0'
12
- spec.swift_version = '5.1'
13
- spec.frameworks = 'CoreBluetooth'
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
@@ -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.7.2'
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 "com.nitra.zebra_printer_plugin"
19
- compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
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
22
- targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
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.VERSION_1_8
38
- targetCompatibility JavaVersion.VERSION_1_8
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
- <?xml version="1.0" encoding="utf-8"?>
2
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
- package="com.nitra.zebra_printer_plugin">
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
- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
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
+ }
@@ -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 };