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
}