init: init proj

This commit is contained in:
2025-11-10 18:21:19 +08:00
commit b65b28ec9e
1796 changed files with 187617 additions and 0 deletions

52
Printer/build.gradle Normal file
View File

@@ -0,0 +1,52 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
}
android {
compileSdkVersion 31
buildToolsVersion '30.0.3'
defaultConfig {
minSdkVersion 22
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding = true
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.4'
}

View File

21
Printer/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,24 @@
package com.huitao.printer
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.huitao.printer.test", appContext.packageName)
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.huitao.printer">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application>
<provider
android:name=".PrinterProvider"
android:authorities="${applicationId}.printer-tool"
android:exported="false" />
</application>
</manifest>

View File

@@ -0,0 +1,82 @@
package com.huitao.printer
import android.content.Context
import com.huitao.printer.printerface.DeviceFoundCallback
import com.huitao.printer.printerface.ProcessData
import com.huitao.printer.printerface.TaskCallback
import java.lang.ref.WeakReference
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*/
class Printer private constructor() {
private var context: WeakReference<Context>? = null
private lateinit var printerModel: PrinterModel
companion object {
private var singleInstance: Printer? = null
get() {
if (null == field) {
field = Printer()
}
return field
}
@Synchronized
fun getInstance() = singleInstance!!
}
fun initPrinter(context: Context) {
this.context = WeakReference(context)
printerModel = PrinterModel(this)
printerModel.startPrinterService()
}
fun getContext(): Context? {
return this.context?.get()
}
fun getBondDevicesList(foundCallback: DeviceFoundCallback): MutableList<String>? {
return printerModel.getBondDeviceList(foundCallback)
}
/**
* @param[mac] the printer mac
* @param[taskCallback] the connect state by the taskCallback call
*/
fun connectDeviceByMac(mac: String, taskCallback: TaskCallback) {
printerModel.connectDeviceByMac(mac, taskCallback)
}
/**
* @param[taskCallback] the disconnect state by taskCallback call
*/
fun disconnected(taskCallback: TaskCallback) {
printerModel.disconnected(taskCallback)
}
/**
* @param[taskCallback] write state by the taskCallback call
* @param[processData] the send data
*/
fun writeData(taskCallback: TaskCallback, processData: ProcessData) {
printerModel.writeData(taskCallback, processData)
}
/**
* @param[byteArray] the send data
* @param[taskCallback] write state by the taskCallback call
*/
fun writeData(byteArray: ByteArray, taskCallback: TaskCallback) {
printerModel.writeData(byteArray, taskCallback)
}
}

View File

@@ -0,0 +1,7 @@
package com.huitao.printer
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*/
data class PrinterConfig(val isAutoConnect:Boolean =false)

View File

@@ -0,0 +1,94 @@
package com.huitao.printer
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import com.huitao.printer.listener.BondListCallback
import com.huitao.printer.printerface.DeviceFoundCallback
import com.huitao.printer.printerface.IMyBinder
import com.huitao.printer.printerface.ProcessData
import com.huitao.printer.printerface.TaskCallback
import com.huitao.printer.service.PrinterService
import com.huitao.printer.utils.PrinterDev
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*/
class PrinterModel constructor(private val printer: Printer) {
private var mIMyBinder: IMyBinder? = null
private val mServiceConnect = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mIMyBinder = service as IMyBinder
}
override fun onServiceDisconnected(name: ComponentName?) {
startPrinterService()
}
}
/**
* @return return the bond devices
*/
fun getBondDeviceList(deviceFoundCallback: DeviceFoundCallback): MutableList<String>? {
mIMyBinder?.let {
printer.getContext()?.let { ctx ->
return it.onDiscovery(
ctx,
PrinterDev.PortType.Bluetooth,
deviceFoundCallback
)
}
}
return null
}
fun connectDeviceByMac(mac: String, taskCallback: TaskCallback) {
mIMyBinder?.let {
it.connectBtPort(mac, taskCallback)
return
}
taskCallback.onFailed("mIMyBinder is not initialization,the lib was wrong")
}
fun disconnected(taskCallback: TaskCallback) {
mIMyBinder?.let {
it.disconnectCurrentPort(taskCallback)
return
}
taskCallback.onFailed("mIMyBinder is not initialization,the lib was wrong")
}
fun writeData(taskCallback: TaskCallback, processData: ProcessData) {
mIMyBinder?.let {
it.writeSendData(taskCallback, processData)
return
}
taskCallback.onFailed("mIMyBinder is not initialization,the lib was wrong")
}
fun writeData(byteArray: ByteArray, taskCallback: TaskCallback) {
mIMyBinder?.let {
it.write(byteArray, taskCallback)
return
}
taskCallback.onFailed("mIMyBinder is not initialization,the lib was wrong")
}
fun startPrinterService(): PrinterModel {
printer.getContext()?.let {
val service = Intent(it, PrinterService::class.java)
it.bindService(service, mServiceConnect, Service.BIND_AUTO_CREATE)
}
return this
}
}

View File

@@ -0,0 +1,50 @@
package com.huitao.printer
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*/
class PrinterProvider : ContentProvider() {
override fun onCreate(): Boolean {
context?.let {
Printer.getInstance().initPrinter(it)
}
return false
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
return null
}
override fun getType(uri: Uri): String? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
return 0
}
}

View File

@@ -0,0 +1,10 @@
package com.huitao.printer.listener
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*/
interface BondListCallback {
fun onBondListCallback(bondList:MutableList<String>)
}

View File

@@ -0,0 +1,12 @@
package com.huitao.printer.printerface
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/28 14:55
*desc :
*version :
*/
interface DeviceFoundCallback {
fun deviceFoundCallback(device:String)
}

View File

@@ -0,0 +1,40 @@
package com.huitao.printer.printerface
import android.content.Context
import com.huitao.printer.utils.PrinterDev
import com.huitao.printer.utils.RoundQueue
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 10:42
*desc :
*version :
*/
interface IMyBinder {
fun connectBtPort(var1: String, var2: TaskCallback)
fun disconnectCurrentPort(var1: TaskCallback)
fun clearBuffer()
fun checkLinkedState(var1: TaskCallback)
fun onDiscovery(
var1: Context,
portType: PrinterDev.PortType,
callback: DeviceFoundCallback
): MutableList<String>?
fun getBtAvailableDevice(): MutableList<String>
fun write(var1: ByteArray?, var2: TaskCallback)
fun writeSendData(var1: TaskCallback, var2: ProcessData)
fun acceptDataFromPrinter(var1: TaskCallback?, var2: Int)
fun readBuffer(): RoundQueue<ByteArray?>?
fun read(var1: TaskCallback)
}

View File

@@ -0,0 +1,12 @@
package com.huitao.printer.printerface
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/14 10:15
*desc :
*version :
*/
interface ProcessData {
fun processDataBeforeSend(): MutableList<ByteArray>?
}

View File

@@ -0,0 +1,14 @@
package com.huitao.printer.printerface
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 10:43
*desc :
*version :
*/
interface TaskCallback {
fun onSucceed()
fun onFailed(error:String)
}

View File

@@ -0,0 +1,271 @@
package com.huitao.printer.service
import android.app.Service
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Binder
import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.widget.Toast
import com.huitao.printer.printerface.DeviceFoundCallback
import com.huitao.printer.printerface.IMyBinder
import com.huitao.printer.printerface.ProcessData
import com.huitao.printer.printerface.TaskCallback
import com.huitao.printer.utils.PrinterDev
import com.huitao.printer.utils.ReturnMessage
import com.huitao.printer.utils.RoundQueue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.*
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 10:39
*desc :
*version :
*/
class PrinterService : Service() {
private val mMyBinder = MyBinder()
private lateinit var mPrinterDev: PrinterDev
private lateinit var mReturnMsg: ReturnMessage
private var mIsConnected = false
private var mQueue: RoundQueue<ByteArray>? = null
private var mDeviceFoundCallback: DeviceFoundCallback? = null
private fun getInstanceRoundQueue(): RoundQueue<ByteArray> {
if (this.mQueue == null) {
mQueue = RoundQueue(500)
}
return this.mQueue!!
}
override fun onCreate() {
super.onCreate()
this.mQueue = this.getInstanceRoundQueue()
}
private val mViewModelScope = CoroutineScope(Dispatchers.IO)
override fun onBind(p0: Intent?): IBinder {
return this.mMyBinder
}
inner class MyBinder : Binder(), IMyBinder {
private var mBluetoothAdapter: BluetoothAdapter? = null
private var mFond: MutableList<String>? = null
private var mBond: MutableList<String>? = null
private var mPortType: PrinterDev.PortType? = null
private val mReceiver = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
p1?.let {
if (it.action == "android.bluetooth.device.action.FOUND") {
val device =
it.getParcelableExtra<BluetoothDevice>("android.bluetooth.device.extra.DEVICE")
?: return
if (!device.name.isNullOrEmpty()) {
mFond?.forEach { found ->
if (found.split("\n").last() == device.address) {
return
}
}
mFond?.add("${device.name}\n${device.address}")
mDeviceFoundCallback?.deviceFoundCallback("${device.name} \n ${device.address}")
}
}
}
}
}
override fun connectBtPort(var1: String, var2: TaskCallback) {
mViewModelScope.launch {
mPrinterDev = PrinterDev(PrinterDev.PortType.Bluetooth, var1)
mReturnMsg = mPrinterDev.open()
mPortType = PrinterDev.PortType.Bluetooth
mViewModelScope.launch(Dispatchers.Main) {
when (mReturnMsg.getErrorCode()) {
PrinterDev.ErrorCode.OpenPortSucceed -> {
mIsConnected = true
var2.onSucceed()
}
else -> {
var2.onFailed("Bluetooth connected Failed")
}
}
}
}
}
override fun disconnectCurrentPort(var1: TaskCallback) {
mViewModelScope.launch {
mReturnMsg = mPrinterDev.close()
mViewModelScope.launch(Dispatchers.Main) {
when (mReturnMsg.getErrorCode()) {
PrinterDev.ErrorCode.ClosePortSucceed -> {
mIsConnected = false
if (mQueue != null) {
mQueue?.clear()
}
var1.onSucceed()
}
else -> {
var1.onFailed("Bluetooth disconnected failed")
}
}
}
}.start()
}
override fun clearBuffer() {
mQueue?.clear()
}
override fun checkLinkedState(var1: TaskCallback) {
mViewModelScope.launch {
if (mPrinterDev.getPortInfo().isOpened) var1.onSucceed() else var1.onFailed("")
}.start()
}
override fun onDiscovery(
var1: Context,
portType: PrinterDev.PortType,
callback: DeviceFoundCallback
): MutableList<String>? {
this.mFond = mutableListOf()
this.mBond = mutableListOf()
mDeviceFoundCallback = callback
if (portType == PrinterDev.PortType.Bluetooth) {
this.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (mBluetoothAdapter == null) {
Toast.makeText(
this@PrinterService,
"Device didn't support bluetooth !\n",
Toast.LENGTH_SHORT
).show()
return null
}
if (mBluetoothAdapter!!.isEnabled) {
if (mBluetoothAdapter!!.enable()) {
if (!mBluetoothAdapter!!.isDiscovering) {
mBluetoothAdapter!!.startDiscovery()
}
val filter = IntentFilter("android.bluetooth.device.action.FOUND")
registerReceiver(mReceiver, filter)
val pairedDevice = mBluetoothAdapter!!.bondedDevices
if (!pairedDevice.isNullOrEmpty()) {
val it = pairedDevice.iterator()
while (it.hasNext()) {
val device = it.next()
mBond?.add("${device.name}\n${device.address}")
}
} else {
Looper.prepare()
Toast.makeText(
this@PrinterService,
"no paired device",
Toast.LENGTH_SHORT
).show()
Looper.loop()
}
} else {
Toast.makeText(
this@PrinterService,
"Bluetooth is not enable !\n",
Toast.LENGTH_SHORT
).show()
}
} else {
Toast.makeText(
this@PrinterService,
"Bluetooth adapter is not enabled !\n",
Toast.LENGTH_SHORT
).show()
}
}
return this.mBond
}
override fun getBtAvailableDevice(): MutableList<String> {
this.mBluetoothAdapter?.cancelDiscovery()
return this.mFond!!
}
override fun write(var1: ByteArray?, var2: TaskCallback) {
if (var1 != null) {
mViewModelScope.launch {
mReturnMsg = mPrinterDev.write(var1)
mViewModelScope.launch(Dispatchers.Main) {
when (mReturnMsg.getErrorCode()) {
PrinterDev.ErrorCode.WriteDataSucceed -> {
mIsConnected = true
var2.onSucceed()
}
else -> {
mIsConnected = false
var2.onFailed("Write data failed ,please check the device connected")
}
}
}
}
}
}
override fun writeSendData(var1: TaskCallback, var2: ProcessData) {
val list = var2.processDataBeforeSend()
if (list == null) {
var1.onFailed("Write data is null")
} else {
mViewModelScope.launch {
list.forEach {
mReturnMsg = mPrinterDev.write(it)
}
when (mReturnMsg.getErrorCode()) {
PrinterDev.ErrorCode.WriteDataSucceed -> {
mIsConnected = true
var1.onSucceed()
}
else -> {
mIsConnected = false
var1.onFailed("Bluetooth is no connected,please connect first")
}
}
}.start()
}
}
override fun acceptDataFromPrinter(var1: TaskCallback?, var2: Int) {
val buffer = ByteArray(var2)
mViewModelScope.launch {
kotlin.runCatching {
mQueue = getInstanceRoundQueue()
mQueue?.clear()
mQueue?.addLast(buffer)
Log.i("frank", "acceptDataFromPrinter: " + Arrays.toString(mQueue!!.last))
}.onSuccess {
}.onFailure {
}
}
}
override fun readBuffer(): RoundQueue<ByteArray?>? {
return null
}
override fun read(var1: TaskCallback) {
mViewModelScope.launch {
val msg = mPrinterDev.read()
Log.d("frank", "read: $msg")
}
}
}
}

View File

@@ -0,0 +1,25 @@
package com.huitao.printer.utils
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/15 10:45
*desc :
*version :
*/
fun byteArrayOfInts(vararg ints: Int) =
ByteArray(ints.size) { pos -> ints[pos].toByte() }
fun byteMerge(b1: ByteArray, b2: ByteArray): ByteArray {
val b3 = ByteArray(b1.size + b2.size)
System.arraycopy(b1, 0, b3, 0, b1.size)
System.arraycopy(b2, 0, b3, b1.size, b2.size)
return b3
}
fun getByteLength(str: String): Int {
return str.toByteArray(charSetName()).size
}

View File

@@ -0,0 +1,190 @@
package com.huitao.printer.utils
import java.lang.StringBuilder
import java.nio.charset.Charset
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/15 9:23
*desc :
*version :
*/
object DataForSendToPrinter {
private const val horizontal_length = 32
private const val left_length = 18
private const val right_length = 14
///水平定位
fun horizontalPositioning(): ByteArray {
return byteArrayOfInts(0x09)
}
///打印并换行
fun printAndFeedLine(): ByteArray {
return byteArrayOfInts(0x0A)
}
///打印并换行
fun printAddPaperWalking(n: Int = 0x01): ByteArray {
return byteArrayOfInts(0x1B, 0x4A, n)
}
///设置字符右间距
fun setCharRightSpace(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x20, n)
}
///选择打印模式
fun selectPrintModel(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x21, n)
}
///设置绝对打印位置
fun setAbsolutePrintPosition(m: Int, n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x24, m, n)
}
///选择/取消用户自定义字符
fun selectOrCancelCustomerChar(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x25, n)
}
///定义用户自定义字符
fun defineUserDefinedCharacters(c1: Int, c2: Int, b: ByteArray): ByteArray {
return byteMerge(byteArrayOf(0x1B.toByte(), 0x26.toByte(), c1.toByte(), c2.toByte()), b)
}
//选择位图模式
fun selectBtmModel(m: Int, nL: Int, nH: Int, b: ByteArray): ByteArray {
return byteMerge(byteArrayOfInts(0x1B, 0x2A, m, nL, nH), b)
}
///选择取消下划线模式
fun selectOrCancelUnderlineModel(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x2D, n)
}
///设置默认行间距
fun setDefaultLineSpace(): ByteArray {
return byteArrayOfInts(0x1B, 0x32)
}
///设置行间距
fun setLineSpace(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x33, n)
}
///取消用户自定义字符
fun cancelUserDefineCharsets(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x3F, n)
}
///初始化打印机
fun initializePrinter(): ByteArray {
return byteArrayOfInts(0x1B, 0x40)
}
///设置横向跳格位置
fun setHorizontalMovementPosition(b: ByteArray): ByteArray {
var data = byteArrayOfInts(0x1B, 0x44)
val nul = ByteArray(1)
data = byteMerge(data, b)
data = byteMerge(data, nul)
return data
}
///选择取消加粗模式
fun selectOrCancelBoldModel(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x45, n)
}
///选择/取消双重打印模式
fun selectOrCancelDoublePrinterModel(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x47, n)
}
///打印并走纸
fun printAndFeed(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x4A, n)
}
///选择字体
fun selectFont(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x4D, n)
}
///选择国际字符集
fun selectInternationalCharsetSets(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x52, n)
}
///选择/取消顺时针旋转90
fun selectOrCancelCW90(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x56, n)
}
///设置相对横向打印位置
fun setRelativeHorizontalPrintPosition(nL: Int, nH: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x5C, nL, nH)
}
///选择对齐方式
fun selectAliment(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x61, n)
}
///允许/禁止按键
fun allowOrForbidPressButton(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x63, 0x35, n)
}
///打印并向前走纸n行
fun printAndFeedForward(n: Int): ByteArray {
return byteArrayOfInts(0x1B, 0x64, n)
}
//设置字体大小
fun selectFontSize(n: Int): ByteArray {
return byteArrayOfInts(0x1D, 0x21, n)
}
fun queryPrinterState(): ByteArray {
return byteArrayOfInts(0x1B, 0x76)
}
fun printBothColumns(str1: String, str2: String): ByteArray {
val byte1Length: Int = getByteLength(str1)
val byte2Length = getByteLength(str2)
val spaceLength = horizontal_length - byte1Length - byte2Length
val sb = StringBuilder()
sb.append(str1)
for (i in 0 until spaceLength) {
sb.append(" ")
}
sb.append(str2)
return strToBytes(sb.toString())!!
}
fun printThreeColumns(str1: String, str2: String, str3: String): ByteArray {
val sb = StringBuilder()
//由于在打印的时候商品名称大度为一行,因此不做左边长度处理
sb.append(str1)
val leftSpaceLength = left_length - getByteLength(str1) - getByteLength(str2) / 2
for (i in 0 until leftSpaceLength) {
sb.append(" ")
}
sb.append(str2)
val rightSpaceLength = right_length - getByteLength(str3) - getByteLength(str2) / 2
for (i in 0 until rightSpaceLength) {
sb.append(" ")
}
sb.delete(sb.length - 1, sb.length)
sb.append(str3)
return strToBytes(sb.toString())!!
}
}

View File

@@ -0,0 +1,18 @@
package com.huitao.printer.utils
import android.content.Context
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 11:22
*desc :
*version :
*/
data class PortInfo(
var bluetoothId: String = "",
var context: Context? = null,
var parIsOk: Boolean = false,
var isOpened: Boolean = false,
var portType: PrinterDev.PortType? = null
)

View File

@@ -0,0 +1,278 @@
package com.huitao.printer.utils
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import java.io.InputStream
import java.io.OutputStream
import java.lang.Exception
import java.util.*
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 11:08
*desc :
*version :
*/
class PrinterDev(portType: PortType, bluetoothId: String) {
enum class ErrorCode private constructor() {
OpenPortFailed,
OpenPortSucceed,
ClosePortFailed,
ClosePortSucceed,
WriteDataSucceed,
WriteDataFailed,
ReadDataSucceed,
ReadDataFailed,
UnKnownError;
}
enum class PortType private constructor() {
UnKnown,
USB,
Bluetooth,
Ethernet;
}
private var mPortInfo: PortInfo = PortInfo()
private var mPrinterPort: PrinterPort? = null
init {
this.mPortInfo.portType = portType
this.mPortInfo.bluetoothId = bluetoothId
}
private fun resetPar() {
mPortInfo = PortInfo()
this.mPrinterPort?.closePort()
this.mPrinterPort = null
}
fun open(): ReturnMessage {
return when (mPortInfo.portType) {
PortType.Bluetooth -> {
this.open(this.mPortInfo.portType!!, this.mPortInfo.bluetoothId)
}
else -> {
ReturnMessage(ErrorCode.OpenPortFailed, "Only support bluetooth !\n")
}
}
}
private fun open(portType: PortType, bluetoothId: String): ReturnMessage {
this.resetPar()
return when {
portType != PortType.Bluetooth -> ReturnMessage(
ErrorCode.OpenPortFailed,
"Port type wrong !\n"
)
!BluetoothAdapter.checkBluetoothAddress(bluetoothId) -> ReturnMessage(
ErrorCode.OpenPortFailed,
"BluetoothId wrong !/n"
)
else -> {
this.mPortInfo.bluetoothId = bluetoothId
this.mPortInfo.portType = PortType.Bluetooth
this.mPrinterPort = BluetoothPort(this.mPortInfo)
this.mPrinterPort!!.openPort()
}
}
}
@Synchronized
fun close(): ReturnMessage {
return if (this.mPrinterPort == null) ReturnMessage(
ErrorCode.ClosePortFailed,
"Not opened port !"
) else this.mPrinterPort!!.closePort()
}
fun write(data: ByteArray): ReturnMessage {
return this.mPrinterPort!!.write(data)
}
fun getPortInfo(): PortInfo {
this.mPortInfo.isOpened = this.mPrinterPort!!.portIsOpen()
return this.mPortInfo
}
fun read(buffer: ByteArray): ReturnMessage {
return this.read(buffer, 0, buffer.size)
}
fun read(buffer: ByteArray, offset: Int, count: Int): ReturnMessage {
val returnMessage: ReturnMessage? = mPrinterPort?.read(buffer, offset, count)
return returnMessage!!
}
fun read(): Int {
val byte = ByteArray(1)
return if (this.read(byte, 0, 1)
.getErrorCode() == ErrorCode.ReadDataSucceed
) byte[0].toInt() else -1
}
private inner class BluetoothPort(portInfo: PortInfo) : PrinterPort(portInfo) {
private val SPP_UUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
private var mBluetoothAdapter: BluetoothAdapter? = null
private var mBluetoothDevice: BluetoothDevice? = null
private var mBluetoothSocket: BluetoothSocket? = null
private var mOutputStream: OutputStream? = null
private var mInputStream: InputStream? = null
init {
when {
portInfo.portType == PortType.Bluetooth && BluetoothAdapter.checkBluetoothAddress(
portInfo.bluetoothId
) -> {
this.mPortInfo?.parIsOk = true
this.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
}
else -> this.mPortInfo?.parIsOk = false
}
}
override fun openPort(): ReturnMessage {
if (!this.mPortInfo!!.parIsOk) {
return ReturnMessage(
ErrorCode.OpenPortFailed,
"PortInfo error !\n"
)
} else {
try {
if (this.mBluetoothAdapter == null) {
return ReturnMessage(
ErrorCode.OpenPortFailed,
"Not Bluetooth adapter !\n"
)
}
if (!this.mBluetoothAdapter!!.isEnabled) {
return ReturnMessage(
ErrorCode.OpenPortFailed,
"Bluetooth adapter was closed !\n"
)
}
this.mBluetoothAdapter?.cancelDiscovery()
this.mBluetoothDevice =
this.mBluetoothAdapter?.getRemoteDevice(this.mPortInfo?.bluetoothId)
this.mBluetoothSocket =
this.mBluetoothDevice?.createRfcommSocketToServiceRecord(this.SPP_UUID)
this.mBluetoothSocket?.connect()
this.mOutputStream = null
this.mOutputStream = this.mBluetoothSocket?.outputStream
this.mInputStream = this.mBluetoothSocket?.inputStream
this.mIsOpen = true
} catch (e: Exception) {
return ReturnMessage(ErrorCode.OpenPortFailed, e.toString())
}
}
return ReturnMessage(ErrorCode.OpenPortSucceed, "Open bluetooth port success !\n")
}
override fun closePort(): ReturnMessage {
try {
if (this.mOutputStream != null) {
this.mOutputStream!!.flush()
}
if (this.mBluetoothSocket != null) {
this.mBluetoothSocket?.close()
}
this.mIsOpen = false
this.mOutputStream = null
this.mInputStream = null
} catch (e: Exception) {
return ReturnMessage(ErrorCode.ClosePortFailed, e.toString())
}
return ReturnMessage(ErrorCode.ClosePortSucceed, "Close bluetooth port success !\n")
}
override fun write(var1: Int): ReturnMessage {
if (this.mIsOpen && this.mBluetoothSocket!!.isConnected && mOutputStream != null) {
try {
this.mOutputStream?.write(var1)
this.mOutputStream?.flush()
} catch (e: Exception) {
this.closePort()
return ReturnMessage(ErrorCode.WriteDataFailed, e.toString())
}
return ReturnMessage(ErrorCode.WriteDataSucceed, "Send 1 byte !\n", 1)
} else {
return ReturnMessage(ErrorCode.WriteDataFailed, "Bluetooth port was closed !\n")
}
}
override fun write(var1: ByteArray): ReturnMessage {
if (this.mIsOpen && this.mBluetoothSocket!!.isConnected && mOutputStream != null) {
try {
this.mOutputStream?.write(var1)
this.mOutputStream?.flush()
} catch (e: Exception) {
this.closePort()
return ReturnMessage(ErrorCode.WriteDataFailed, e.toString())
}
return ReturnMessage(ErrorCode.WriteDataSucceed, "Send ${var1.size} bytes !\n")
} else {
return ReturnMessage(ErrorCode.WriteDataFailed, "Bluetooth port was closed !\n")
}
}
override fun write(var1: ByteArray, var2: Int, var3: Int): ReturnMessage {
if (this.mIsOpen && this.mBluetoothSocket!!.isConnected && mOutputStream != null) {
try {
this.mOutputStream?.write(var1, var2, var3)
this.mOutputStream?.flush()
} catch (e: Exception) {
this.closePort()
return ReturnMessage(ErrorCode.WriteDataFailed, e.toString())
}
return ReturnMessage(ErrorCode.WriteDataSucceed, "Send $var3 bytes !\n ", var3)
} else {
return ReturnMessage(ErrorCode.WriteDataFailed, "Bluetooth port was closed")
}
}
override fun read(): Int {
return if (this.mIsOpen && this.mBluetoothSocket!!.isConnected && this.mInputStream != null) {
try {
this.mInputStream!!.read()
} catch (e: Exception) {
-1
}
} else {
-1
}
}
override fun read(var1: ByteArray): ReturnMessage {
return this.read(var1, 0, var1.size)
}
override fun read(var1: ByteArray, var2: Int, var3: Int): ReturnMessage {
if (this.mIsOpen && this.mBluetoothSocket!!.isConnected && this.mInputStream != null) {
val readBytes: Int
try {
readBytes = this.mInputStream!!.read(var1, var2, var3)
} catch (e: Exception) {
return ReturnMessage(ErrorCode.ReadDataFailed, e.toString())
}
return ReturnMessage(ErrorCode.ReadDataSucceed, "Read $var3 bytes !\n", readBytes)
} else {
return ReturnMessage(ErrorCode.ReadDataFailed, "Bluetooth port was close !\n")
}
}
override fun portIsOpen(): Boolean {
val b = ByteArray(4)
val msg: ReturnMessage = this.read(b)
this.mIsOpen = msg.getReadByteCount() != -1
return this.mIsOpen
}
}
}

