This library combines various printer implementations into a single, easy-to-use interface.
Step 1. Add the JitPack repository to your settings.gradle
file:
dependencyResolutionManagement {
repositories {
...
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Step 2. Add the dependency to your app build.gradle
:
dependencies {
implementation 'com.github.tillhub:print-engine:x.x.x'
}
Step 3. Enable JNI legacy packaging
packaging {
jniLibs {
useLegacyPackaging = true
}
}
This SDK offers supports 2 types printing devices:
- PAX devices with a build in printer (A920, A920 pro, etc.)
- Sunmi devices with a build in printer (V2, V2 PRO, V2s, etc.)
- Verifone devices with a build in printer
For devices without printer support the SDK defaults to an EmulatedPrinter
implementation that
prints into Logcat, this way it is easy to develop on emulators and unsupported devices.
The SDK will automatically select the correct implementation based on the device its running on.
Singleton Access
: Obtain a singleton reference to thePrinterEngine
instance.Initialization
: Create a per Context Printer instance. SDK will automatically selects the appropriate printer based on the device manufacturer (Sunmi, Pax, or emulated).Initiate print
: Callprinter.startPrintJob(printJob)
to initiate printing. Pass appropriate PrintJob that should be printed. When print is finished it returnsPrinterResult
.Handle printer state
: Subscribe to theprinter.observePrinterState()
flow to receivePrinterState
objects that inform about the state of printer.
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val printEngine = PrintEngine.getInstance(context)
val printer = printEngine.printer
printButton.setOnClickListener {
lifecycleScope.launch {
val result = printer.startPrintJob(printJob)
when (result) {
is PrinterResult.Success -> {
// Handle success
}
is PrinterResult.Error -> {
// Handle failure
}
}
}
}
lifecycleScope.launch {
// Observing printer state of printer
printer.observePrinterState().collect { printerState ->
// Handle printerState result
}
}
}
companion object {
private val printJob = PrintJob(
listOf(
PrintCommand.Text("This is a line"),
PrintCommand.Text("This is a another line"),
PrintCommand.Text("-------"),
PrintCommand.Text("Barcode:"),
PrintCommand.Barcode("123ABC"),
PrintCommand.Text("QR code:"),
PrintCommand.QrCode("123ABC"),
PrintCommand.FeedPaper,
)
)
}
A PrintJob
is defined as a set of PrintCommand
objects, the commands will be executed
sequentially, with that a receipt can be build.
The PrintJob
object offers 2 convenience values:
isNotEmpty
gives the information if aPrintJob
is empty or notdescription
gives a string representation of the build receipt
List of commands:
PrintCommand.Text(val text : String)
PrintCommand.Image(val image : Bitmap)
PrintCommand.Barcode(val barcode : String)
PrintCommand.QrCode(val code : String)
PrintCommand.RawData(val data : RawPrinterData)
/**
* Due to the distance between the paper hatch and the print head,
* the paper needs to be fed out automatically
* But if the Api does not support it, it will be replaced by printing three lines
*/
PrintCommand.FeedPaper
/**
* Printer cuts paper and throws exception on machines without a cutter
*/
PrintCommand.CutPaper
interface PrintAnalytics {
fun logPrintReceipt(receiptText: String)
fun logErrorPrintReceipt(message: String)
}
The PrinterEngine SDK offers a PrintAnalytics
interface that can be attached to the
PrinterEngine
and will call log methods for printed receipts and errors.
This can be used to for analytics and logging purposes.
The interface implementation has to be attached to the engine instance before getting the printer
instance for it to work.
PrintAnalytics usage:
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val printEngine = PrintEngine.getInstance(context)
// attaching the interface implementation
printEngine.setAnalytics(object : PrintAnalytics {
override fun logPrintReceipt(receiptText: String) {
// Handle printed receipt text
}
override fun logErrorPrintReceipt(message: String) {
// Handle printing error
}
})
val printer = printEngine.printer
}
Because some printing implementations don't support printing barcodes as is, the BarcodeEncoder
implementation was made that generates a bitmap of a Code 128 barcode or QR code, that is then
printed.
The PrinterEngine
offers an instance of this interface that can be used in the host app for
convenience.
In case of an error at time of bitmap generation an null
value is returned.
override fun onCreate(savedInstanceState: Bundle?) {
// ...
val printEngine = PrintEngine.getInstance(context)
val barcodeEncoder = printEngine.barcodeEncoder
val qrCode: Bitmap? = barcodeEncoder.encodeAsBitmap(
content = "barcode_content",
type = BarcodeType.QR_CODE,
imgWidth = 500,
imgHeight = 500
)
val code128: Bitmap? = barcodeEncoder.encodeAsBitmap(
content = "barcode_content",
type = BarcodeType.CODE_128,
imgWidth = 500,
imgHeight = 250
)
}
A library for integrating Star Micronics printers using the StarXpand SDK.
Add to your build.gradle
implementation 'com.tillhub.printengine:star-printer:1.0.0'
-
Permissions These permissions enable Bluetooth communication and scanning to discover and connect to nearby devices, add the following permissions check logic:
val permissions = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { mutableListOf<String>().apply { if (checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH) } if (checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_ADMIN) } if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.ACCESS_COARSE_LOCATION) } } } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { mutableListOf<String>().apply { if (checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH) } if (checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_ADMIN) } if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.ACCESS_FINE_LOCATION) } } } else { mutableListOf<String>().apply { if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_SCAN) } if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_CONNECT) } } } if (permissions.isNotEmpty()) { launcher.launch(permissions.toTypedArray()) }
To avoid the USB connection permission dialog appearing every time a cable is connected or disconnected, add the following
<intent-filter>
and<meta-data>
entries to yourAndroidManifest.xml
:<intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter>
-
Discovery
To begin discovering Star printers, use the following method and handle the results accordingly:// Observe discovery state printerEngine.discoverExternalPrinters(StarPrinterDiscovery).collect { discoveryState -> // Handle discoveryState result }
-
Initialization & Printing
Once a user selects a Star printer, initialize and start printer using the following approach:val service = printer.manufacturer.build( context = context, printer = printer ) printerEngine.initPrinter(service) printerEngine.printer.startPrintJob(printJob)
A library for integrating Epson printers using the ePOS2 lib.
Add to your build.gradle
implementation 'com.tillhub.printengine:epson-printer:1.0.0'
-
Permissions
These permissions enable Bluetooth communication and scanning to discover and connect to nearby devices, add the following permissions check logic:val permissions = if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { mutableListOf<String>().apply { if (checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH) } if (checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_ADMIN) } if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.ACCESS_COARSE_LOCATION) } } } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { mutableListOf<String>().apply { if (checkSelfPermission(Manifest.permission.BLUETOOTH) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH) } if (checkSelfPermission(Manifest.permission.BLUETOOTH_ADMIN) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_ADMIN) } if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.ACCESS_FINE_LOCATION) } } } else { mutableListOf<String>().apply { if (checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_SCAN) } if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { add(Manifest.permission.BLUETOOTH_CONNECT) } } } if (permissions.isNotEmpty()) { launcher.launch(permissions.toTypedArray()) }
To avoid the USB connection permission dialog appearing every time a cable is connected or disconnected, add the following
<intent-filter>
and<meta-data>
entries to yourAndroidManifest.xml
:<intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" />
-
Discovery
To begin discovering Epson printers, use the following method and handle the results accordingly:// Observe discovery state printerEngine.discoverExternalPrinters(EpsonPrinterDiscovery.create()).collect { discoveryState -> // Handle discoveryState result }
-
Initialization & Printing
Once a user selects a Epson printer, initialize and start printer using the following approach:val service = printer.manufacturer.build( context = context, printer = printer ) printerEngine.initPrinter(service) printerEngine.printer.startPrintJob(printJob)
MIT License
Copyright (c) 2024 Tillhub GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.