Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android support for USB printers #38

Open
plokko opened this issue Jul 10, 2020 · 3 comments
Open

Android support for USB printers #38

plokko opened this issue Jul 10, 2020 · 3 comments
Labels
enhancement New feature or request

Comments

@plokko
Copy link

plokko commented Jul 10, 2020

I made this library work with Android USB printers by creating a custom OutputStream;
i also created some helper classes to ease the integration on Android.

The code is written in Kotlin and i also used Coroutines (not needed but they make the code a lot cleaner).
I'm not a package developer and i do understand this code may be better on a separate package but i'm not familiar with Java/Android package development so i'll leave the source here if anyone can help me release it.

Usage example:

val usbManager = getSystemService(Context.USB_SERVICE) as UsbManager

//Get a list of attached printers
val printerList = UsbPrinter.list(usbManager)

//Select the first printer
val printer = printerList.first()

//Launch a Coroutine
GlobalScope.launch(Dispatchers.IO) {
    try{
        //Ask for USB permission (and waits for result)
        val escpos = printer.requestPermission(applicationContext, usbManager)
        //use EscPos class as usual
        escpos.writeLF("Hello Wold")
        escpos.feed(5)
        escpos.cut(EscPos.CutMode.FULL)
        //...
        escpos.close()
    }catch(e:UsbPrinter.UsbPermissionDeniedException){
       //Usb permission denied!
    }

}

Code:

import android.hardware.usb.*
import java.io.OutputStream

/**
 * Handle an USB ESC/POS printer output stream
 */
class UsbPrinterOutputStream(
        val usbmanager: UsbManager,
        val device: UsbDevice,
        var usbInterface: UsbInterface?=null,
        var usbEndpoint: UsbEndpoint?=null,
        val timeout:Int=10000
    ): OutputStream() {

    val conn = usbmanager.openDevice(device)

    init{
        if(usbInterface==null){
            usbInterface = device.getInterface(0)
            for(i in 0..device.interfaceCount) {
                val int = device.getInterface(i)
                if(int.interfaceClass== UsbConstants.USB_CLASS_PRINTER){
                    usbInterface = int
                }
            }
        }
        if(usbEndpoint==null) {
            usbEndpoint = usbInterface?.getEndpoint(0)
            if(usbInterface!=null)
                for(i in 0..usbInterface!!.endpointCount-1){
                    val endpoint = usbInterface!!.getEndpoint(i)
                    if(endpoint.type==UsbConstants.USB_DIR_IN){
                        usbEndpoint = endpoint
                    }
                }
        }
        conn.claimInterface(usbInterface,true)
    }
    override fun write(i: Int) {
        this.write(byteArrayOf(i.toByte()))
    }

    override fun write(b: ByteArray?) {
        val count = b?.count() ?:0
        if(count>0)
            conn.bulkTransfer(usbEndpoint,b,count,timeout)
    }

    override fun write(b: ByteArray?, off: Int, len: Int) {
        val count = b?.count() ?:0
        if(count>0 && len>0)
            conn.bulkTransfer(usbEndpoint,b,off,len,timeout)
    }

    override fun close(){
        conn.releaseInterface(usbInterface)
        conn.close()
    }

}

import android.hardware.usb.*
import java.io.OutputStream

/**
 * Handle an USB ESC/POS printer output stream
 */
class UsbPrinterOutputStream(
        val usbmanager: UsbManager,
        val device: UsbDevice,
        var usbInterface: UsbInterface?=null,
        var usbEndpoint: UsbEndpoint?=null,
        val timeout:Int=10000
    ): OutputStream() {

    val conn = usbmanager.openDevice(device)

    init{
        if(usbInterface==null){
            usbInterface = device.getInterface(0)
            for(i in 0..device.interfaceCount) {
                val int = device.getInterface(i)
                if(int.interfaceClass== UsbConstants.USB_CLASS_PRINTER){
                    usbInterface = int
                }
            }
        }
        if(usbEndpoint==null) {
            usbEndpoint = usbInterface?.getEndpoint(0)
            if(usbInterface!=null)
                for(i in 0..usbInterface!!.endpointCount-1){
                    val endpoint = usbInterface!!.getEndpoint(i)
                    if(endpoint.type==UsbConstants.USB_DIR_IN){
                        usbEndpoint = endpoint
                    }
                }
        }
        conn.claimInterface(usbInterface,true)
    }
    override fun write(i: Int) {
        this.write(byteArrayOf(i.toByte()))
    }

    override fun write(b: ByteArray?) {
        val count = b?.count() ?:0
        if(count>0)
            conn.bulkTransfer(usbEndpoint,b,count,timeout)
    }

    override fun write(b: ByteArray?, off: Int, len: Int) {
        val count = b?.count() ?:0
        if(count>0 && len>0)
            conn.bulkTransfer(usbEndpoint,b,off,len,timeout)
    }

    override fun close(){
        conn.releaseInterface(usbInterface)
        conn.close()
    }

}
@plokko plokko added the enhancement New feature or request label Jul 10, 2020
@plokko
Copy link
Author