View File

@@ -0,0 +1,50 @@
package com.huitao.printer.utils
import java.util.*
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 11:36
*desc :
*version :
*/
abstract class PrinterPort() {
protected var mPortInfo: PortInfo? = null
protected var mIsOpen = false
protected var mRxdQueue: Queue<Byte>? = null
private var mTxdQueue: Queue<Byte>? = null
abstract fun openPort(): ReturnMessage
abstract fun closePort(): ReturnMessage
abstract fun write(var1: Int): ReturnMessage
abstract fun write(var1: ByteArray): ReturnMessage
abstract fun write(var1: ByteArray, var2: Int, var3: Int): ReturnMessage
abstract fun read(): Int
abstract fun read(var1: ByteArray): ReturnMessage
abstract fun read(var1: ByteArray, var2: Int, var3: Int): ReturnMessage
abstract fun portIsOpen(): Boolean
constructor(portInfo: PortInfo) : this() {
this.mPortInfo = portInfo
}
fun getRxdCount(): Int {
return if (mRxdQueue != null) mRxdQueue!!.size else 0
}
fun getTxdCount(): Int {
return if (mTxdQueue != null) mTxdQueue!!.size else 0
}
}

View File

@@ -0,0 +1,59 @@
package com.huitao.printer.utils
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/13 11:39
*desc :
*version :
*/
class ReturnMessage() {
private var mErrorCode: PrinterDev.ErrorCode? = null
private var mErrorStrings: String? = null
private var mReadBytes: Int? = null
private var mWriteBytes: Int? = null
init {
this.mErrorCode = PrinterDev.ErrorCode.UnKnownError
this.mErrorStrings = "unknown error \n"
this.mReadBytes = -1
this.mWriteBytes = -1
}
constructor(ec: PrinterDev.ErrorCode, es: String) : this() {
this.mWriteBytes = -1
this.mReadBytes = -1
this.mErrorStrings = es
this.mErrorCode = ec
}
constructor(ec: PrinterDev.ErrorCode, es: String, count: Int) : this() {
this.mErrorCode = ec
this.mErrorStrings = es
this.mReadBytes = -1
this.mWriteBytes = -1
when (count) {
6 -> this.mReadBytes = count
7 -> this.mReadBytes = count
}
}
fun getErrorCode(): PrinterDev.ErrorCode {
return this.mErrorCode!!
}
fun getErrorString(): String {
return this.mErrorStrings!!
}
fun getReadByteCount(): Int {
return this.mReadBytes!!
}
fun getWriteByCount(): Int {
return this.mWriteBytes!!
}
}

