Compare commits
No commits in common. "09f00f8b72830ddddfbd3c204b8ab091036cd739" and "7924d9103f8a7ab0732cd49efa988527ec0878e4" have entirely different histories.
09f00f8b72
...
7924d9103f
|
|
@ -13,6 +13,4 @@
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
.cxx
|
.cxx
|
||||||
local.properties
|
local.properties
|
||||||
/StandAPP/.idea
|
/StandAPP/.idea
|
||||||
/StandAPP/app/release
|
|
||||||
/.idea/
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="deploymentTargetSelector">
|
|
||||||
<selectionStates>
|
|
||||||
<SelectionState runConfigName="StandAPP.app">
|
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
|
||||||
</SelectionState>
|
|
||||||
</selectionStates>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DeviceTable">
|
|
||||||
<option name="columnSorters">
|
|
||||||
<list>
|
|
||||||
<ColumnSorterState>
|
|
||||||
<option name="column" value="Name" />
|
|
||||||
<option name="order" value="ASCENDING" />
|
|
||||||
</ColumnSorterState>
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|
|
||||||
|
|
@ -22,27 +22,6 @@
|
||||||
.status-offline { background: #FEE2E2; color: #991B1B; }
|
.status-offline { background: #FEE2E2; color: #991B1B; }
|
||||||
.bill-preview { border: 1px dashed #aaa; padding: 15px; background: #fff; margin-top: 20px; }
|
.bill-preview { border: 1px dashed #aaa; padding: 15px; background: #fff; margin-top: 20px; }
|
||||||
.footer { text-align: center; margin-top: 30px; font-size: 12px; color: #999; }
|
.footer { text-align: center; margin-top: 30px; font-size: 12px; color: #999; }
|
||||||
.exit-notice { color: #ef4444; font-weight: bold; font-size: 12px; margin-top: 5px; }
|
|
||||||
|
|
||||||
/* WebSocket Styles */
|
|
||||||
.ws-card { margin-top: 20px; border-top: 2px solid #eee; padding-top: 20px; }
|
|
||||||
.ws-status { font-size: 12px; padding: 4px 8px; border-radius: 4px; margin-left: 10px; }
|
|
||||||
.ws-connected { background: #dcfce7; color: #166534; }
|
|
||||||
.ws-disconnected { background: #fee2e2; color: #991b1b; }
|
|
||||||
.ws-input-group { margin-top: 10px; display: flex; flex-direction: column; gap: 5px; }
|
|
||||||
.ws-input {
|
|
||||||
padding: 10px; border: 1px solid #cbd5e1; border-radius: 6px;
|
|
||||||
font-size: 12px; font-family: monospace; width: 100%; box-sizing: border-box;
|
|
||||||
background: #f8fafc; color: #334155;
|
|
||||||
}
|
|
||||||
.log-container {
|
|
||||||
background: #1e293b; color: #f8fafc; padding: 10px;
|
|
||||||
border-radius: 6px; font-family: monospace; font-size: 11px;
|
|
||||||
max-height: 200px; overflow-y: auto; margin-top: 10px;
|
|
||||||
}
|
|
||||||
.log-item { margin-bottom: 4px; border-bottom: 1px solid #334155; padding-bottom: 2px; }
|
|
||||||
.log-time { color: #94a3b8; margin-right: 5px; }
|
|
||||||
.log-msg { word-break: break-all; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -68,36 +47,6 @@
|
||||||
<button class="btn btn-orange" onclick="callUpdate()">3. 检查系统更新</button>
|
<button class="btn btn-orange" onclick="callUpdate()">3. 检查系统更新</button>
|
||||||
<button class="btn btn-red" onclick="callExit()">4. 退出应用</button>
|
<button class="btn btn-red" onclick="callExit()">4. 退出应用</button>
|
||||||
|
|
||||||
<!-- WebSocket 控制区 -->
|
|
||||||
<div class="ws-card">
|
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
|
||||||
<strong>WebSocket 实时推送</strong>
|
|
||||||
<span id="ws-status-tag" class="ws-status ws-disconnected">未连接</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="margin-top: 10px; font-size: 13px;">
|
|
||||||
ID: <span id="ws-client-id" style="color: #6366f1;">-</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- WS 地址配置 -->
|
|
||||||
<div class="ws-input-group">
|
|
||||||
<label style="font-size: 12px; color: #64748b;">WebSocket 连接地址:</label>
|
|
||||||
<input type="text" id="ws-url-input" class="ws-input"
|
|
||||||
value="wss://template.gyouzhe.com/api/ws/{clientId}?Authorization=qiangfengsuiyuehen">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap;">
|
|
||||||
<button class="btn btn-green" style="margin:0; flex:1; padding: 10px; min-width: 120px;" onclick="toggleWS()">连接/断开 WS</button>
|
|
||||||
<button class="btn" style="margin:0; flex:1; padding: 10px; min-width: 120px; background: #6366f1;" onclick="manualAddRecord()">手动新增记录</button>
|
|
||||||
<button class="btn btn-orange" style="margin:0; flex:1; padding: 10px; min-width: 120px;" onclick="loadHistory()">从终端同步</button>
|
|
||||||
<button class="btn btn-red" style="margin:0; flex:1; padding: 10px; min-width: 120px; background: #991b1b;" onclick="clearHistory()">清空终端存储</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="log-list" class="log-container">
|
|
||||||
<div class="log-item">等待连接...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bill-preview" id="printArea">
|
<div class="bill-preview" id="printArea">
|
||||||
<div style="text-align: center; font-weight: bold; font-size: 18px;">测试签收单</div>
|
<div style="text-align: center; font-weight: bold; font-size: 18px;">测试签收单</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
@ -239,170 +188,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 安卓回调函数:退出确认时执行
|
|
||||||
function addExitText() {
|
|
||||||
const tag = document.getElementById('inject-status');
|
|
||||||
if (tag) {
|
|
||||||
const notice = document.createElement('div');
|
|
||||||
notice.className = 'exit-notice';
|
|
||||||
notice.innerText = "⚠️ 检测到退出尝试 (" + new Date().toLocaleTimeString() + ")";
|
|
||||||
tag.parentNode.insertBefore(notice, tag.nextSibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 延迟检测
|
// 延迟检测
|
||||||
setTimeout(checkApp, 300);
|
setTimeout(checkApp, 300);
|
||||||
|
|
||||||
// === WebSocket 逻辑 ===
|
|
||||||
let ws = null;
|
|
||||||
let clientId = "WEB_" + Math.random().toString(36).substr(2, 9);
|
|
||||||
let heartbeatTimer = null;
|
|
||||||
let reconnectTimer = null;
|
|
||||||
const logList = document.getElementById('log-list');
|
|
||||||
|
|
||||||
document.getElementById('ws-client-id').innerText = clientId;
|
|
||||||
|
|
||||||
function addLog(msg, save = true) {
|
|
||||||
const time = new Date().toLocaleTimeString();
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'log-item';
|
|
||||||
item.innerHTML = `<span class="log-time">[${time}]</span><span class="log-msg">${msg}</span>`;
|
|
||||||
logList.insertBefore(item, logList.firstChild);
|
|
||||||
|
|
||||||
// 限制 DOM 节点数量,避免页面卡顿(虽然存储 5000 条,但显示建议限制在 200 条)
|
|
||||||
if (logList.childNodes.length > 200) {
|
|
||||||
logList.removeChild(logList.lastChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用安卓接口持久化存储
|
|
||||||
if (save && window.android && window.android.saveMessage) {
|
|
||||||
window.android.saveMessage(`${time}: ${msg}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateWSStatus(connected) {
|
|
||||||
const tag = document.getElementById('ws-status-tag');
|
|
||||||
if (connected) {
|
|
||||||
tag.innerText = "已连接";
|
|
||||||
tag.className = "ws-status ws-connected";
|
|
||||||
} else {
|
|
||||||
tag.innerText = "已断开";
|
|
||||||
tag.className = "ws-status ws-disconnected";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectWS() {
|
|
||||||
if (ws) {
|
|
||||||
ws.onclose = null; // 清除之前的重连逻辑
|
|
||||||
ws.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
const rawUrl = document.getElementById('ws-url-input').value;
|
|
||||||
const finalUrl = rawUrl.replace("{clientId}", clientId);
|
|
||||||
|
|
||||||
addLog("正在尝试连接: " + finalUrl, false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
ws = new WebSocket(finalUrl);
|
|
||||||
|
|
||||||
ws.onopen = () => {
|
|
||||||
updateWSStatus(true);
|
|
||||||
addLog("连接成功", false);
|
|
||||||
startHeartbeat();
|
|
||||||
clearTimeout(reconnectTimer);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onmessage = (evt) => {
|
|
||||||
addLog("收到消息: " + evt.data);
|
|
||||||
// 如果收到打印指令,可以自动触发打印
|
|
||||||
if (evt.data.includes("print")) {
|
|
||||||
callRawPrint();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onclose = () => {
|
|
||||||
updateWSStatus(false);
|
|
||||||
addLog("连接关闭", false);
|
|
||||||
stopHeartbeat();
|
|
||||||
// 5秒后自动重连
|
|
||||||
reconnectTimer = setTimeout(connectWS, 5000);
|
|
||||||
};
|
|
||||||
|
|
||||||
ws.onerror = (err) => {
|
|
||||||
addLog("连接错误或被拒绝", false);
|
|
||||||
console.error(err);
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
addLog("连接失败: " + e.message, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleWS() {
|
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
||||||
clearTimeout(reconnectTimer);
|
|
||||||
reconnectTimer = null;
|
|
||||||
ws.close();
|
|
||||||
} else {
|
|
||||||
connectWS();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function startHeartbeat() {
|
|
||||||
stopHeartbeat();
|
|
||||||
heartbeatTimer = setInterval(() => {
|
|
||||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
||||||
ws.send("ping");
|
|
||||||
}
|
|
||||||
}, 30000); // 30秒心跳
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopHeartbeat() {
|
|
||||||
clearInterval(heartbeatTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearHistory() {
|
|
||||||
if (confirm("确定要清空安卓端存储的所有消息吗?")) {
|
|
||||||
if (window.android && window.android.clearMessages) {
|
|
||||||
window.android.clearMessages();
|
|
||||||
}
|
|
||||||
logList.innerHTML = '<div class="log-item">记录已清空</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function manualAddRecord() {
|
|
||||||
const text = prompt("请输入要保存的消息内容:", "手动测试记录");
|
|
||||||
if (text) {
|
|
||||||
addLog("用户新增: " + text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化加载历史记录
|
|
||||||
function loadHistory() {
|
|
||||||
if (window.android && window.android.getMessages) {
|
|
||||||
const history = window.android.getMessages();
|
|
||||||
logList.innerHTML = ""; // 先清空当前显示
|
|
||||||
if (history) {
|
|
||||||
const msgs = history.split("|---|");
|
|
||||||
addLog(`系统: 已从安卓端同步 ${msgs.length} 条记录`, false);
|
|
||||||
msgs.forEach((msg, index) => {
|
|
||||||
if (!msg || index > 500) return; // 界面显示限制前500条,防止卡死
|
|
||||||
const item = document.createElement('div');
|
|
||||||
item.className = 'log-item';
|
|
||||||
const parts = msg.split(": ");
|
|
||||||
const time = parts[0];
|
|
||||||
const content = parts.slice(1).join(": ");
|
|
||||||
item.innerHTML = `<span class="log-time">[${time}]</span><span class="log-msg">${content}</span>`;
|
|
||||||
logList.appendChild(item);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
addLog("系统: 终端暂无保存的记录", false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
alert("未检测到安卓接口,无法同步");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(loadHistory, 500);
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -89,35 +89,4 @@ class AndroidInterface(private val activity: MainActivity) {
|
||||||
"1.0.0"
|
"1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* JS 调用:保存消息记录
|
|
||||||
*/
|
|
||||||
@JavascriptInterface
|
|
||||||
fun saveMessage(message: String) {
|
|
||||||
val prefs = activity.getSharedPreferences("msg_records", Context.MODE_PRIVATE)
|
|
||||||
val currentMsgs = prefs.getString("history", "") ?: ""
|
|
||||||
val updatedMsgs = if (currentMsgs.isEmpty()) message else "$message|---|$currentMsgs"
|
|
||||||
// 限制保存最近5000条
|
|
||||||
val list = updatedMsgs.split("|---|").take(5000)
|
|
||||||
prefs.edit().putString("history", list.joinToString("|---|")).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JS 调用:获取消息记录
|
|
||||||
*/
|
|
||||||
@JavascriptInterface
|
|
||||||
fun getMessages(): String {
|
|
||||||
val prefs = activity.getSharedPreferences("msg_records", Context.MODE_PRIVATE)
|
|
||||||
return prefs.getString("history", "") ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JS 调用:清空消息记录
|
|
||||||
*/
|
|
||||||
@JavascriptInterface
|
|
||||||
fun clearMessages() {
|
|
||||||
activity.getSharedPreferences("msg_records", Context.MODE_PRIVATE).edit().clear().apply()
|
|
||||||
Toast.makeText(activity, "消息记录已清空", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,27 +32,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
.addJavascriptInterface("android", AndroidInterface(this))
|
.addJavascriptInterface("android", AndroidInterface(this))
|
||||||
.createAgentWeb()
|
.createAgentWeb()
|
||||||
.ready()
|
.ready()
|
||||||
.go("https://template.gyouzhe.com/example/print_demo.html?t=\${System.currentTimeMillis()}")
|
.go("http://152.32.186.199/local/print_demo.html")
|
||||||
|
|
||||||
// 配置 WebView 属性以支持 SSE 和跨域
|
|
||||||
mAgentWeb?.let { agent ->
|
|
||||||
try {
|
|
||||||
val webView = agent.webCreator.webView
|
|
||||||
val settings = webView.settings
|
|
||||||
// 允许混合内容 (HTTP/HTTPS)
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
settings.mixedContentMode = android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
|
||||||
}
|
|
||||||
// 允许跨域和存储
|
|
||||||
settings.allowUniversalAccessFromFileURLs = true
|
|
||||||
settings.allowFileAccessFromFileURLs = true
|
|
||||||
settings.domStorageEnabled = true
|
|
||||||
settings.javaScriptEnabled = true
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("MainActivity", "WebView settings error: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// .go("http://152.32.186.199/local/print_demo.html")
|
|
||||||
|
|
||||||
// 启动时检查更新
|
// 启动时检查更新
|
||||||
UpdateManager(this).checkUpdate()
|
UpdateManager(this).checkUpdate()
|
||||||
|
|
@ -68,9 +48,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showExitDialog() {
|
fun showExitDialog() {
|
||||||
// 调用 JS 函数
|
|
||||||
mAgentWeb?.jsAccessEntrace?.quickCallJs("addExitText")
|
|
||||||
|
|
||||||
androidx.appcompat.app.AlertDialog.Builder(this)
|
androidx.appcompat.app.AlertDialog.Builder(this)
|
||||||
.setTitle("提示")
|
.setTitle("提示")
|
||||||
.setMessage("确定要退出应用吗?")
|
.setMessage("确定要退出应用吗?")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue