@rejaul/react-native-printer-kit 0.1.0
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/LICENSE +20 -0
- package/README.md +199 -0
- package/ReactNativePrinterKit.podspec +20 -0
- package/android/build.gradle +65 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/rejaul/reactnativeprinterkit/ReactNativePrinterKitModule.kt +283 -0
- package/android/src/main/java/com/rejaul/reactnativeprinterkit/ReactNativePrinterKitPackage.kt +31 -0
- package/ios/ReactNativePrinterKit.h +5 -0
- package/ios/ReactNativePrinterKit.mm +21 -0
- package/lib/module/NativeReactNativePrinterKit.js +8 -0
- package/lib/module/NativeReactNativePrinterKit.js.map +1 -0
- package/lib/module/index.js +113 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeReactNativePrinterKit.d.ts +85 -0
- package/lib/typescript/src/NativeReactNativePrinterKit.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +66 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/package.json +133 -0
- package/src/NativeReactNativePrinterKit.ts +135 -0
- package/src/index.tsx +149 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rejaul Karim
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
6
|
+
in the Software without restriction, including without limitation the rights
|
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
9
|
+
furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
20
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# react-native-printer-kit
|
|
2
|
+
|
|
3
|
+
A professional-grade React Native module for printing directly to Bluetooth POS/thermal (ESC/POS) printers on **Android devices only** — no third-party printer app, no OS print dialog. Print raw text, images, PDFs, and full HTML/CSS content, including **Bangla and other non-Latin scripts** that ESC/POS printer fonts don't support natively.
|
|
4
|
+
|
|
5
|
+
> **Important:** This library is **exclusively for Android**. Bluetooth Classic SPP + ESC/POS is an Android-specific integration; iOS does not expose the APIs this relies on.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@rejaul/react-native-printer-kit)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[]()
|
|
10
|
+
[]()
|
|
11
|
+
[]()
|
|
12
|
+
|
|
13
|
+
## Why this exists
|
|
14
|
+
|
|
15
|
+
Most ESC/POS printer libraries only send raw text or a single pre-made image. They can't render real content — an actual HTML/CSS receipt layout, a PDF, or text in a script the printer's built-in font doesn't have (Bangla, Arabic, Hindi, etc.). This library renders your HTML/PDF to a bitmap on-device (Android's own `WebView` and `PdfRenderer` — no external rendering library) and sends it to the printer as a dithered ESC/POS raster image.
|
|
16
|
+
|
|
17
|
+
It's a thin React Native bridge over [`android-printer-kit`](https://github.com/DeveloperRejaul/android-printer-kit), the native Kotlin library that does the actual work.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Direct Bluetooth Classic (SPP) connection** to any paired ESC/POS printer.
|
|
22
|
+
- **HTML → PDF → print** pipeline, so you can print an actual styled receipt/report.
|
|
23
|
+
- **PDF → image → print** pipeline via Android's built-in `PdfRenderer`.
|
|
24
|
+
- **Non-Latin text support** (Bangla and others) via image-based rendering.
|
|
25
|
+
- **Persistent connection**: the printer stays connected even if the app is swiped away from Recents (a foreground service keeps it alive).
|
|
26
|
+
- **Auto-reconnect**: remembers the last connected printer and reconnects automatically next time the app starts.
|
|
27
|
+
- **Banded raster printing**: images are sent in small, paced chunks, since many cheap ESC/POS boards silently drop large single print commands.
|
|
28
|
+
- **Type-safe**: full TypeScript definitions, backed by a New Architecture TurboModule.
|
|
29
|
+
|
|
30
|
+
## Platform Support
|
|
31
|
+
|
|
32
|
+
| Platform | Status | Details |
|
|
33
|
+
|----------|--------|---------|
|
|
34
|
+
| **Android** | **Fully Supported** | API Level 24+ required |
|
|
35
|
+
| **iOS** | **Not Supported** | No Bluetooth Classic SPP / ESC/POS API equivalent |
|
|
36
|
+
|
|
37
|
+
## Scope
|
|
38
|
+
|
|
39
|
+
This library speaks **Bluetooth Classic SPP + ESC/POS** — the standard used by virtually all budget Bluetooth receipt/thermal printers. It does not support Bluetooth LE printers, WiFi/network printers, USB-connected printers, or non-ESC/POS protocols (PCL/PostScript inkjet & laser printers).
|
|
40
|
+
|
|
41
|
+
## Minimum Requirements
|
|
42
|
+
|
|
43
|
+
- **React Native** >= 0.76 (New Architecture / TurboModules)
|
|
44
|
+
- **Android API Level** >= 24 (Android 7.0+)
|
|
45
|
+
- **Kotlin** 2.0.21+ (included in the build system)
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
npm install @rejaul/react-native-printer-kit
|
|
51
|
+
# or
|
|
52
|
+
yarn add @rejaul/react-native-printer-kit
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Nothing to configure manually — all required Bluetooth and foreground-service permissions are declared by the underlying native library's manifest and merge in automatically. You still need to request the runtime `BLUETOOTH_CONNECT` (Android 12+) and `POST_NOTIFICATIONS` (Android 13+) permissions at runtime — `requestBluetoothPermission()` handles both in one call; see [Permissions](#permissions) below.
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import {
|
|
61
|
+
hasBluetoothPermission,
|
|
62
|
+
requestBluetoothPermission,
|
|
63
|
+
getBondedBluetoothPrinters,
|
|
64
|
+
connectPrinter,
|
|
65
|
+
printText,
|
|
66
|
+
printHtml,
|
|
67
|
+
} from '@rejaul/react-native-printer-kit';
|
|
68
|
+
|
|
69
|
+
async function printReceipt() {
|
|
70
|
+
// 1. Make sure Bluetooth permission is granted (Android 12+)
|
|
71
|
+
if (!hasBluetoothPermission()) {
|
|
72
|
+
const granted = await requestBluetoothPermission();
|
|
73
|
+
if (!granted) return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// 2. List paired printers and connect to one
|
|
77
|
+
const printers = getBondedBluetoothPrinters();
|
|
78
|
+
if (printers.length === 0) return;
|
|
79
|
+
await connectPrinter(printers[0]!.address);
|
|
80
|
+
|
|
81
|
+
// 3a. Print plain text
|
|
82
|
+
await printText('Hello from PrinterKit');
|
|
83
|
+
|
|
84
|
+
// 3b. Or print a full HTML receipt (Bangla, styling, everything)
|
|
85
|
+
const ok = await printHtml(myReceiptHtml);
|
|
86
|
+
console.log('Printed:', ok);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Permissions
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import {
|
|
94
|
+
hasBluetoothPermission,
|
|
95
|
+
requestBluetoothPermission,
|
|
96
|
+
} from '@rejaul/react-native-printer-kit';
|
|
97
|
+
|
|
98
|
+
// Synchronous check - always true below Android 12
|
|
99
|
+
const granted = hasBluetoothPermission();
|
|
100
|
+
|
|
101
|
+
// Shows the system permission dialog if needed (no-op if already granted,
|
|
102
|
+
// or not required on this Android version)
|
|
103
|
+
const result = await requestBluetoothPermission();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## API Reference
|
|
107
|
+
|
|
108
|
+
### Permissions
|
|
109
|
+
|
|
110
|
+
#### `hasBluetoothPermission(): boolean`
|
|
111
|
+
Whether `BLUETOOTH_CONNECT` specifically is already granted (always `true` below Android 12) — the permission that actually gates connect/print.
|
|
112
|
+
|
|
113
|
+
#### `requestBluetoothPermission(): Promise<boolean>`
|
|
114
|
+
Shows the system permission dialog(s) for whatever's needed on this Android version — `BLUETOOTH_CONNECT` (12+) and `POST_NOTIFICATIONS` (13+, for the persistent-connection notification's visibility) — and resolves based on `BLUETOOTH_CONNECT`'s result specifically. A denied `POST_NOTIFICATIONS` alone doesn't make this resolve `false`: printing still works, the notification just won't show.
|
|
115
|
+
|
|
116
|
+
### Connection
|
|
117
|
+
|
|
118
|
+
#### `getBondedBluetoothPrinters(): BluetoothPrinterDevice[]`
|
|
119
|
+
Lists Bluetooth devices already paired with the phone via Android's own Bluetooth settings.
|
|
120
|
+
|
|
121
|
+
#### `connectPrinter(address: string): Promise<boolean>`
|
|
122
|
+
Opens an RFCOMM/SPP connection to the paired printer. Closes any existing connection first, and remembers the address for auto-reconnect on the next app launch.
|
|
123
|
+
|
|
124
|
+
#### `disconnectPrinter(): Promise<void>`
|
|
125
|
+
Closes the current connection and forgets it for auto-reconnect.
|
|
126
|
+
|
|
127
|
+
#### `isConnectedPrinter(): Promise<boolean>`
|
|
128
|
+
Whether a printer connection is currently open.
|
|
129
|
+
|
|
130
|
+
#### `getConnectedPrinter(): Promise<BluetoothPrinterDevice | null>`
|
|
131
|
+
The currently connected printer, if any.
|
|
132
|
+
|
|
133
|
+
### Printing
|
|
134
|
+
|
|
135
|
+
#### `printText(text: string, feedLines?: number): Promise<void>`
|
|
136
|
+
Prints raw text using the printer's built-in font. **ASCII only** — use `printHtml()` for Bangla or other non-Latin scripts.
|
|
137
|
+
|
|
138
|
+
#### `printImage(imagePath: string, printerWidthDots?: number, feedLines?: number): Promise<void>`
|
|
139
|
+
Prints an image file as a dithered ESC/POS raster image. `printerWidthDots`: 384 for 58mm printers, 576 for 80mm printers (default 384).
|
|
140
|
+
|
|
141
|
+
#### `printImageBase64(base64: string, printerWidthDots?: number, feedLines?: number): Promise<void>`
|
|
142
|
+
Same as above, from a base64-encoded image string.
|
|
143
|
+
|
|
144
|
+
#### `pdfToImage(pdfPath: string, imageType?: 'PNG' | 'JPEG', page?: number, targetWidthPx?: number): Promise<string>`
|
|
145
|
+
Renders one PDF page to an image file and returns its path.
|
|
146
|
+
|
|
147
|
+
#### `printPdf(pdfPath: string, printerWidthDots?: number, page?: number, feedLines?: number): Promise<void>`
|
|
148
|
+
`pdfToImage` + print, in one call.
|
|
149
|
+
|
|
150
|
+
#### `htmlToPdf(html: string, pageWidthDp?: number, heightDp?: number, minPageHeightDp?: number): Promise<string | null>`
|
|
151
|
+
Renders HTML to a PDF file using an off-screen `WebView` and returns its path, or `null` on failure. `pageWidthDp` controls how large the content renders (like a CSS viewport width), independent of the final printed width. Leave `heightDp` unset to auto-measure the real content height (recommended).
|
|
152
|
+
|
|
153
|
+
#### `printHtml(html: string, printerWidthDots?: number, pageWidthDp?: number, heightDp?: number, minPageHeightDp?: number): Promise<boolean>`
|
|
154
|
+
Full pipeline: `htmlToPdf` → `printPdf`. This is how you print Bangla (or other non-Latin) text or any real HTML/CSS layout.
|
|
155
|
+
|
|
156
|
+
## Type Definitions
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
type BluetoothPrinterDevice = {
|
|
160
|
+
name: string | null;
|
|
161
|
+
address: string;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
type PrinterImageType = 'PNG' | 'JPEG';
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Persistent connection & auto-reconnect
|
|
168
|
+
|
|
169
|
+
Connecting through this library keeps the printer connected even if the app is swiped away from Recents — the native side runs a foreground service that outlives the Activity/JS runtime. Every successful `connectPrinter()` call also remembers the device, so the next time the app is opened (even after being fully killed) it reconnects automatically without the user having to pick the printer again.
|
|
170
|
+
|
|
171
|
+
## Printing Bangla / other non-Latin text
|
|
172
|
+
|
|
173
|
+
ESC/POS printer firmware fonts only cover ASCII, so `printText()` can't render Bangla. For anything with non-Latin text, render it as an image instead — build a bitmap yourself and use `printImage()`/`printImageBase64()`, or write it as HTML/CSS and call `printHtml()`, which renders it with a real font via `WebView` and prints the result as a dithered raster image.
|
|
174
|
+
|
|
175
|
+
## Example Application
|
|
176
|
+
|
|
177
|
+
See the [`example`](./example) directory for a complete React Native application demonstrating permission handling, printer pairing/connection, and every print function above.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
cd example
|
|
181
|
+
yarn install
|
|
182
|
+
yarn android
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Contributing
|
|
186
|
+
|
|
187
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
188
|
+
|
|
189
|
+
## Code of Conduct
|
|
190
|
+
|
|
191
|
+
This project adheres to the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT - See [LICENSE](LICENSE) file for details.
|
|
196
|
+
|
|
197
|
+
## Support
|
|
198
|
+
|
|
199
|
+
For issues, questions, or suggestions, please open an [issue on GitHub](https://github.com/DeveloperRejaul/react-native-printer-kit/issues).
|
|
@@ -0,0 +1,20 @@
|
|
|
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 = "ReactNativePrinterKit"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => min_ios_version_supported }
|
|
14
|
+
s.source = { :git => "https://github.com/DeveloperRejaul/react-native-printer-kit.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
|
|
17
|
+
s.private_header_files = "ios/**/*.h"
|
|
18
|
+
|
|
19
|
+
install_modules_dependencies(s)
|
|
20
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.ReactNativePrinterKit = [
|
|
3
|
+
kotlinVersion: "2.0.21",
|
|
4
|
+
minSdkVersion: 24,
|
|
5
|
+
compileSdkVersion: 36
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
ext.getExtOrDefault = { prop ->
|
|
9
|
+
if (rootProject.ext.has(prop)) {
|
|
10
|
+
return rootProject.ext.get(prop)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return ReactNativePrinterKit[prop]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
repositories {
|
|
17
|
+
google()
|
|
18
|
+
mavenCentral()
|
|
19
|
+
maven { url = uri("https://jitpack.io") }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dependencies {
|
|
23
|
+
classpath "com.android.tools.build:gradle:8.7.2"
|
|
24
|
+
// noinspection DifferentKotlinGradleVersion
|
|
25
|
+
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
apply plugin: "com.android.library"
|
|
31
|
+
|
|
32
|
+
if (project.extensions.findByName("kotlin") == null) {
|
|
33
|
+
apply plugin: "kotlin-android"
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
apply plugin: "com.facebook.react"
|
|
38
|
+
|
|
39
|
+
android {
|
|
40
|
+
namespace "com.rejaul.reactnativeprinterkit"
|
|
41
|
+
|
|
42
|
+
compileSdkVersion getExtOrDefault("compileSdkVersion")
|
|
43
|
+
|
|
44
|
+
defaultConfig {
|
|
45
|
+
minSdkVersion getExtOrDefault("minSdkVersion")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
compileOptions {
|
|
49
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
50
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
repositories {
|
|
55
|
+
google()
|
|
56
|
+
mavenCentral()
|
|
57
|
+
// JitPack resolves this multi-module repo by its GitHub repo name
|
|
58
|
+
// (android-printer-kit), not the internal Gradle module name (printerkit).
|
|
59
|
+
maven { url = uri("https://jitpack.io") }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
dependencies {
|
|
63
|
+
implementation "com.facebook.react:react-android"
|
|
64
|
+
implementation("com.github.DeveloperRejaul:android-printer-kit:v0.0.3")
|
|
65
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
package com.rejaul.reactnativeprinterkit
|
|
2
|
+
|
|
3
|
+
import android.content.ComponentName
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.content.ServiceConnection
|
|
7
|
+
import android.content.pm.PackageManager
|
|
8
|
+
import android.os.IBinder
|
|
9
|
+
import androidx.core.content.ContextCompat
|
|
10
|
+
import com.facebook.react.bridge.Arguments
|
|
11
|
+
import com.facebook.react.bridge.Promise
|
|
12
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
13
|
+
import com.facebook.react.bridge.WritableArray
|
|
14
|
+
import com.facebook.react.bridge.WritableMap
|
|
15
|
+
import com.facebook.react.modules.core.PermissionAwareActivity
|
|
16
|
+
import com.rezaul.printerkit.BluetoothPermissions
|
|
17
|
+
import com.rezaul.printerkit.BluetoothPrinter
|
|
18
|
+
import com.rezaul.printerkit.BluetoothPrinterDevice
|
|
19
|
+
import com.rezaul.printerkit.BluetoothPrinterService
|
|
20
|
+
import com.rezaul.printerkit.PrinterImageType
|
|
21
|
+
import java.util.concurrent.Executors
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Bridges [com.rezaul.printerkit.BluetoothPrinter] to React Native.
|
|
25
|
+
*
|
|
26
|
+
* Connect/print calls run through a bound [BluetoothPrinterService] (started once,
|
|
27
|
+
* on module init) instead of a bare [BluetoothPrinter], so the connection survives
|
|
28
|
+
* the app being swiped away from Recents - see [BluetoothPrinterService]. Calls made
|
|
29
|
+
* before the service finishes binding (essentially instant, but not synchronous) are
|
|
30
|
+
* queued and run once it connects.
|
|
31
|
+
*
|
|
32
|
+
* All blocking Bluetooth/PDF/image I/O runs on a single background executor, off the
|
|
33
|
+
* calling (JS) thread - also serializing printer writes, since concurrent writes to
|
|
34
|
+
* the same socket from two overlapping calls would corrupt the output.
|
|
35
|
+
*/
|
|
36
|
+
class ReactNativePrinterKitModule(reactContext: ReactApplicationContext) :
|
|
37
|
+
NativeReactNativePrinterKitSpec(reactContext) {
|
|
38
|
+
|
|
39
|
+
private val executor = Executors.newSingleThreadExecutor()
|
|
40
|
+
|
|
41
|
+
// getBondedBluetoothPrinters() is synchronous (no connection involved) and must
|
|
42
|
+
// answer immediately, so it uses its own plain BluetoothPrinter rather than
|
|
43
|
+
// waiting on the service to bind.
|
|
44
|
+
private val standalonePrinter by lazy { BluetoothPrinter(reactApplicationContext) }
|
|
45
|
+
|
|
46
|
+
private var printerService: BluetoothPrinterService? = null
|
|
47
|
+
private val pendingActions = mutableListOf<(BluetoothPrinterService) -> Unit>()
|
|
48
|
+
|
|
49
|
+
private val serviceConnection = object : ServiceConnection {
|
|
50
|
+
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
|
51
|
+
val service = (binder as BluetoothPrinterService.LocalBinder).getService()
|
|
52
|
+
printerService = service
|
|
53
|
+
val queued = synchronized(pendingActions) {
|
|
54
|
+
val copy = pendingActions.toList()
|
|
55
|
+
pendingActions.clear()
|
|
56
|
+
copy
|
|
57
|
+
}
|
|
58
|
+
queued.forEach { it(service) }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
override fun onServiceDisconnected(name: ComponentName?) {
|
|
62
|
+
printerService = null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
init {
|
|
67
|
+
val intent = Intent(reactApplicationContext, BluetoothPrinterService::class.java)
|
|
68
|
+
reactApplicationContext.startService(intent)
|
|
69
|
+
reactApplicationContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Runs [action] with the bound service, once available (queued if not yet bound). */
|
|
73
|
+
private fun withService(action: (BluetoothPrinterService) -> Unit) {
|
|
74
|
+
val service = printerService
|
|
75
|
+
if (service != null) {
|
|
76
|
+
action(service)
|
|
77
|
+
} else {
|
|
78
|
+
synchronized(pendingActions) { pendingActions.add(action) }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private fun deviceToMap(device: BluetoothPrinterDevice): WritableMap {
|
|
83
|
+
val map = Arguments.createMap()
|
|
84
|
+
map.putString("name", device.name)
|
|
85
|
+
map.putString("address", device.address)
|
|
86
|
+
return map
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
override fun hasBluetoothPermission(): Boolean {
|
|
90
|
+
return BluetoothPermissions.isGranted(reactApplicationContext)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override fun requestBluetoothPermission(promise: Promise) {
|
|
94
|
+
// getRequiredPermissions() can list more than one permission (BLUETOOTH_CONNECT
|
|
95
|
+
// and, on Android 13+, POST_NOTIFICATIONS for the foreground service's ongoing
|
|
96
|
+
// notification) - request every one of them, not just check isGranted(), which
|
|
97
|
+
// only reflects BLUETOOTH_CONNECT and would otherwise skip requesting the rest.
|
|
98
|
+
val required = BluetoothPermissions.getRequiredPermissions()
|
|
99
|
+
val alreadyGranted = required.all {
|
|
100
|
+
ContextCompat.checkSelfPermission(reactApplicationContext, it) == PackageManager.PERMISSION_GRANTED
|
|
101
|
+
}
|
|
102
|
+
if (required.isEmpty() || alreadyGranted) {
|
|
103
|
+
promise.resolve(true)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
val activity = currentActivity as? PermissionAwareActivity
|
|
107
|
+
if (activity == null) {
|
|
108
|
+
promise.reject("NO_ACTIVITY", "No current activity to request permission from")
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
activity.requestPermissions(required, PERMISSION_REQUEST_CODE) { requestCode, permissions, grantResults ->
|
|
112
|
+
if (requestCode == PERMISSION_REQUEST_CODE) {
|
|
113
|
+
promise.resolve(BluetoothPermissions.isGrantResult(requestCode, permissions, grantResults, PERMISSION_REQUEST_CODE))
|
|
114
|
+
true
|
|
115
|
+
} else {
|
|
116
|
+
false
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
override fun getBondedBluetoothPrinters(): WritableArray {
|
|
122
|
+
val result: WritableArray = Arguments.createArray()
|
|
123
|
+
standalonePrinter.getBondedBluetoothPrinters().forEach { device ->
|
|
124
|
+
result.pushMap(deviceToMap(device))
|
|
125
|
+
}
|
|
126
|
+
return result
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
override fun connectPrinter(address: String, promise: Promise) {
|
|
130
|
+
withService { service ->
|
|
131
|
+
executor.execute {
|
|
132
|
+
try {
|
|
133
|
+
promise.resolve(service.connectAndKeepAlive(address))
|
|
134
|
+
} catch (e: Exception) {
|
|
135
|
+
promise.reject("ERROR", e.message)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
override fun disconnectPrinter(promise: Promise) {
|
|
142
|
+
withService { service ->
|
|
143
|
+
executor.execute {
|
|
144
|
+
try {
|
|
145
|
+
service.disconnect()
|
|
146
|
+
promise.resolve(null)
|
|
147
|
+
} catch (e: Exception) {
|
|
148
|
+
promise.reject("ERROR", e.message)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
override fun isConnectedPrinter(promise: Promise) {
|
|
155
|
+
withService { service ->
|
|
156
|
+
executor.execute {
|
|
157
|
+
try {
|
|
158
|
+
promise.resolve(service.printer.isConnectedPrinter())
|
|
159
|
+
} catch (e: Exception) {
|
|
160
|
+
promise.reject("ERROR", e.message)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override fun getConnectedPrinter(promise: Promise) {
|
|
167
|
+
withService { service ->
|
|
168
|
+
executor.execute {
|
|
169
|
+
try {
|
|
170
|
+
val device = service.printer.getConnectedPrinter()
|
|
171
|
+
promise.resolve(device?.let { deviceToMap(it) })
|
|
172
|
+
} catch (e: Exception) {
|
|
173
|
+
promise.reject("ERROR", e.message)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
override fun printText(text: String, feedLines: Double?, promise: Promise) {
|
|
180
|
+
withService { service ->
|
|
181
|
+
executor.execute {
|
|
182
|
+
try {
|
|
183
|
+
service.printer.printText(text, (feedLines ?: 3.0).toInt())
|
|
184
|
+
promise.resolve(null)
|
|
185
|
+
} catch (e: Exception) {
|
|
186
|
+
promise.reject("ERROR", e.message)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
override fun printImage(imagePath: String, printerWidthDots: Double?, feedLines: Double?, promise: Promise) {
|
|
193
|
+
withService { service ->
|
|
194
|
+
executor.execute {
|
|
195
|
+
try {
|
|
196
|
+
service.printer.printImageFile(imagePath, (printerWidthDots ?: 384.0).toInt(), (feedLines ?: 3.0).toInt())
|
|
197
|
+
promise.resolve(null)
|
|
198
|
+
} catch (e: Exception) {
|
|
199
|
+
promise.reject("ERROR", e.message)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
override fun printImageBase64(base64: String, printerWidthDots: Double?, feedLines: Double?, promise: Promise) {
|
|
206
|
+
withService { service ->
|
|
207
|
+
executor.execute {
|
|
208
|
+
try {
|
|
209
|
+
service.printer.printImageBase64(base64, (printerWidthDots ?: 384.0).toInt(), (feedLines ?: 3.0).toInt())
|
|
210
|
+
promise.resolve(null)
|
|
211
|
+
} catch (e: Exception) {
|
|
212
|
+
promise.reject("ERROR", e.message)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
override fun pdfToImage(pdfPath: String, imageType: String?, page: Double?, targetWidthPx: Double?, promise: Promise) {
|
|
219
|
+
withService { service ->
|
|
220
|
+
executor.execute {
|
|
221
|
+
try {
|
|
222
|
+
val type = if (imageType == "JPEG") PrinterImageType.JPEG else PrinterImageType.PNG
|
|
223
|
+
val path = service.printer.pdfToImage(pdfPath, type, (page ?: 0.0).toInt(), targetWidthPx?.toInt())
|
|
224
|
+
promise.resolve(path)
|
|
225
|
+
} catch (e: Exception) {
|
|
226
|
+
promise.reject("ERROR", e.message)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
override fun printPdf(pdfPath: String, printerWidthDots: Double?, page: Double?, feedLines: Double?, promise: Promise) {
|
|
233
|
+
withService { service ->
|
|
234
|
+
executor.execute {
|
|
235
|
+
try {
|
|
236
|
+
service.printer.printPdf(pdfPath, (printerWidthDots ?: 384.0).toInt(), (page ?: 0.0).toInt(), (feedLines ?: 3.0).toInt())
|
|
237
|
+
promise.resolve(null)
|
|
238
|
+
} catch (e: Exception) {
|
|
239
|
+
promise.reject("ERROR", e.message)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
override fun htmlToPdf(html: String, pageWidthDp: Double?, heightDp: Double?, minPageHeightDp: Double?, promise: Promise) {
|
|
246
|
+
withService { service ->
|
|
247
|
+
service.printer.htmlToPdf(
|
|
248
|
+
html = html,
|
|
249
|
+
pageWidthDp = (pageWidthDp ?: 412.0).toInt(),
|
|
250
|
+
heightDp = heightDp?.toInt(),
|
|
251
|
+
minPageHeightDp = (minPageHeightDp ?: 1000.0).toInt()
|
|
252
|
+
) { path ->
|
|
253
|
+
promise.resolve(path)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override fun printHtml(
|
|
259
|
+
html: String,
|
|
260
|
+
printerWidthDots: Double?,
|
|
261
|
+
pageWidthDp: Double?,
|
|
262
|
+
heightDp: Double?,
|
|
263
|
+
minPageHeightDp: Double?,
|
|
264
|
+
promise: Promise
|
|
265
|
+
) {
|
|
266
|
+
withService { service ->
|
|
267
|
+
service.printer.printHtml(
|
|
268
|
+
html = html,
|
|
269
|
+
printerWidthDots = (printerWidthDots ?: 384.0).toInt(),
|
|
270
|
+
pageWidthDp = (pageWidthDp ?: 412.0).toInt(),
|
|
271
|
+
heightDp = heightDp?.toInt(),
|
|
272
|
+
minPageHeightDp = (minPageHeightDp ?: 1000.0).toInt()
|
|
273
|
+
) { ok ->
|
|
274
|
+
promise.resolve(ok)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
companion object {
|
|
280
|
+
const val NAME = NativeReactNativePrinterKitSpec.NAME
|
|
281
|
+
private const val PERMISSION_REQUEST_CODE = 4201
|
|
282
|
+
}
|
|
283
|
+
}
|
package/android/src/main/java/com/rejaul/reactnativeprinterkit/ReactNativePrinterKitPackage.kt
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
package com.rejaul.reactnativeprinterkit
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
import java.util.HashMap
|
|
9
|
+
|
|
10
|
+
class ReactNativePrinterKitPackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == ReactNativePrinterKitModule.NAME) {
|
|
13
|
+
ReactNativePrinterKitModule(reactContext)
|
|
14
|
+
} else {
|
|
15
|
+
null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
|
|
20
|
+
mapOf(
|
|
21
|
+
ReactNativePrinterKitModule.NAME to ReactModuleInfo(
|
|
22
|
+
name = ReactNativePrinterKitModule.NAME,
|
|
23
|
+
className = ReactNativePrinterKitModule.NAME,
|
|
24
|
+
canOverrideExistingModule = false,
|
|
25
|
+
needsEagerInit = false,
|
|
26
|
+
isCxxModule = false,
|
|
27
|
+
isTurboModule = true
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#import "ReactNativePrinterKit.h"
|
|
2
|
+
|
|
3
|
+
@implementation ReactNativePrinterKit
|
|
4
|
+
- (NSNumber *)multiply:(double)a b:(double)b {
|
|
5
|
+
NSNumber *result = @(a * b);
|
|
6
|
+
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
11
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
12
|
+
{
|
|
13
|
+
return std::make_shared<facebook::react::NativeReactNativePrinterKitSpecJSI>(params);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
+ (NSString *)moduleName
|
|
17
|
+
{
|
|
18
|
+
return @"ReactNativePrinterKit";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { TurboModuleRegistry, Platform } from 'react-native';
|
|
4
|
+
function getModule() {
|
|
5
|
+
return Platform.OS === 'android' ? TurboModuleRegistry.getEnforcing('ReactNativePrinterKit') : {};
|
|
6
|
+
}
|
|
7
|
+
export default getModule();
|
|
8
|
+
//# sourceMappingURL=NativeReactNativePrinterKit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TurboModuleRegistry","Platform","getModule","OS","getEnforcing"],"sourceRoot":"../../src","sources":["NativeReactNativePrinterKit.ts"],"mappings":";;AAAA,SAASA,mBAAmB,EAAoBC,QAAQ,QAAQ,cAAc;AAgI9E,SAASC,SAASA,CAAA,EAAS;EACzB,OAAOD,QAAQ,CAACE,EAAE,KAAK,SAAS,GAC5BH,mBAAmB,CAACI,YAAY,CAAO,uBAAuB,CAAC,GAC9D,CAAC,CAAU;AAClB;AAEA,eAAeF,SAAS,CAAC,CAAC","ignoreList":[]}
|