View File

@@ -0,0 +1,102 @@
package com.huitao.printer.utils;
import java.io.Serializable;
import java.util.NoSuchElementException;
/**
* author : huitao
* e-mail : pig.huitao@gmail.com
* date : 2021/4/13 14:27
* desc :
* version :
*/
public class RoundQueue<T> implements Serializable {
private static final long serialVersionUID = -873109114121357176L;
private T[] queue;
private int head = 0;
private int tail = 0;
private int realSize = 0;
public RoundQueue(int size) {
this.queue = (T[]) new Object[size <= 0 ? 500 : size];
}
public void addLast(T element) {
if (this.isFull()) {
this.removeFirst();
}
this.tail = (this.head + this.realSize) % this.queue.length;
this.queue[this.tail] = element;
++this.realSize;
}
public T removeFirst() {
if (this.isEmpty()) {
throw new NoSuchElementException();
} else {
T tempLog = this.queue[this.head];
this.queue[this.head] = null;
this.head = (this.head + 1) % this.queue.length;
--this.realSize;
return tempLog;
}
}
public int realSize() {
return this.realSize;
}
public boolean isEmpty() {
return this.realSize() == 0;
}
public boolean isFull() {
return this.realSize() == this.queue.length;
}
public void clear() {
while (!this.isEmpty()) {
this.removeFirst();
}
}
public T get(int index) {
if (index >= 0 && index < this.realSize) {
return this.queue[index];
} else {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.realSize);
}
}
public int indexOf(T key) {
if (key == null) {
return -1;
} else {
for (int index = 0; index <= this.realSize() - 1; ++index) {
if (key.equals(this.queue[index])) {
return index;
}
}
return -1;
}
}
public int getHead() {
return this.head;
}
public int getTail() {
return this.tail;
}
public T getLast() {
return this.queue[this.tail];
}
public T getFirst() {
return this.queue[this.head];
}
}

View File

@@ -0,0 +1,43 @@
package com.huitao.printer.utils
import android.util.Log
import java.lang.Exception
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.nio.charset.StandardCharsets.UTF_8
/**
*author : huitao
*e-mail : pig.huitao@gmail.com
*date : 2021/4/15 13:51
*desc :
*version :
*/
fun charSetName(): Charset = Charset.forName("gbk")
fun strToBytes(str: String): ByteArray? {
val data: ByteArray
try {
val b = str.toByteArray(charset = StandardCharsets.UTF_8)
data = String(b).toByteArray(Charset.forName("gbk"))
} catch (e: Exception) {
return null
}
return data
}
fun strToBytes(str: String, charset: String): ByteArray? {
val data: ByteArray?
try {
val b = str.toByteArray(charset = Charsets.UTF_8)
data = String(b).toByteArray(Charset.forName("gbk"))
} catch (e: Exception) {
return null
}
return data
}

View File

@@ -0,0 +1,17 @@
package com.huitao.printer
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}