打印机封装
This commit is contained in:
+34
-11
@@ -1,25 +1,47 @@
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Properties
|
||||
import java.io.File
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
}
|
||||
|
||||
// 自动版本号管理
|
||||
val versionPropsFile = file("version.properties")
|
||||
fun getVersionCode(): Int {
|
||||
val props = Properties()
|
||||
if (versionPropsFile.exists()) {
|
||||
props.load(versionPropsFile.inputStream())
|
||||
}
|
||||
val code = props.getProperty("VERSION_CODE", "1").toInt()
|
||||
props.setProperty("VERSION_CODE", (code + 1).toString())
|
||||
props.store(versionPropsFile.outputStream(), null)
|
||||
return code
|
||||
}
|
||||
|
||||
fun getVersionName(): String {
|
||||
val props = Properties()
|
||||
if (versionPropsFile.exists()) {
|
||||
props.load(versionPropsFile.inputStream())
|
||||
}
|
||||
return props.getProperty("VERSION_NAME", "1.2")
|
||||
}
|
||||
|
||||
val autoVersionCode = getVersionCode()
|
||||
val autoVersionName = getVersionName()
|
||||
|
||||
android {
|
||||
namespace = "com.example.ops_android"
|
||||
compileSdk {
|
||||
version = release(36) {
|
||||
minorApiLevel = 1
|
||||
}
|
||||
}
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.ops_android"
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
versionCode = 2
|
||||
versionName = "1.1"
|
||||
versionCode = autoVersionCode
|
||||
versionName = autoVersionName
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -50,7 +72,6 @@ android {
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
aidl = true
|
||||
buildConfig = true
|
||||
}
|
||||
}
|
||||
@@ -58,11 +79,9 @@ android {
|
||||
androidComponents {
|
||||
onVariants { variant ->
|
||||
variant.outputs.forEach { output ->
|
||||
val versionName = "1.1"
|
||||
val versionCode = "2"
|
||||
val buildType = variant.buildType
|
||||
val date = SimpleDateFormat("yyyyMMdd").format(Date())
|
||||
output.outputFileName.set("PDA_Printer_Test_v${versionName}_${versionCode}_${buildType}_${date}.apk")
|
||||
output.outputFileName.set("PDA_Printer_Test_v${autoVersionName}_${autoVersionCode}_${buildType}_${date}.apk")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,6 +95,10 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
|
||||
// 打印SDK
|
||||
implementation(files("libs/lcprintsdk-release.aar"))
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
|
||||
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
package android.bld;
|
||||
|
||||
interface IPrintManager {
|
||||
void initPrinter();
|
||||
void closePrinter();
|
||||
void printText(String text);
|
||||
void printText2(String text);
|
||||
void printQR(String content, int size);
|
||||
void printBarcode(String content, int type, int width, int height);
|
||||
void printBitmap(in byte[] bitmap, int width, int height);
|
||||
void printLine(int lineCount);
|
||||
void start();
|
||||
void setFontSize(int size);
|
||||
void setTextBold(boolean bold);
|
||||
void setUnderLine(boolean underline);
|
||||
void setConcentration(int level);
|
||||
void setFeedPaperSpace(int space);
|
||||
void setUnwindPaperLen(int length);
|
||||
void printEnableMark(boolean enable);
|
||||
void printGoToNextMark();
|
||||
}
|
||||
@@ -35,6 +35,11 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
printerManager.bindService()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
printerManager.unbindService()
|
||||
@@ -49,6 +54,14 @@ fun PrinterTestScreen(
|
||||
var statusMessage by remember { mutableStateOf("未连接打印服务") }
|
||||
var isConnected by remember { mutableStateOf(false) }
|
||||
|
||||
DisposableEffect(printerManager) {
|
||||
printerManager.setConnectionCallback { connected, message ->
|
||||
isConnected = connected
|
||||
statusMessage = message
|
||||
}
|
||||
onDispose { }
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
@@ -56,14 +69,12 @@ fun PrinterTestScreen(
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 标题
|
||||
Text(
|
||||
text = "PDA 打印机测试",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
|
||||
// 版本信息
|
||||
Text(
|
||||
text = "v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) - ${BuildConfig.AUTHOR}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
@@ -71,7 +82,7 @@ fun PrinterTestScreen(
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
|
||||
// 状态显示
|
||||
// ── 状态卡片 ──────────────────────────────────────
|
||||
Card(
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = if (isConnected)
|
||||
@@ -87,48 +98,40 @@ fun PrinterTestScreen(
|
||||
)
|
||||
}
|
||||
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
|
||||
// 连接控制区
|
||||
// ══════════════════════════════════════════════════
|
||||
// 连接控制
|
||||
// ══════════════════════════════════════════════════
|
||||
Text("连接控制", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
val success = printerManager.bindService()
|
||||
isConnected = success
|
||||
statusMessage = if (success) "正在连接打印服务..." else "连接失败"
|
||||
},
|
||||
onClick = { printerManager.bindService() },
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("绑定服务")
|
||||
}
|
||||
) { Text("绑定服务") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.initPrinter().fold(
|
||||
printerManager.initDevice().fold(
|
||||
onSuccess = { statusMessage = "打印机初始化成功" },
|
||||
onFailure = { statusMessage = "初始化失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("初始化")
|
||||
}
|
||||
) { Text("初始化") }
|
||||
}
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.closePrinter().fold(
|
||||
printerManager.closeDevice().fold(
|
||||
onSuccess = { statusMessage = "打印机已关闭" },
|
||||
onFailure = { statusMessage = "关闭失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("关闭打印机")
|
||||
}
|
||||
) { Text("关闭打印机") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
@@ -137,186 +140,248 @@ fun PrinterTestScreen(
|
||||
statusMessage = "服务已解绑"
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("解绑服务")
|
||||
}
|
||||
) { Text("解绑服务") }
|
||||
}
|
||||
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
|
||||
// 基础打印测试
|
||||
Text("基础打印测试", style = MaterialTheme.typography.titleMedium)
|
||||
// ══════════════════════════════════════════════════
|
||||
// 快速打印
|
||||
// ══════════════════════════════════════════════════
|
||||
Text("快速打印", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.quickPrintText("Hello Printer!\n测试打印\n").fold(
|
||||
printerManager.quickText("Hello Printer!\n测试打印\n").fold(
|
||||
onSuccess = { statusMessage = "快速打印成功" },
|
||||
onFailure = { statusMessage = "打印失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("快速打印测试")
|
||||
) { Text("快速打印测试") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.buildJob()
|
||||
.text("工单号: WO-001\n操作员: 张三\n")
|
||||
.build()
|
||||
.fold(
|
||||
onSuccess = { statusMessage = "PrintJob 打印成功" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text("PrintJob 构建器测试") }
|
||||
|
||||
HorizontalDivider()
|
||||
|
||||
// ══════════════════════════════════════════════════
|
||||
// 单步打印(手动组合)
|
||||
// ══════════════════════════════════════════════════
|
||||
Text("单步打印", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = {
|
||||
printerManager.addText("手动 text\n").fold(
|
||||
onSuccess = { statusMessage = "文字已加入缓冲区" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) { Text("添加文字") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = {
|
||||
printerManager.addQR("https://example.com").fold(
|
||||
onSuccess = { statusMessage = "QR 已加入缓冲区" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) { Text("添加QR") }
|
||||
}
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.printText("使用 printText\n").fold(
|
||||
onSuccess = { statusMessage = "printText 调用成功" },
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = {
|
||||
printerManager.addBarcode("1234567890").fold(
|
||||
onSuccess = { statusMessage = "条码已加入缓冲区" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("printText")
|
||||
}
|
||||
) { Text("添加条码") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.printText2("使用 printText2\n").fold(
|
||||
onSuccess = { statusMessage = "printText2 调用成功" },
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = {
|
||||
printerManager.addLineFeed(3).fold(
|
||||
onSuccess = { statusMessage = "空行已加入缓冲区" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("printText2")
|
||||
}
|
||||
) { Text("添加空行") }
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.start().fold(
|
||||
onSuccess = { statusMessage = "开始打印(出纸)" },
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = {
|
||||
printerManager.commit().fold(
|
||||
onSuccess = { statusMessage = "已提交打印" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("start() - 提交打印")
|
||||
}
|
||||
) { Text("提交打印 (start)") }
|
||||
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
|
||||
// 样式测试
|
||||
// ══════════════════════════════════════════════════
|
||||
// 样式设置
|
||||
// ══════════════════════════════════════════════════
|
||||
Text("样式设置", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.setFontSize(24).fold(
|
||||
onSuccess = { statusMessage = "字体大小已设置" },
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = { statusMessage = "设备已打开,可设置样式" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
printerManager.setFontSize(32).fold(
|
||||
onSuccess = { statusMessage = "大字体已设置" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("大字体")
|
||||
}
|
||||
) { Text("大字体") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.setTextBold(true).fold(
|
||||
onSuccess = { statusMessage = "已设置粗体" },
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = { statusMessage = "设备已打开" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
printerManager.setBold(true).fold(
|
||||
onSuccess = { statusMessage = "粗体已设置" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("粗体")
|
||||
}
|
||||
) { Text("粗体") }
|
||||
}
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.setUnderLine(true).fold(
|
||||
onSuccess = { statusMessage = "已设置下划线" },
|
||||
printerManager.setUnderline(true).fold(
|
||||
onSuccess = { statusMessage = "下划线已设置" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("下划线")
|
||||
}
|
||||
) { Text("下划线") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.setConcentration(5).fold(
|
||||
printerManager.setDensity(5).fold(
|
||||
onSuccess = { statusMessage = "浓度已设置" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("浓度")
|
||||
}
|
||||
) { Text("浓度") }
|
||||
}
|
||||
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
|
||||
// 二维码/条形码测试
|
||||
Text("二维码与条形码", style = MaterialTheme.typography.titleMedium)
|
||||
// ══════════════════════════════════════════════════
|
||||
// 标签打印
|
||||
// ══════════════════════════════════════════════════
|
||||
Text("标签打印(黑标对齐)", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.printQR("https://example.com", 6).fold(
|
||||
onSuccess = { statusMessage = "二维码已加入队列" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
printerManager.quickLabel("标签打印测试\n工单号: WO-001\n").fold(
|
||||
onSuccess = { statusMessage = "标签打印成功" },
|
||||
onFailure = { statusMessage = "标签打印失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("打印二维码")
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.printBarcode("1234567890", 0, 2, 60).fold(
|
||||
onSuccess = { statusMessage = "条形码已加入队列" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text("打印条形码")
|
||||
}
|
||||
|
||||
Divider()
|
||||
|
||||
// 走纸控制
|
||||
Text("走纸控制", style = MaterialTheme.typography.titleMedium)
|
||||
) { Text("快速标签打印") }
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.printLine(3).fold(
|
||||
onSuccess = { statusMessage = "打印空行" },
|
||||
printerManager.enableMarkDetection(true).fold(
|
||||
onSuccess = { statusMessage = "已开启黑标检测" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("打印3空行")
|
||||
}
|
||||
) { Text("开启黑标") }
|
||||
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.setFeedPaperSpace(100).fold(
|
||||
onSuccess = { statusMessage = "走纸间距已设置" },
|
||||
printerManager.enableMarkDetection(false).fold(
|
||||
onSuccess = { statusMessage = "已关闭黑标检测" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text("走纸间距")
|
||||
}
|
||||
) { Text("关闭黑标") }
|
||||
}
|
||||
|
||||
Divider()
|
||||
Button(
|
||||
onClick = {
|
||||
printerManager.ensureOpen().fold(
|
||||
onSuccess = {
|
||||
printerManager.enableMarkDetection(true).fold(
|
||||
onSuccess = {
|
||||
printerManager.gotoNextMark().fold(
|
||||
onSuccess = { statusMessage = "已移动到下一标签" },
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
onFailure = { statusMessage = "失败: ${it.message}" }
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) { Text("下一标签") }
|
||||
|
||||
// 综合测试
|
||||
HorizontalDivider()
|
||||
|
||||
// ══════════════════════════════════════════════════
|
||||
// 综合测试
|
||||
// ══════════════════════════════════════════════════
|
||||
Text("综合测试", style = MaterialTheme.typography.titleMedium)
|
||||
|
||||
Button(
|
||||
@@ -327,9 +392,7 @@ fun PrinterTestScreen(
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
) {
|
||||
Text("运行完整测试")
|
||||
}
|
||||
) { Text("运行完整测试") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,54 +402,47 @@ private fun runComprehensiveTest(
|
||||
) {
|
||||
onStatus("开始综合测试...")
|
||||
|
||||
printerManager.initPrinter().onFailure {
|
||||
printerManager.initDevice().onFailure {
|
||||
onStatus("初始化失败: ${it.message}")
|
||||
return
|
||||
}
|
||||
|
||||
// 标题
|
||||
printerManager.setFontSize(32)
|
||||
printerManager.setTextBold(true)
|
||||
printerManager.printText2("=== 打印测试 ===\n")
|
||||
printerManager.setBold(true)
|
||||
printerManager.addText("=== 打印测试 ===\n")
|
||||
|
||||
// 普通文本
|
||||
printerManager.setFontSize(24)
|
||||
printerManager.setTextBold(false)
|
||||
printerManager.printText2("普通文本测试\n")
|
||||
printerManager.setBold(false)
|
||||
printerManager.addText("普通文本测试\n")
|
||||
|
||||
// 粗体文本
|
||||
printerManager.setTextBold(true)
|
||||
printerManager.printText2("粗体文本测试\n")
|
||||
printerManager.setTextBold(false)
|
||||
printerManager.setBold(true)
|
||||
printerManager.addText("粗体文本测试\n")
|
||||
printerManager.setBold(false)
|
||||
|
||||
// 下划线文本
|
||||
printerManager.setUnderLine(true)
|
||||
printerManager.printText2("下划线文本测试\n")
|
||||
printerManager.setUnderLine(false)
|
||||
printerManager.setUnderline(true)
|
||||
printerManager.addText("下划线文本测试\n")
|
||||
printerManager.setUnderline(false)
|
||||
|
||||
// 空行
|
||||
printerManager.printLine(1)
|
||||
printerManager.addLineFeed(1)
|
||||
|
||||
// 二维码
|
||||
printerManager.printText2("二维码测试:\n")
|
||||
printerManager.printQR("https://example.com", 6)
|
||||
printerManager.printLine(1)
|
||||
printerManager.addText("二维码测试:\n")
|
||||
printerManager.addQR("https://example.com")
|
||||
printerManager.addLineFeed(1)
|
||||
|
||||
// 条形码
|
||||
printerManager.printText2("条形码测试:\n")
|
||||
printerManager.printBarcode("123456789012", 0, 2, 60)
|
||||
printerManager.printLine(3)
|
||||
printerManager.addText("条形码测试:\n")
|
||||
printerManager.addBarcode("123456789012")
|
||||
printerManager.addLineFeed(3)
|
||||
|
||||
// 分隔线
|
||||
printerManager.printText2("--------------------------------\n")
|
||||
printerManager.printText2("测试完成\n")
|
||||
printerManager.printLine(2)
|
||||
printerManager.addText("--------------------------------\n")
|
||||
printerManager.addText("测试完成\n")
|
||||
printerManager.addLineFeed(2)
|
||||
|
||||
// 提交打印
|
||||
printerManager.start().fold(
|
||||
onSuccess = { onStatus("综合测试完成,开始打印") },
|
||||
printerManager.commit().fold(
|
||||
onSuccess = {
|
||||
onStatus("综合测试完成,开始打印")
|
||||
Thread.sleep(1500)
|
||||
printerManager.closeDevice()
|
||||
},
|
||||
onFailure = { onStatus("提交打印失败: ${it.message}") }
|
||||
)
|
||||
|
||||
printerManager.closePrinter()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.example.ops_android.printer
|
||||
|
||||
class PrintJob(private val manager: PrinterManager) {
|
||||
|
||||
private var textContent: String? = null
|
||||
private var qrContent: String? = null
|
||||
private var barcodeContent: String? = null
|
||||
private var lineFeeds: Int = 0
|
||||
private var useLabelMode: Boolean = false
|
||||
|
||||
fun text(text: String): PrintJob {
|
||||
textContent = text
|
||||
return this
|
||||
}
|
||||
|
||||
fun qr(content: String): PrintJob {
|
||||
qrContent = content
|
||||
return this
|
||||
}
|
||||
|
||||
fun barcode(content: String): PrintJob {
|
||||
barcodeContent = content
|
||||
return this
|
||||
}
|
||||
|
||||
fun lineFeed(count: Int = 1): PrintJob {
|
||||
lineFeeds = count
|
||||
return this
|
||||
}
|
||||
|
||||
fun labelMode(): PrintJob {
|
||||
useLabelMode = true
|
||||
return this
|
||||
}
|
||||
|
||||
/** 初始化 → 添加内容 → 提交 → 等待 → 关闭 */
|
||||
fun build(): Result<Unit> = runCatching {
|
||||
manager.initDevice().getOrThrow()
|
||||
|
||||
if (useLabelMode)
|
||||
manager.enableMarkDetection(true).getOrThrow()
|
||||
|
||||
textContent?.let { manager.addText(it).getOrThrow() }
|
||||
qrContent?.let { manager.addQR(it).getOrThrow() }
|
||||
barcodeContent?.let { manager.addBarcode(it).getOrThrow() }
|
||||
if (lineFeeds > 0) manager.addLineFeed(lineFeeds).getOrThrow()
|
||||
|
||||
manager.commit().getOrThrow()
|
||||
Thread.sleep(1500)
|
||||
manager.closeDevice().getOrThrow()
|
||||
}
|
||||
|
||||
/** 初始化 → 添加内容 → 提交(不关闭) */
|
||||
fun print(): Result<Unit> = runCatching {
|
||||
manager.ensureOpen().getOrThrow()
|
||||
|
||||
if (useLabelMode)
|
||||
manager.enableMarkDetection(true).getOrThrow()
|
||||
|
||||
textContent?.let { manager.addText(it).getOrThrow() }
|
||||
qrContent?.let { manager.addQR(it).getOrThrow() }
|
||||
barcodeContent?.let { manager.addBarcode(it).getOrThrow() }
|
||||
if (lineFeeds > 0) manager.addLineFeed(lineFeeds).getOrThrow()
|
||||
|
||||
manager.commit().getOrThrow()
|
||||
}
|
||||
}
|
||||
@@ -1,136 +1,322 @@
|
||||
package com.example.ops_android.printer
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.bld.IPrintManager
|
||||
import android.bld.PrintManager
|
||||
import android.bld.print.aidl.PrinterBinderListener
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
|
||||
class PrinterManager(private val context: Context) {
|
||||
private var printService: IPrintManager? = null
|
||||
private var isBound = false
|
||||
|
||||
// ── 内部状态 ────────────────────────────────────────────
|
||||
internal var printManager: PrintManager? = null
|
||||
private set
|
||||
@Volatile internal var isOpen = false
|
||||
private set
|
||||
@Volatile private var intentionalClose = false
|
||||
private var connectionCallback: ((Boolean, String) -> Unit)? = null
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
|
||||
companion object {
|
||||
private const val TAG = "PrinterManager"
|
||||
private const val PRINT_SERVICE_ACTION = "android.bld.PrintManager"
|
||||
private const val PRINT_SERVICE_PACKAGE = "android.bld"
|
||||
}
|
||||
private const val CONNECT_TIMEOUT_MS = 10_000L
|
||||
|
||||
private val serviceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
printService = IPrintManager.Stub.asInterface(service)
|
||||
isBound = true
|
||||
Log.d(TAG, "Print service connected")
|
||||
}
|
||||
// 错误码
|
||||
internal const val ERROR_NO_ERROR = 0
|
||||
internal const val ERROR_DEV_IS_BUSY = 1
|
||||
internal const val ERROR_PRINT_HOT = 2
|
||||
internal const val ERROR_PRINT_NOPAPER = 3
|
||||
internal const val ERROR_DEV_NO_BATTERY = 4
|
||||
internal const val ERROR_DEV_FEED = 5
|
||||
internal const val ERROR_DEV_PRINT = 6
|
||||
internal const val ERROR_DEV_BMARK = 7
|
||||
internal const val ERROR_DEV_NOT_OPEN = 16
|
||||
internal const val ERROR_NO_DATA = 17
|
||||
internal const val ERROR_DATA_INVALID = 18
|
||||
internal const val ERROR_CMD = 19
|
||||
internal const val ERROR_GRAY_INVALID = 20
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
printService = null
|
||||
isBound = false
|
||||
Log.d(TAG, "Print service disconnected")
|
||||
fun getErrorMessage(errorCode: Int): String = when (errorCode) {
|
||||
ERROR_NO_ERROR -> "正常"
|
||||
ERROR_DEV_IS_BUSY -> "设备忙"
|
||||
ERROR_PRINT_HOT -> "打印头过热"
|
||||
ERROR_PRINT_NOPAPER -> "缺纸"
|
||||
ERROR_DEV_NO_BATTERY -> "电量不足"
|
||||
ERROR_DEV_FEED -> "走纸错误"
|
||||
ERROR_DEV_PRINT -> "打印错误"
|
||||
ERROR_DEV_BMARK -> "黑标错误"
|
||||
ERROR_DEV_NOT_OPEN -> "设备未打开"
|
||||
ERROR_NO_DATA -> "无数据"
|
||||
ERROR_DATA_INVALID -> "数据无效"
|
||||
ERROR_CMD -> "指令错误"
|
||||
ERROR_GRAY_INVALID -> "浓度无效"
|
||||
165 -> "数据输入错误"
|
||||
else -> "未知错误($errorCode)"
|
||||
}
|
||||
}
|
||||
|
||||
fun bindService(): Boolean {
|
||||
return try {
|
||||
val intent = Intent(PRINT_SERVICE_ACTION).apply {
|
||||
setPackage(PRINT_SERVICE_PACKAGE)
|
||||
// ── 连接回调 ───(运行在 Binder 线程,需切换到主线程)──────
|
||||
private val printerListener = object : PrinterBinderListener {
|
||||
override fun onPrintCallback(status: Int) {
|
||||
Log.d(TAG, "Printer callback: status=$status (${getErrorMessage(status)})")
|
||||
mainHandler.post {
|
||||
when (status) {
|
||||
ERROR_NO_ERROR -> {
|
||||
isOpen = true
|
||||
connectionCallback?.invoke(true, "打印机已就绪")
|
||||
}
|
||||
ERROR_DEV_NOT_OPEN -> {
|
||||
if (intentionalClose) return@post
|
||||
isOpen = false
|
||||
connectionCallback?.invoke(false, "设备未打开,请重新连接")
|
||||
}
|
||||
else -> connectionCallback?.invoke(false, getErrorMessage(status))
|
||||
}
|
||||
}
|
||||
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to bind print service", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun setConnectionCallback(callback: (Boolean, String) -> Unit) {
|
||||
connectionCallback = callback
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 连接管理
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
fun bindService() {
|
||||
notifyConnection(false, "正在连接打印服务...")
|
||||
Thread {
|
||||
try {
|
||||
kotlinx.coroutines.runBlocking {
|
||||
withTimeout(CONNECT_TIMEOUT_MS) {
|
||||
withContext(Dispatchers.IO) {
|
||||
intentionalClose = false
|
||||
Log.d(TAG, "Attempting to get PrintManager instance...")
|
||||
val manager = PrintManager.getDefaultInstance(context)
|
||||
if (manager == null) {
|
||||
notifyConnection(false, "设备不支持打印功能")
|
||||
return@withContext
|
||||
}
|
||||
printManager = manager
|
||||
manager.addPrintListener(printerListener)
|
||||
Log.d(TAG, "Opening printer...")
|
||||
val success = manager.open()
|
||||
Log.d(TAG, "open() returned: $success")
|
||||
if (success) {
|
||||
isOpen = true
|
||||
val version = manager.getPrinterVer()
|
||||
if (version != null) {
|
||||
notifyConnection(true, "打印机已连接 (固件: $version)")
|
||||
} else {
|
||||
notifyConnection(true, "打印机已连接")
|
||||
}
|
||||
} else {
|
||||
isOpen = false
|
||||
notifyConnection(false, "打印机打开失败,请检查设备")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: TimeoutCancellationException) {
|
||||
Log.e(TAG, "bindService timeout after ${CONNECT_TIMEOUT_MS}ms")
|
||||
notifyConnection(false, "连接超时,请检查打印服务是否运行")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to open print service", e)
|
||||
notifyConnection(false, "连接异常: ${e.message}")
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun unbindService() {
|
||||
if (isBound) {
|
||||
context.unbindService(serviceConnection)
|
||||
isBound = false
|
||||
Thread {
|
||||
try {
|
||||
if (isOpen) {
|
||||
printManager?.removePrintListener(printerListener)
|
||||
printManager?.close()
|
||||
isOpen = false
|
||||
notifyConnection(false, "服务已断开")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to close print service", e)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun isConnected(): Boolean = isOpen && printManager != null
|
||||
|
||||
fun getFirmwareVersion(): String? {
|
||||
return try {
|
||||
printManager?.getPrinterVer()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to get printer version", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// 基础连接方法
|
||||
fun initPrinter(): Result<Unit> = runCatching {
|
||||
printService?.initPrinter() ?: throw Exception("Print service not connected")
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 设备控制
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
fun ensureOpen(): Result<Unit> = runCatching {
|
||||
checkPrinter()
|
||||
if (!isOpen) {
|
||||
intentionalClose = false
|
||||
val opened = printManager?.open() ?: false
|
||||
if (!opened) throw Exception("设备未打开,请重新绑定服务")
|
||||
isOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
fun closePrinter(): Result<Unit> = runCatching {
|
||||
printService?.closePrinter() ?: throw Exception("Print service not connected")
|
||||
fun initDevice(): Result<Unit> = runCatching {
|
||||
ensureOpen().getOrThrow()
|
||||
val result = printManager!!.reset()
|
||||
if (result != ERROR_NO_ERROR) throw Exception("初始化失败: ${getErrorMessage(result)}")
|
||||
}
|
||||
|
||||
// 打印方法
|
||||
fun printText(text: String): Result<Unit> = runCatching {
|
||||
printService?.printText(text) ?: throw Exception("Print service not connected")
|
||||
fun closeDevice(): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
isOpen = false
|
||||
intentionalClose = true
|
||||
printManager?.close()
|
||||
}
|
||||
|
||||
fun printText2(text: String): Result<Unit> = runCatching {
|
||||
printService?.printText2(text) ?: throw Exception("Print service not connected")
|
||||
fun commit(): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.start()
|
||||
}
|
||||
|
||||
fun printQR(content: String, size: Int = 6): Result<Unit> = runCatching {
|
||||
printService?.printQR(content, size) ?: throw Exception("Print service not connected")
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 内容添加(供 PrintJob 使用)
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
internal fun addText(text: String): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
val pm = printManager!!
|
||||
pm.addText(0, pm.getFontSize() ?: 24, pm.isFontBold() ?: false, pm.isUnderLine() ?: false, text)
|
||||
}
|
||||
|
||||
fun printBarcode(content: String, type: Int = 0, width: Int = 2, height: Int = 60): Result<Unit> = runCatching {
|
||||
printService?.printBarcode(content, type, width, height) ?: throw Exception("Print service not connected")
|
||||
internal fun addQR(content: String): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.addQRCode(2, 384, content)
|
||||
}
|
||||
|
||||
fun printBitmap(bitmap: ByteArray, width: Int, height: Int): Result<Unit> = runCatching {
|
||||
printService?.printBitmap(bitmap, width, height) ?: throw Exception("Print service not connected")
|
||||
internal fun addBarcode(content: String): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.addBarcode(2, 60, content, 73, 3)
|
||||
}
|
||||
|
||||
fun printLine(lineCount: Int = 1): Result<Unit> = runCatching {
|
||||
printService?.printLine(lineCount) ?: throw Exception("Print service not connected")
|
||||
internal fun addLineFeed(count: Int = 1): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.addLineFeed(count)
|
||||
}
|
||||
|
||||
fun start(): Result<Unit> = runCatching {
|
||||
printService?.start() ?: throw Exception("Print service not connected")
|
||||
}
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 样式设置
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
// 样式设置方法
|
||||
fun setFontSize(size: Int): Result<Unit> = runCatching {
|
||||
printService?.setFontSize(size) ?: throw Exception("Print service not connected")
|
||||
checkOpen()
|
||||
printManager!!.setFontSize(size)
|
||||
}
|
||||
|
||||
fun setTextBold(bold: Boolean): Result<Unit> = runCatching {
|
||||
printService?.setTextBold(bold) ?: throw Exception("Print service not connected")
|
||||
fun setBold(bold: Boolean): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.setFontBold(bold)
|
||||
}
|
||||
|
||||
fun setUnderLine(underline: Boolean): Result<Unit> = runCatching {
|
||||
printService?.setUnderLine(underline) ?: throw Exception("Print service not connected")
|
||||
fun setUnderline(underline: Boolean): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.setUnderLine(underline)
|
||||
}
|
||||
|
||||
fun setConcentration(level: Int): Result<Unit> = runCatching {
|
||||
printService?.setConcentration(level) ?: throw Exception("Print service not connected")
|
||||
fun setDensity(level: Int): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.setDensity(level)
|
||||
}
|
||||
|
||||
// 走纸控制
|
||||
fun setFeedPaperSpace(space: Int): Result<Unit> = runCatching {
|
||||
printService?.setFeedPaperSpace(space) ?: throw Exception("Print service not connected")
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 走纸控制
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
fun setPaperGap(dots: Int): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.setFeedPaperSpace(dots)
|
||||
}
|
||||
|
||||
fun setUnwindPaperLen(length: Int): Result<Unit> = runCatching {
|
||||
printService?.setUnwindPaperLen(length) ?: throw Exception("Print service not connected")
|
||||
fun setUnwindLength(len: Int): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.setUnwindPaperLen(len)
|
||||
}
|
||||
|
||||
// 标签纸控制
|
||||
fun printEnableMark(enable: Boolean): Result<Unit> = runCatching {
|
||||
printService?.printEnableMark(enable) ?: throw Exception("Print service not connected")
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 黑标 / 标签
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
fun enableMarkDetection(enable: Boolean): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.setBlackLabel(enable)
|
||||
}
|
||||
|
||||
fun printGoToNextMark(): Result<Unit> = runCatching {
|
||||
printService?.printGoToNextMark() ?: throw Exception("Print service not connected")
|
||||
fun gotoNextMark(): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.addLineFeed(1)
|
||||
printManager!!.start()
|
||||
}
|
||||
|
||||
// 便捷组合方法
|
||||
fun quickPrintText(text: String): Result<Unit> = runCatching {
|
||||
initPrinter().getOrThrow()
|
||||
printText2(text).getOrThrow()
|
||||
start().getOrThrow()
|
||||
closePrinter().getOrThrow()
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 图片
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
fun printImage(bitmap: Bitmap, align: Int = 1): Result<Unit> = runCatching {
|
||||
checkOpen()
|
||||
printManager!!.addImage(align, bitmap)
|
||||
}
|
||||
|
||||
fun isServiceConnected(): Boolean = isBound && printService != null
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// 便捷方法
|
||||
// ══════════════════════════════════════════════════════════
|
||||
|
||||
fun quickText(text: String): Result<Unit> = runCatching {
|
||||
initDevice().getOrThrow()
|
||||
addText(text).getOrThrow()
|
||||
commit().getOrThrow()
|
||||
Thread.sleep(1500)
|
||||
closeDevice().getOrThrow()
|
||||
}
|
||||
|
||||
fun quickLabel(text: String): Result<Unit> = runCatching {
|
||||
initDevice().getOrThrow()
|
||||
enableMarkDetection(true).getOrThrow()
|
||||
addText(text).getOrThrow()
|
||||
commit().getOrThrow()
|
||||
Thread.sleep(1500)
|
||||
closeDevice().getOrThrow()
|
||||
}
|
||||
|
||||
/** 创建链式打印任务 */
|
||||
fun buildJob(): PrintJob = PrintJob(this)
|
||||
|
||||
// ── 内部工具 ────────────────────────────────────────────
|
||||
|
||||
internal fun checkPrinter() {
|
||||
if (printManager == null)
|
||||
throw Exception("打印服务未初始化,请先点击'绑定服务'")
|
||||
}
|
||||
|
||||
private fun checkOpen() {
|
||||
checkPrinter()
|
||||
if (!isOpen)
|
||||
throw Exception("打印服务未连接,请先点击'绑定服务'")
|
||||
}
|
||||
|
||||
private fun notifyConnection(connected: Boolean, message: String) {
|
||||
mainHandler.post { connectionCallback?.invoke(connected, message) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#Wed Jun 24 20:47:38 CST 2026
|
||||
VERSION_CODE=23
|
||||
VERSION_NAME=1.2
|
||||
Reference in New Issue
Block a user