打印机接口
This commit is contained in:
parent
d3e4a6de53
commit
a87aac59ce
|
|
@ -76,10 +76,44 @@
|
||||||
<div class="info-box">
|
<div class="info-box">
|
||||||
<strong>设备信息:</strong><br>
|
<strong>设备信息:</strong><br>
|
||||||
应用版本: <span id="app-version">Unknown</span><br>
|
应用版本: <span id="app-version">Unknown</span><br>
|
||||||
环境检测: <span id="env-type">Browser</span>
|
环境检测: <span id="env-type">Browser</span><br>
|
||||||
|
打印机: <span id="printer-status" style="font-weight: bold; color: #ef4444;">未连接</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button class="btn" onclick="callSystemPrint()">1. 调用系统标准打印</button>
|
<!-- 打印功能网格 -->
|
||||||
|
<div style="margin-bottom: 20px;">
|
||||||
|
<div style="font-size: 13px; font-weight: bold; color: #64748b; margin-bottom: 8px;">基础打印测试</div>
|
||||||
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
||||||
|
<button class="btn btn-green" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintText()">文本打印</button>
|
||||||
|
<button class="btn btn-green" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintQR()">二维码打印</button>
|
||||||
|
<button class="btn btn-green" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintBarcode()">条形码打印</button>
|
||||||
|
<button class="btn btn-green" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintImg()">图片打印</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px;">
|
||||||
|
<div style="font-size: 13px; font-weight: bold; color: #64748b; margin-bottom: 8px;">高级业务样式</div>
|
||||||
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
||||||
|
<button class="btn btn-orange" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintColumns()">分栏打印测试</button>
|
||||||
|
<button class="btn btn-orange" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintAlarm()">警情通知单</button>
|
||||||
|
<button class="btn btn-orange" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintDemo(false)">2寸演示单据</button>
|
||||||
|
<button class="btn btn-orange" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintDemo(true)">3寸演示单据</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom: 20px;">
|
||||||
|
<div style="font-size: 13px; font-weight: bold; color: #64748b; margin-bottom: 8px;">硬件控制与维护</div>
|
||||||
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
||||||
|
<button class="btn" style="margin:0; padding:10px; font-size:13px; background: #6366f1;" onclick="callNativeTestPage()">打印自检页</button>
|
||||||
|
<button class="btn" style="margin:0; padding:10px; font-size:13px; background: #8b5cf6;" onclick="callNativeCheckPaper()">查询纸张状态</button>
|
||||||
|
<button class="btn btn-red" style="margin:0; padding:10px; font-size:13px;" onclick="callNativeCutPaper(false)">执行半切</button>
|
||||||
|
<button class="btn btn-red" style="margin:0; padding:10px; font-size:13px;" onclick="callNativeCutPaper(true)">执行全切</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr style="border: none; border-top: 1px solid #eee; margin: 20px 0;">
|
||||||
|
|
||||||
|
<button class="btn" style="background: #475569;" onclick="callSystemPrint()">1. 调用系统标准打印 (A4)</button>
|
||||||
<p style="font-size: 12px; color: #666;">(支持 PDF 导出、网络打印机、A4 适配)</p>
|
<p style="font-size: 12px; color: #666;">(支持 PDF 导出、网络打印机、A4 适配)</p>
|
||||||
|
|
||||||
<button class="btn btn-green" onclick="callRawPrint()">2. 发送热敏指令打印</button>
|
<button class="btn btn-green" onclick="callRawPrint()">2. 发送热敏指令打印</button>
|
||||||
|
|
@ -219,6 +253,134 @@
|
||||||
if (window.android.getVersionName) {
|
if (window.android.getVersionName) {
|
||||||
versionSpan.innerText = window.android.getVersionName();
|
versionSpan.innerText = window.android.getVersionName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePrinterStatus();
|
||||||
|
// 每 3 秒同步一次打印机状态
|
||||||
|
setInterval(updatePrinterStatus, 3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePrinterStatus() {
|
||||||
|
if (window.android && window.android.getPrinterStatus) {
|
||||||
|
var status = JSON.parse(window.android.getPrinterStatus());
|
||||||
|
var span = document.getElementById('printer-status');
|
||||||
|
var statusTxt = status.connected ? "已连接 (" + status.port + ")" : "未连接 (" + status.port + ")";
|
||||||
|
var paperTxt = status.hasPaper ? " [有纸]" : " [⚠️缺纸]";
|
||||||
|
|
||||||
|
span.innerText = statusTxt + paperTxt;
|
||||||
|
span.style.color = (status.connected && status.hasPaper) ? "#10B981" : "#ef4444";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintText() {
|
||||||
|
if (window.android && window.android.printText) {
|
||||||
|
window.android.printText("消防值守台 终端打印测试\n", 2, 1, 20);
|
||||||
|
window.android.printText("当前时间: " + new Date().toLocaleString() + "\n", 1, 1, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintQR() {
|
||||||
|
if (window.android && window.android.printQR) {
|
||||||
|
window.android.printQR("https://www.gyouzhe.com", 6, 2);
|
||||||
|
window.android.printText("\n\n\n", 1, 1, 10);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintBarcode() {
|
||||||
|
if (window.android && window.android.printBarcode) {
|
||||||
|
window.android.printBarcode("88888888", 3, 100, 5, 2, 2);
|
||||||
|
window.android.printText("\n\n\n", 1, 1, 10);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintImg() {
|
||||||
|
if (window.android && window.android.printImage) {
|
||||||
|
window.android.printImage("1.jpg", 384, 1, 2);
|
||||||
|
window.android.printText("\n\n\n", 1, 1, 10);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintColumns() {
|
||||||
|
if (window.android && window.android.printColumns) {
|
||||||
|
window.android.printColumns("项目", 0, "单价", 180, "数量", 250, "金额", 320);
|
||||||
|
window.android.printColumnsWithHeight("测试商品", 0, "10.0", 180, "1", 250, "10.0", 320, 70);
|
||||||
|
window.android.printText("\n\n\n", 1, 1, 10);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintAlarm() {
|
||||||
|
if (window.android && window.android.printText) {
|
||||||
|
// 1. 标题
|
||||||
|
window.android.printText("警情通知单", 2, 2, 40);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
// 2. 二维码 (居中)
|
||||||
|
window.android.printQR("ALARM_20260304172432", 7, 2);
|
||||||
|
window.android.printText("\n", 1, 1, 20);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
// 3. 详情列表
|
||||||
|
window.android.printText("报警时间: 2026-03-04 17:24:32", 1, 1, 20);
|
||||||
|
window.android.printText("报警人: 张先生", 1, 1, 20);
|
||||||
|
window.android.printText("报警人电话: 18722727636", 1, 1, 20);
|
||||||
|
window.android.printText("灾害地址: 贵州省安顺市西秀区(赵家山村)宋旗镇赵家山村", 1, 1, 20);
|
||||||
|
window.android.printText("警情分类: 抢险救援", 1, 1, 20);
|
||||||
|
window.android.printText("报警描述: 车祸", 1, 1, 20);
|
||||||
|
window.android.printText("主管救援站联系电话: 0851-33410119", 1, 1, 20);
|
||||||
|
window.android.printText("出动车辆: ", 1, 1, 20);
|
||||||
|
|
||||||
|
// 4. 分割线
|
||||||
|
window.android.printText("--------------------------------", 1, 1, 10);
|
||||||
|
|
||||||
|
// 5. 注意事项
|
||||||
|
window.android.printText("注意事项:", 1, 1, 20);
|
||||||
|
window.android.printText("1、请专职消防队人员穿戴好防护装备,携带灭火、救援器材赶往现场科学处置。", 1, 1, 20);
|
||||||
|
window.android.printText("2、到达现场后及时上报情况,注意安全,做好个人防护和现场警戒。", 1, 1, 20);
|
||||||
|
window.android.printText("3、根据现场情况迅速组织人员疏散,并在确保安全的前提下利用灭火救援装备开展救援。", 1, 1, 20);
|
||||||
|
window.android.printText("4、待责任消防救援站力量到达现场后,主动与其汇报现场已知情况,并协助做好灭火救援工作。", 1, 1, 20);
|
||||||
|
window.android.printText("5、严格落实作战行动安全规定,防止触电、溺水、烧伤、坠落、中毒、砸伤、放射线等伤害。", 1, 1, 20);
|
||||||
|
|
||||||
|
// 6. 结尾
|
||||||
|
window.android.printText("--------------------------------", 1, 1, 10);
|
||||||
|
window.android.printText("\n\n\n", 1, 1, 10);
|
||||||
|
|
||||||
|
// 7. 切纸
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativePrintDemo(is3Inch) {
|
||||||
|
if (window.android && window.android.printText) {
|
||||||
|
var width = is3Inch ? 400 : 300;
|
||||||
|
var right = is3Inch ? 120 : 100;
|
||||||
|
window.android.printText("\nStandAPP 业务测试单\n", 2, 1, 30);
|
||||||
|
window.android.printText("--------------------------------\n", 1, 1, 10);
|
||||||
|
window.android.printColumns("名称", 0, "单价", 180, "数量", 250, "金额", width);
|
||||||
|
window.android.printColumnsWithHeight("演示商品A", 0, "9.9", 180, "1", 250, "9.90", width, right);
|
||||||
|
window.android.printText("--------------------------------\n", 1, 1, 10);
|
||||||
|
window.android.printQR("http://www.standapp.com", 6, 2);
|
||||||
|
window.android.printText("\n\n\n", 1, 1, 10);
|
||||||
|
window.android.cutPaper(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativeTestPage() {
|
||||||
|
if (window.android && window.android.printTestPage) {
|
||||||
|
window.android.printTestPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativeCheckPaper() {
|
||||||
|
if (window.android && window.android.checkPaper) {
|
||||||
|
window.android.checkPaper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callNativeCutPaper(allCut) {
|
||||||
|
if (window.android && window.android.cutPaper) {
|
||||||
|
window.android.cutPaper(allCut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,152 @@ import android.print.PrintAttributes
|
||||||
import android.print.PrintManager
|
import android.print.PrintManager
|
||||||
import com.stand.standapp.R
|
import com.stand.standapp.R
|
||||||
import com.stand.standapp.utils.LogManager
|
import com.stand.standapp.utils.LogManager
|
||||||
|
import com.stand.standapp.printer.PrinterManager
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class AndroidInterface(private val activity: MainActivity) {
|
class AndroidInterface(private val activity: MainActivity) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:获取打印机当前连接状态
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun getPrinterStatus(): String {
|
||||||
|
return try {
|
||||||
|
val json = JSONObject()
|
||||||
|
val connected = PrinterManager.isConnected()
|
||||||
|
json.put("connected", connected)
|
||||||
|
json.put("hasPaper", PrinterManager.isPaperReady())
|
||||||
|
json.put("port", AppConfig.getSerialPort(activity))
|
||||||
|
json.toString()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"{\"connected\":false, \"hasPaper\":false, \"error\":\"${e.message}\"}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:通用打印文本
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printText(text: String?, size: Int, align: Int, space: Int) {
|
||||||
|
if (text.isNullOrEmpty()) return
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.let {
|
||||||
|
if (it.isConnection) {
|
||||||
|
it.PrintText(text, size, align, space)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printText failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:打印条码
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printBarcode(data: String?, size: Int, height: Int, align: Int, text_pos: Int, type: Int) {
|
||||||
|
if (data.isNullOrEmpty()) return
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.PrintBarcode(data, size, height, align, text_pos, type)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printBarcode failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:打印二维码
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printQR(data: String?, size: Int, l_m: Int) {
|
||||||
|
if (data.isNullOrEmpty()) return
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.PrintQR(data, size, l_m)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printQR failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:打印图片 (从 Assets 获取)
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printImage(fileName: String?, width: Int, align: Int, mode: Int) {
|
||||||
|
if (fileName.isNullOrEmpty()) return
|
||||||
|
try {
|
||||||
|
val stream = activity.assets.open(fileName)
|
||||||
|
val bitmap = android.graphics.BitmapFactory.decodeStream(stream)
|
||||||
|
PrinterManager.getFactory()?.PrintImage(bitmap, width, align, mode)
|
||||||
|
stream.close()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printImage failed: $fileName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:分栏打印 (4列)
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printColumns(c1: String?, p1: Int, c2: String?, p2: Int, c3: String?, p3: Int, c4: String?, p4: Int) {
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.Printcolumncontent(c1 ?: "", p1, c2 ?: "", p2, c3 ?: "", p3, c4 ?: "", p4)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printColumns failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:分栏打印 (带行高)
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printColumnsWithHeight(c1: String?, p1: Int, c2: String?, p2: Int, c3: String?, p3: Int, c4: String?, p4: Int, height: Int) {
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.Printcolumncontent(c1 ?: "", p1, c2 ?: "", p2, c3 ?: "", p3, c4 ?: "", p4, height)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printColumnsWithHeight failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:切纸
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun cutPaper(allCut: Boolean) {
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.let {
|
||||||
|
if (it.isConnection) {
|
||||||
|
if (allCut) it.PaperAllCut() else it.PaperCut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "cutPaper failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:打印自检页
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun printTestPage() {
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.PrintTestPage()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "printTestPage failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JS 调用:主动检查纸张状态 (会触发回调)
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun checkPaper() {
|
||||||
|
try {
|
||||||
|
PrinterManager.getFactory()?.Check_Paper()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("JS_Interface").e(e, "checkPaper failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JS 调用:压缩并导出日志
|
* JS 调用:压缩并导出日志
|
||||||
* 返回压缩后的文件路径
|
* 返回压缩后的文件路径
|
||||||
|
|
@ -76,15 +217,15 @@ class AndroidInterface(private val activity: MainActivity) {
|
||||||
/**
|
/**
|
||||||
* JS 调用:原始数据打印
|
* JS 调用:原始数据打印
|
||||||
* 适用于热敏小票打印机 (蓝牙、USB、网络)
|
* 适用于热敏小票打印机 (蓝牙、USB、网络)
|
||||||
* @param data 可以是 JSON 字符串,包含打印的指令或文本
|
* @param data String 可以是 JSON 字符串,包含打印的指令或文本
|
||||||
*/
|
*/
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun printRawData(data: String) {
|
fun printRawData(data: String?) {
|
||||||
|
if (data.isNullOrEmpty()) return
|
||||||
try {
|
try {
|
||||||
Timber.tag("Print").d("收到 H5 原始打印数据: $data")
|
Timber.tag("Print").d("收到 H5 原始打印数据: $data")
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
try {
|
try {
|
||||||
// 这里可以弹出一个选择框让用户选择打印机,或者根据配置自动连接
|
|
||||||
Toast.makeText(activity, "H5指令打印: $data", Toast.LENGTH_SHORT).show()
|
Toast.makeText(activity, "H5指令打印: $data", Toast.LENGTH_SHORT).show()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag("Print").e(e, "Toast显示失败")
|
Timber.tag("Print").e(e, "Toast显示失败")
|
||||||
|
|
@ -99,7 +240,7 @@ class AndroidInterface(private val activity: MainActivity) {
|
||||||
* JS 调用:在线升级 (手动触发检查)
|
* JS 调用:在线升级 (手动触发检查)
|
||||||
*/
|
*/
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun startUpdate(apkUrl: String) {
|
fun startUpdate(apkUrl: String?) {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
try {
|
try {
|
||||||
UpdateManager(activity).checkUpdate()
|
UpdateManager(activity).checkUpdate()
|
||||||
|
|
@ -172,7 +313,8 @@ class AndroidInterface(private val activity: MainActivity) {
|
||||||
* JS 调用:保存消息记录
|
* JS 调用:保存消息记录
|
||||||
*/
|
*/
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun saveMessage(message: String) {
|
fun saveMessage(message: String?) {
|
||||||
|
if (message.isNullOrEmpty()) return
|
||||||
try {
|
try {
|
||||||
val prefs = activity.getSharedPreferences("msg_records", Context.MODE_PRIVATE)
|
val prefs = activity.getSharedPreferences("msg_records", Context.MODE_PRIVATE)
|
||||||
val currentMsgs = prefs.getString("history", "") ?: ""
|
val currentMsgs = prefs.getString("history", "") ?: ""
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ object AppConfig {
|
||||||
|
|
||||||
// 默认配置
|
// 默认配置
|
||||||
const val DEFAULT_SERVER_URL = "https://template.gyouzhe.com"
|
const val DEFAULT_SERVER_URL = "https://template.gyouzhe.com"
|
||||||
const val DEFAULT_SERIAL_PORT = "/dev/ttyACM0"
|
const val DEFAULT_SERIAL_PORT = "ttyACM0"
|
||||||
|
|
||||||
private fun getPrefs(context: Context): SharedPreferences {
|
private fun getPrefs(context: Context): SharedPreferences {
|
||||||
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
|
|
|
||||||
|
|
@ -127,8 +127,12 @@ class LoginActivity : AppCompatActivity() {
|
||||||
AppConfig.setSavedPassword(this@LoginActivity, "")
|
AppConfig.setSavedPassword(this@LoginActivity, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show()
|
||||||
startActivity(Intent(this@LoginActivity, MainActivity::class.java))
|
|
||||||
|
// 初始化打印机 (自动重连)
|
||||||
|
com.stand.standapp.printer.PrinterManager.init(this@LoginActivity)
|
||||||
|
|
||||||
|
startActivity(Intent(this@LoginActivity, MainActivity::class.java))
|
||||||
finish()
|
finish()
|
||||||
} else {
|
} else {
|
||||||
val msg = json.optString("msg", "登录失败")
|
val msg = json.optString("msg", "登录失败")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
package com.stand.standapp.printer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import com.dp.dp_serialportlist.Serialport_Factory
|
||||||
|
import com.stand.standapp.AppConfig
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object PrinterManager {
|
||||||
|
private var sf: Serialport_Factory? = null
|
||||||
|
private var isConnecting = false
|
||||||
|
private val executor = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
private var mContext: Context? = null
|
||||||
|
private var hasPaper: Boolean = true // 默认为有纸
|
||||||
|
|
||||||
|
private val mHandler = Handler(Looper.getMainLooper()) { msg ->
|
||||||
|
if (msg.what == Serialport_Factory.PAPER_STATE) {
|
||||||
|
hasPaper = msg.data.getBoolean("PAPER_STATE")
|
||||||
|
val statusStr = if (hasPaper) "有纸" else "缺纸"
|
||||||
|
Timber.tag("Printer").i("打印机状态变更: %s", statusStr)
|
||||||
|
|
||||||
|
// 在 Android 系统层面主动提示
|
||||||
|
mContext?.let {
|
||||||
|
if (!hasPaper) {
|
||||||
|
android.widget.Toast.makeText(it, "⚠️ 警告:打印机缺纸!", android.widget.Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isPaperReady(): Boolean = hasPaper
|
||||||
|
|
||||||
|
fun init(context: Context) {
|
||||||
|
if (sf != null) return
|
||||||
|
mContext = context.applicationContext
|
||||||
|
|
||||||
|
// 全异步初始化,不阻塞主线程跳转
|
||||||
|
executor.execute {
|
||||||
|
try {
|
||||||
|
Timber.tag("Printer").i("开始异步初始化打印机组件...")
|
||||||
|
sf = Serialport_Factory.getSerialport_Factory(mContext, mHandler)
|
||||||
|
|
||||||
|
// 启动自动重连定时任务
|
||||||
|
executor.scheduleWithFixedDelay({
|
||||||
|
checkAndConnect()
|
||||||
|
}, 0, 10, TimeUnit.SECONDS)
|
||||||
|
|
||||||
|
Timber.tag("Printer").i("打印机组件初始化任务已分发")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("Printer").e(e, "打印机组件初始化失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
private fun checkAndConnect() {
|
||||||
|
val factory = sf ?: return
|
||||||
|
if (factory.isConnection) return
|
||||||
|
|
||||||
|
val path = AppConfig.getSerialPort(mContext!!)
|
||||||
|
val baudrate = "9600"
|
||||||
|
|
||||||
|
Timber.tag("Printer").d("尝试自动连接打印机: %s @ %s", path, baudrate)
|
||||||
|
try {
|
||||||
|
if (factory.OpenPort(path, baudrate)) {
|
||||||
|
Timber.tag("Printer").i("打印机自动连接成功")
|
||||||
|
}
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
Timber.tag("Printer").e(e, "打印机自动连接异常")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getFactory(): Serialport_Factory? = sf
|
||||||
|
|
||||||
|
fun isConnected(): Boolean = sf?.isConnection ?: false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提供便捷的打印方法
|
||||||
|
*/
|
||||||
|
fun printText(text: String, size: Int = 1, align: Int = 1, space: Int = 10) {
|
||||||
|
sf?.let {
|
||||||
|
if (it.isConnection) {
|
||||||
|
it.PrintText(text, size, align, space)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cutPaper() {
|
||||||
|
sf?.let {
|
||||||
|
if (it.isConnection) {
|
||||||
|
it.PaperCut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="56dp"
|
||||||
android:background="@drawable/bg_input_field"
|
android:background="@drawable/bg_input_field"
|
||||||
android:hint="/dev/ttyACM0"
|
android:hint="ttyACM0"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:textColor="#0F172A"
|
android:textColor="#0F172A"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue