Printer module
Call flow
It is rather simple to print receipts or reports using the built-in printer in APOLLO.
Setup
Create an instance and connect the app to the terminal by getControllerInstance() and connectController().
The app shall be notified by onError if there's any errors occurred.
abstract class ApolloPrinter(private val context: Context) : PrinterDelegate {
private var printerController: PrinterController? = null
private val printerFixedWidth = 384
private lateinit var printData: ByteArray
fun connect(): Boolean {
try {
if (printerController == null) {
// get instance
printerController = getControllerInstance(context, this)
// enable debug log display in console
SPDeviceController.enableDebugLog(true)
}
// establish the communication channel with terminal
printerController?.connectController()
} catch (ex: Exception) {
return false
}
return true
}
fun disconnect() {
printerController?.disconnectController()
printerController?.releaseControllerInstance()
}
override fun onError(errorType: ControllerError.Error?, message: String?) {
// handle error here
}
}
Printing flow
Prepare printData
The APOLLO thermal printer has 384 dots/ line. The app should convert the receipt or report to byte array before sending to the printer.
A simple approach is first create the receipt or report in HTML and convert it to bitmap. On the app build.gradle, add the html2bitmap dependency.
dependencies {
//HTML to bitmap library
implementation 'com.izettle:html2bitmap:1.6'
}
In ApolloPrinter class, there's an example for converting bitmap to ByteArray.
/**
* Input bitmap can be any size but preferred use APOLLO printer width or scaling is needed
*/
private fun bitmapTo1bppByteArray(bitmap: Bitmap): ByteArray {
val bFilter = true
val bmpResult: Bitmap
var width = bitmap.width
var height = bitmap.height
val widthNew = printerFixedWidth
val dataLen = ceil((width * height).toDouble() / 8).toInt()
val paddedData = ByteArray(dataLen)
/* First do a scale to make sure width match apollo criteria */
if (widthNew != bitmap.width) {
val scaleWidthFactor = widthNew.toDouble() / width
val dstHeight = (height * scaleWidthFactor).toInt()
bmpResult = Bitmap.createScaledBitmap(bitmap, widthNew, dstHeight, bFilter)
} else {
bmpResult = bitmap
}
//Change to B&W and RGB565
val convertedBitmap = Bitmap.createBitmap(bmpResult.width, bmpResult.height, Bitmap.Config.RGB_565)
val canvas = Canvas(convertedBitmap)
val ma = ColorMatrix()
ma.setSaturation(0f)
val paint = Paint()
paint.colorFilter = ColorMatrixColorFilter(ma)
canvas.drawBitmap(bitmap, 0f, 0f, paint)
width = convertedBitmap.width
height = convertedBitmap.height
val bb: ByteBuffer = ByteBuffer.allocate(convertedBitmap.byteCount)
convertedBitmap.copyPixelsToBuffer(bb)
val byteStream = ByteArrayInputStream(bb.array())
var bitPosition = 0
var bytePosition = 0
for (y in 0 until height) {
for (x in 0 until width) {
//Read 2 bytes from byte stream, beware of endian
val rgb565 = byteStream.read() or (byteStream.read() shl 8)
//Here for simplicity just use Red value (5bits) and threshold set to 16 which assume the input bitmap already in Grey scale
val red = (rgb565 shr 11)
//get binary value
var value: Byte
value = if (red <= 16) {
0x01.toByte()
} else {
0x00.toByte()
}
paddedData[bytePosition] = paddedData[bytePosition] or (value.toInt() shl bitPosition).toByte()
bitPosition++
if (bitPosition == 8) {
bitPosition = 0
bytePosition++
}
}
}
return paddedData
}
You can convert the bitmap to byteArray by bitmapTo1bppByteArray.
// data that feed to the printer later
private lateinit var printData: ByteArray
// pass your html string
fun preparePrintData(html: String) {
val bitmap = Html2Bitmap.Builder()
.setContext(context)
.setBitmapWidth(printerFixedWidth) // 384
.setContent(WebViewContent.html(html))
.build().bitmap
printData = bitmapTo1bppByteArray(bitmap)
}
Send printData
The app shall receive onPrintDataRequested after getControllerInstance() and connectController().
The app should pass the printData in byte array format to the printer by sendPrinterData.
override fun onPrintDataRequested() {
printerController?.sendPrinterData(printData)
}
Print status
The app shall receive onPrinterStatus and onPrinterCompleted for printer status.
override fun onPrinterStatus(p0: PrintStatus?) {
when (p0) {
PrintStatus.OVERHEAT -> {
// printer overheat, please wait
}
PrintStatus.NO_PAPER_OR_COVER_OPENED -> {
// no paper or the cover is opened, please re-fill the paper roll and close the cover properly
}
PrintStatus.PRINTER_LOWBATTERY -> {
// battery is low, please charge the APOLLO
}
PrintStatus.SUCCESS -> {
// print success
}
else -> {
}
}
}
override fun onPrinterCompleted() {
// do something
}