plokko commented Jul 10, 2020

I added a CoffeImage implementation to use Android Bitmap instead of BufferedImage

Example code:

val bm = BitmapFactory.decodeResource(resources, R.mipmap.myimage)

val imageWrapper = BitImageWrapper()
val algorithm = BitonalOrderedDither()

val escposImage = EscPosImage(BitmapCoffeeImage(bm), algorithm)

escpos.write(imageWrapper, escposImage)
//...

Source:

import android.graphics.Bitmap
import android.graphics.Color
import com.github.anastaciocintra.escpos.image.CoffeeImage


class BitmapCoffeeImage(val image:Bitmap): CoffeeImage {
    override fun getHeight(): Int = image.height
    override fun getWidth(): Int = image.width

    override fun getSubimage(x: Int, y: Int, w: Int, h: Int): CoffeeImage = BitmapCoffeeImage( Bitmap.createBitmap(image,x,y,w,h))

    override fun getRGB(x: Int, y: Int): Int {
        val pixel  = image.getPixel(x,y)
        return (Color.alpha(pixel) shl 24 ) or (Color.red(pixel) shl 16) or (Color.green(pixel) shl 8) or (Color.blue(pixel) shl 0);
    }

}

1 similar comment
@plokko
Copy link
Author

plokko commented Jul 10, 2020

I added a CoffeImage implementation to use Android Bitmap instead of BufferedImage

Example code:

val bm = BitmapFactory.decodeResource(resources, R.mipmap.myimage)

val imageWrapper = BitImageWrapper()
val algorithm = BitonalOrderedDither()

val escposImage = EscPosImage(BitmapCoffeeImage(bm), algorithm)

escpos.write(imageWrapper, escposImage)
//...

Source:

import android.graphics.Bitmap
import android.graphics.Color
import com.github.anastaciocintra.escpos.image.CoffeeImage


class BitmapCoffeeImage(val image:Bitmap): CoffeeImage {
    override fun getHeight(): Int = image.height
    override fun getWidth(): Int = image.width

    override fun getSubimage(x: Int, y: Int, w: Int, h: Int): CoffeeImage = BitmapCoffeeImage( Bitmap.createBitmap(image,x,y,w,h))

    override fun getRGB(x: Int, y: Int): Int {
        val pixel  = image.getPixel(x,y)
        return (Color.alpha(pixel) shl 24 ) or (Color.red(pixel) shl 16) or (Color.green(pixel) shl 8) or (Color.blue(pixel) shl 0);
    }

}

@anastaciocintra
Copy link
Owner

Good @plokko
nice examples in kotlin... and usb android.. and image class in kotlin
Congratulations!!!.

I don't know how to work with mix kotlin and java in one package. Or the best way to do this. But I know that some library for android are uncompatible with desktop.
We need to think about best way to maintain compatibility, stability, etc ...

but before the package, feel invited to integrate the contributors team.

So, in my hubble opinion, we need to pass on some phases:

  • put the project (as simple as you can sample app) on escpos-coffee-samples/miscellaneous/
  • wait some time to stability, (you @plokko need to help) to resolve possible issues
  • with stable code then package time ... end think the best whay, with some time to discuss and make the right decision.

If you agree, you can put the code on escpos-coffee-samples/miscellaneous/
entire project to run on android studio. from start to end with one page of README.md.
and make a pull request.

Marco.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants