Compare commits
No commits in common. "cafdfe3e4d4ea0697feea6c2df893f7ff7c3ee5b" and "828ad240eb8306f7b7b1b1a393f784ba3199b2b9" have entirely different histories.
cafdfe3e4d
...
828ad240eb
|
|
@ -92,13 +92,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-bottom: 20px;">
|
<div style="margin-bottom: 20px;">
|
||||||
<div style="font-size: 13px; font-weight: bold; color: #64748b; margin-bottom: 8px;">高级业务样式与测试</div>
|
<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;">
|
<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="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="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(false)">2寸演示单据</button>
|
||||||
<button class="btn btn-orange" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintDemo(true)">3寸演示单据</button>
|
<button class="btn btn-orange" style="margin:0; padding:10px; font-size:13px;" onclick="callNativePrintDemo(true)">3寸演示单据</button>
|
||||||
<button class="btn" style="margin:0; padding:10px; font-size:13px; background: #0ea5e9; grid-column: span 2;" onclick="testES6Features()">ES6 引擎特性测试</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -419,19 +418,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传日志
|
// 上传日志
|
||||||
var isUploading = false;
|
|
||||||
function callUploadLogs() {
|
function callUploadLogs() {
|
||||||
if (isUploading) {
|
|
||||||
alert("正在上传中,请耐心等待(30秒内请勿重复点击)...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (window.android && window.android.uploadLogs) {
|
if (window.android && window.android.uploadLogs) {
|
||||||
isUploading = true;
|
|
||||||
alert("开始上传日志,请耐心等待...");
|
|
||||||
window.android.uploadLogs();
|
window.android.uploadLogs();
|
||||||
setTimeout(function() {
|
|
||||||
isUploading = false;
|
|
||||||
}, 30000);
|
|
||||||
} else {
|
} else {
|
||||||
alert("上传日志接口不可用");
|
alert("上传日志接口不可用");
|
||||||
}
|
}
|
||||||
|
|
@ -471,68 +460,7 @@
|
||||||
document.getElementById('aboutModal').style.display = 'none';
|
document.getElementById('aboutModal').style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打印机查纸回调
|
// 安卓回调函数:退出确认时执行
|
||||||
window.onPaperStateChanged = function(hasPaper) {
|
|
||||||
if (hasPaper) {
|
|
||||||
alert("打印机状态正常:当前有纸");
|
|
||||||
} else {
|
|
||||||
alert("⚠️ 打印机缺纸!请装入纸卷!");
|
|
||||||
}
|
|
||||||
updatePrinterStatus();
|
|
||||||
};
|
|
||||||
|
|
||||||
// PTT 各种事件回调
|
|
||||||
window.onPttEvent = function(eventType, payload) {
|
|
||||||
console.log("收到 PTT 事件: ", eventType, payload);
|
|
||||||
var msg = "";
|
|
||||||
switch(eventType) {
|
|
||||||
case 'GroupList':
|
|
||||||
msg = "获取群组成功,共 " + payload.length + " 个群组。第一组: " + (payload[0] ? payload[0].groupName : "无");
|
|
||||||
break;
|
|
||||||
case 'MemberList':
|
|
||||||
msg = "获取群成员成功,共 " + payload.length + " 个成员。";
|
|
||||||
break;
|
|
||||||
case 'SpeakerUpdate':
|
|
||||||
msg = "当前讲话人: " + payload.talkName + " (组:" + payload.groupName + ")";
|
|
||||||
break;
|
|
||||||
case 'PlayStatus':
|
|
||||||
msg = (payload.playStatus === 1 ? "开始播放:" : "结束播放:") + payload.talkName;
|
|
||||||
break;
|
|
||||||
case 'TempCall':
|
|
||||||
msg = "临时呼叫消息: " + payload.msg;
|
|
||||||
break;
|
|
||||||
case 'UpdateGroup':
|
|
||||||
msg = "进入群组: " + payload.groupName;
|
|
||||||
break;
|
|
||||||
case 'Alarm':
|
|
||||||
msg = "⚠️收到报警: " + payload.memberName + " (纬度:" + payload.latitude + ")";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
msg = "收到事件: " + eventType;
|
|
||||||
}
|
|
||||||
showPttResult("[回调] " + msg);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ES6 特性测试函数(通过 eval 避免阻塞旧版 JS 引擎整体解析)
|
|
||||||
function testES6Features() {
|
|
||||||
try {
|
|
||||||
var code = [
|
|
||||||
"const arr = [1, 2, 3];",
|
|
||||||
"const [a, ...rest] = arr;",
|
|
||||||
"let sum = rest.reduce((acc, val) => acc + val, 0);",
|
|
||||||
"const myMap = new Map();",
|
|
||||||
"myMap.set('key', `Sum is ${sum}`);",
|
|
||||||
"new Promise((resolve) => {",
|
|
||||||
" setTimeout(() => resolve(myMap.get('key')), 100);",
|
|
||||||
"}).then(res => {",
|
|
||||||
" alert(`ES6 引擎测试成功!\\n解构: a=${a}\\nPromise/Map 返回: ${res}`);",
|
|
||||||
"});"
|
|
||||||
].join("");
|
|
||||||
eval(code);
|
|
||||||
} catch(e) {
|
|
||||||
alert("ES6 测试失败 (当前环境可能不支持): " + e.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function addExitText() {
|
function addExitText() {
|
||||||
var tag = document.getElementById('inject-status');
|
var tag = document.getElementById('inject-status');
|
||||||
if (tag) {
|
if (tag) {
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ public class CallBackResolution {
|
||||||
|
|
||||||
case "80"://返回群组信息
|
case "80"://返回群组信息
|
||||||
Log.d(TAG, "at_OEM_AT_cb get group info: " + lisHex);
|
Log.d(TAG, "at_OEM_AT_cb get group info: " + lisHex);
|
||||||
StringBuilder strGroup = new StringBuilder();
|
String strGroup = "";
|
||||||
String groupId = lisHex.get(6) + "" + lisHex.get(7) + "" + lisHex.get(8) + "" + lisHex.get(9);
|
String groupId = lisHex.get(6) + "" + lisHex.get(7) + "" + lisHex.get(8) + "" + lisHex.get(9);
|
||||||
int groupNo = Integer.valueOf((lisHex.get(4) + "" + lisHex.get(5)), 16);
|
int groupNo = Integer.valueOf((lisHex.get(4) + "" + lisHex.get(5)), 16);
|
||||||
int groupCount = Integer.valueOf((lisHex.get(10) + "" + lisHex.get(11)), 16);
|
int groupCount = Integer.valueOf((lisHex.get(10) + "" + lisHex.get(11)), 16);
|
||||||
|
|
@ -108,9 +108,9 @@ public class CallBackResolution {
|
||||||
size = (size / 2) - 1;
|
size = (size / 2) - 1;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
strGroup.append(ngx).append(lisHex.get(i + numB)).append(lisHex.get(i + numB - 1));
|
strGroup += ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numB - 1);
|
||||||
} else {
|
} else {
|
||||||
strGroup.append(ngx).append(lisHex.get(i + numB + 1)).append(lisHex.get(i + numB));
|
strGroup += ngx + lisHex.get(i + numB + 1) + "" + lisHex.get(i + numB);
|
||||||
numB = numB + 1;
|
numB = numB + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ public class CallBackResolution {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
String tempTxt = "";
|
String tempTxt = "";
|
||||||
tempTxt = unicodeToString(strGroup.toString());
|
tempTxt = unicodeToString(strGroup);
|
||||||
Log.i(TAG, " 组名称 " + tempTxt);
|
Log.i(TAG, " 组名称 " + tempTxt);
|
||||||
CallBackUtil.callBackGroupInfo(groupId, tempTxt, groupNo, groupCount);
|
CallBackUtil.callBackGroupInfo(groupId, tempTxt, groupNo, groupCount);
|
||||||
Log.d(TAG, "at_OEM_AT_cb group info groupId: " + groupId + ", groupNo: " + groupNo+", groupCount: " +groupCount);
|
Log.d(TAG, "at_OEM_AT_cb group info groupId: " + groupId + ", groupNo: " + groupNo+", groupCount: " +groupCount);
|
||||||
|
|
@ -129,7 +129,7 @@ public class CallBackResolution {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "81"://返回群成员信息
|
case "81"://返回群成员信息
|
||||||
StringBuilder groupMemberName = new StringBuilder();
|
String groupMemberName = "";
|
||||||
String status = lisHex.get(1);
|
String status = lisHex.get(1);
|
||||||
boolean haveVideo = lisHex.get(10).equals("01") ? true : false;
|
boolean haveVideo = lisHex.get(10).equals("01") ? true : false;
|
||||||
String memberId = lisHex.get(6) + "" + lisHex.get(7) + "" + lisHex.get(8) + "" + lisHex.get(9);
|
String memberId = lisHex.get(6) + "" + lisHex.get(7) + "" + lisHex.get(8) + "" + lisHex.get(9);
|
||||||
|
|
@ -141,16 +141,16 @@ public class CallBackResolution {
|
||||||
size = (size / 2) - 1;
|
size = (size / 2) - 1;
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
groupMemberName.append(ngx).append(lisHex.get(i + numB)).append(lisHex.get(i + numB - 1));
|
groupMemberName += ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numB - 1);
|
||||||
} else {
|
} else {
|
||||||
groupMemberName.append(ngx).append(lisHex.get(i + numB + 1)).append(lisHex.get(i + numB));
|
groupMemberName += ngx + lisHex.get(i + numB + 1) + "" + lisHex.get(i + numB);
|
||||||
numB = numB + 1;
|
numB = numB + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
String tempTxt = unicodeToString(groupMemberName.toString());
|
String tempTxt = unicodeToString(groupMemberName);
|
||||||
GroupMemberInfoDto infoDto = new GroupMemberInfoDto(memberId, tempTxt, status, memberNo, haveVideo);
|
GroupMemberInfoDto infoDto = new GroupMemberInfoDto(memberId, tempTxt, status, memberNo, haveVideo);
|
||||||
if (memberNo == 1) memberInfoDtos.clear();
|
if (memberNo == 1) memberInfoDtos.clear();
|
||||||
memberInfoDtos.add(infoDto);
|
memberInfoDtos.add(infoDto);
|
||||||
|
|
@ -461,11 +461,10 @@ public class CallBackResolution {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} finally {
|
} finally {
|
||||||
StringBuilder sb = new StringBuilder();
|
String tempTxt = "";
|
||||||
for (int k = 0; k < strings.size(); k++) {
|
for (int k = 0; k < strings.size(); k++) {
|
||||||
sb.append(unicode(strings.get(k)));
|
tempTxt += unicode(strings.get(k));
|
||||||
}
|
}
|
||||||
String tempTxt = sb.toString();
|
|
||||||
Log.i(TAG,"at_Play_TTS_cb " + tempTxt);
|
Log.i(TAG,"at_Play_TTS_cb " + tempTxt);
|
||||||
if (tempTxt.contains("已登录")) {
|
if (tempTxt.contains("已登录")) {
|
||||||
String showName = tempTxt.substring(3, tempTxt.length());
|
String showName = tempTxt.substring(3, tempTxt.length());
|
||||||
|
|
@ -478,7 +477,6 @@ public class CallBackResolution {
|
||||||
};
|
};
|
||||||
executor.scheduleAtFixedRate(task, 0, 40, TimeUnit.SECONDS);
|
executor.scheduleAtFixedRate(task, 0, 40, TimeUnit.SECONDS);
|
||||||
} else if (tempTxt.contains("账号已更新")) {
|
} else if (tempTxt.contains("账号已更新")) {
|
||||||
com.stand.standapp.MainActivity.showPttUpdateDialog();
|
|
||||||
} else if (tempTxt.contains("账号或密码错误")) {
|
} else if (tempTxt.contains("账号或密码错误")) {
|
||||||
CallBackUtil.callBackLogin(false, "");
|
CallBackUtil.callBackLogin(false, "");
|
||||||
}else if (tempTxt.contains("请配置账号")) {
|
}else if (tempTxt.contains("请配置账号")) {
|
||||||
|
|
|
||||||
|
|
@ -68,19 +68,7 @@ public class CallBackUtil {
|
||||||
* state true-成功, false-失败
|
* state true-成功, false-失败
|
||||||
*/
|
*/
|
||||||
public static void callBackOnMemberSuccess(boolean state, List<GroupMemberInfoDto> dtos) {
|
public static void callBackOnMemberSuccess(boolean state, List<GroupMemberInfoDto> dtos) {
|
||||||
try {
|
|
||||||
org.json.JSONArray arr = new org.json.JSONArray();
|
|
||||||
if (dtos != null && state) {
|
|
||||||
for (GroupMemberInfoDto dto : dtos) {
|
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("memberId", dto.getMemberId());
|
|
||||||
obj.put("memberName", dto.getGmemberName());
|
|
||||||
obj.put("status", dto.getStatus());
|
|
||||||
arr.put(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('MemberList', " + arr.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -113,17 +101,9 @@ public class CallBackUtil {
|
||||||
* GroupInfoDto 群组信息
|
* GroupInfoDto 群组信息
|
||||||
* */
|
* */
|
||||||
public static void callBackGroupInfo (ArrayList < GroupInfoDto > groupInfoDtos) {
|
public static void callBackGroupInfo (ArrayList < GroupInfoDto > groupInfoDtos) {
|
||||||
try {
|
for (GroupInfoDto groupInfoDto : groupInfoDtos) {
|
||||||
org.json.JSONArray arr = new org.json.JSONArray();
|
Log.d(TAG, "callBackGroupInfo: " + groupInfoDto.toString());
|
||||||
for (GroupInfoDto dto : groupInfoDtos) {
|
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("groupId", dto.getGroupId());
|
|
||||||
obj.put("groupName", dto.getGroupName());
|
|
||||||
obj.put("groupNo", dto.getGroupNo());
|
|
||||||
arr.put(obj);
|
|
||||||
}
|
}
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('GroupList', " + arr.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -132,46 +112,44 @@ public class CallBackUtil {
|
||||||
* talkId 讲话用户ID
|
* talkId 讲话用户ID
|
||||||
* talkName 讲话用户名字
|
* talkName 讲话用户名字
|
||||||
* */
|
* */
|
||||||
public static void callBackNotifySpker ( int speckType, String groupId, String groupName, String talkId, String talkName){
|
public static void callBackNotifySpker ( int speckType, String groupId, String
|
||||||
try {
|
groupName, String talkId, String talkName){
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("speckType", speckType);
|
|
||||||
obj.put("groupId", groupId);
|
|
||||||
obj.put("groupName", groupName);
|
|
||||||
obj.put("talkId", talkId);
|
|
||||||
obj.put("talkName", talkName);
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('SpeakerUpdate', " + obj.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void callBackNotifyPlayStatus ( int playStatus, int speckType, String groupId, String groupName, String talkId, String talkName){
|
/**
|
||||||
try {
|
* 对方讲话通知
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
* playStatus 1表示开始讲话, 0 表示结束讲话
|
||||||
obj.put("playStatus", playStatus);
|
* speckType 自己讲话状态: 0 : 表示自己无法讲话; 1 : 表示自己可以中断讲话人的讲话,可以进行强插讲话
|
||||||
obj.put("speckType", speckType);
|
* groupId 所在群组Id
|
||||||
obj.put("groupId", groupId);
|
* groupName 所在群组名称
|
||||||
obj.put("groupName", groupName);
|
* talkId 讲话用户Id
|
||||||
obj.put("talkId", talkId);
|
* talkName 讲话用户名称
|
||||||
obj.put("talkName", talkName);
|
* */
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('PlayStatus', " + obj.toString() + ");}");
|
public static void callBackNotifyPlayStatus ( int playStatus, int speckType, String
|
||||||
} catch (Exception e) {}
|
groupId, String groupName, String talkId, String talkName){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*临时呼叫信息通知
|
||||||
|
* tempCallNotify:
|
||||||
|
* 临时呼叫xxx:表示被单呼,呼叫名:xxx
|
||||||
|
* 退出临时呼叫:表示退出单呼
|
||||||
|
* 呼叫成功:表示单呼成功
|
||||||
|
* 呼叫失败:表示单呼失败
|
||||||
|
* */
|
||||||
public static void callBackTempCallNotify (String tempCallNotify){
|
public static void callBackTempCallNotify (String tempCallNotify){
|
||||||
try {
|
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("msg", tempCallNotify);
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('TempCall', " + obj.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户进入群组信息通知
|
||||||
|
* userGroupId 用户所在的群组ID
|
||||||
|
* userGroupName 用户所在的群组名称
|
||||||
|
* */
|
||||||
public static void callBackNotifyUpdateGroup (String userGroupId, String userGroupName){
|
public static void callBackNotifyUpdateGroup (String userGroupId, String userGroupName){
|
||||||
try {
|
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("groupId", userGroupId);
|
|
||||||
obj.put("groupName", userGroupName);
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('UpdateGroup', " + obj.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -182,16 +160,9 @@ public class CallBackUtil {
|
||||||
* longitude 经度
|
* longitude 经度
|
||||||
* time 时间
|
* time 时间
|
||||||
*/
|
*/
|
||||||
public static void callBackNotifyLocationStatus ( int status, String userId,double latitude, double longitude, String time){
|
public static void callBackNotifyLocationStatus ( int status, String userId,double latitude,
|
||||||
try {
|
double longitude, String time){
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("status", status);
|
|
||||||
obj.put("userId", userId);
|
|
||||||
obj.put("latitude", latitude);
|
|
||||||
obj.put("longitude", longitude);
|
|
||||||
obj.put("time", time);
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('LocationStatus', " + obj.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -271,13 +242,7 @@ public class CallBackUtil {
|
||||||
* longitude 经度
|
* longitude 经度
|
||||||
* */
|
* */
|
||||||
public static void callBackEnotifyAlarm (String memberName,double latitude, double longitude){
|
public static void callBackEnotifyAlarm (String memberName,double latitude, double longitude){
|
||||||
try {
|
|
||||||
org.json.JSONObject obj = new org.json.JSONObject();
|
|
||||||
obj.put("memberName", memberName);
|
|
||||||
obj.put("latitude", latitude);
|
|
||||||
obj.put("longitude", longitude);
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('Alarm', " + obj.toString() + ");}");
|
|
||||||
} catch (Exception e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
package com.example.kingway.ptt;
|
package com.example.kingway.ptt;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.AutoCompleteTextView;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
|
@ -16,44 +12,29 @@ import com.hjq.permissions.Permission;
|
||||||
import com.hjq.permissions.XXPermissions;
|
import com.hjq.permissions.XXPermissions;
|
||||||
import com.stand.standapp.R;
|
import com.stand.standapp.R;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class pttActivity extends AppCompatActivity {
|
public class pttActivity extends AppCompatActivity {
|
||||||
private Button pttBtn;
|
private Button pttBtn;
|
||||||
private AutoCompleteTextView etIp, etAccount, etPassword, etGroupId;
|
|
||||||
private static final String PREFS_NAME = "ptt_history";
|
|
||||||
private static final String KEY_IP_HISTORY = "ip_history";
|
|
||||||
private static final String KEY_ACCOUNT_HISTORY = "account_history";
|
|
||||||
private static final String KEY_PASSWORD_HISTORY = "password_history";
|
|
||||||
private static final String KEY_GROUP_HISTORY = "group_history";
|
|
||||||
private static final String SEPARATOR = ",";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_ptt);
|
setContentView(R.layout.activity_ptt);
|
||||||
|
|
||||||
etIp = findViewById(R.id.etIp);
|
|
||||||
etAccount = findViewById(R.id.etAccount);
|
|
||||||
etPassword = findViewById(R.id.etPassword);
|
|
||||||
etGroupId = findViewById(R.id.etGroupId);
|
|
||||||
|
|
||||||
loadHistory();
|
|
||||||
|
|
||||||
pttBtn = (Button)findViewById(R.id.pttBtn);
|
pttBtn = (Button)findViewById(R.id.pttBtn);
|
||||||
XXPermissions.with(this)
|
XXPermissions.with(this)
|
||||||
.permission(Permission.RECORD_AUDIO)
|
.permission(Permission.RECORD_AUDIO)
|
||||||
.request(null);
|
.request(null);
|
||||||
pttBtn.setOnTouchListener(new View.OnTouchListener() {
|
pttBtn.setOnTouchListener(new View.OnTouchListener()
|
||||||
|
{
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
public boolean onTouch(View view, MotionEvent motionEvent)
|
||||||
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
{
|
||||||
|
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN)
|
||||||
|
{
|
||||||
pttBtn.setBackgroundColor(Color.RED);
|
pttBtn.setBackgroundColor(Color.RED);
|
||||||
SendAtUtil.speakPlay();
|
SendAtUtil.speakPlay();
|
||||||
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
}
|
||||||
|
else if(motionEvent.getAction() == MotionEvent.ACTION_UP)
|
||||||
|
{
|
||||||
pttBtn.setBackgroundColor(Color.BLUE);
|
pttBtn.setBackgroundColor(Color.BLUE);
|
||||||
SendAtUtil.speakRelease();
|
SendAtUtil.speakRelease();
|
||||||
}
|
}
|
||||||
|
|
@ -61,6 +42,7 @@ public class pttActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
|
@ -68,31 +50,16 @@ public class pttActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
findViewById(R.id.btnCustomLogin).setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
String ip = etIp.getText().toString().trim();
|
|
||||||
String account = etAccount.getText().toString().trim();
|
|
||||||
String password = etPassword.getText().toString().trim();
|
|
||||||
String groupId = etGroupId.getText().toString().trim();
|
|
||||||
|
|
||||||
if (ip.isEmpty() || account.isEmpty() || password.isEmpty() || groupId.isEmpty()) {
|
|
||||||
Toast.makeText(pttActivity.this, "请填写所有字段", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
saveToHistory(ip, account, password, groupId);
|
|
||||||
SendAtUtil.toLogin(account, password, ip, 1, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
findViewById(R.id.login1).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.login1).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
// SendAtUtil.toLogin("gzzdweb01", "123456", "61.243.1.123", 1, 1);
|
||||||
SendAtUtil.toLogin("test004", "123456", "220.154.131.231", 1, 1);
|
SendAtUtil.toLogin("test004", "123456", "220.154.131.231", 1, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 获取群组 */
|
||||||
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
|
@ -100,6 +67,7 @@ public class pttActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 获取群成员 */
|
||||||
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
|
@ -107,6 +75,7 @@ public class pttActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 切换群组 */
|
||||||
findViewById(R.id.btnJumpGroup).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.btnJumpGroup).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
|
@ -121,6 +90,7 @@ public class pttActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/** 切换群组 */
|
||||||
findViewById(R.id.btnCancel2).setOnClickListener(new View.OnClickListener() {
|
findViewById(R.id.btnCancel2).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
|
@ -128,44 +98,4 @@ public class pttActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadHistory() {
|
|
||||||
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
|
||||||
setupAdapter(etIp, prefs.getString(KEY_IP_HISTORY, ""));
|
|
||||||
setupAdapter(etAccount, prefs.getString(KEY_ACCOUNT_HISTORY, ""));
|
|
||||||
setupAdapter(etPassword, prefs.getString(KEY_PASSWORD_HISTORY, ""));
|
|
||||||
setupAdapter(etGroupId, prefs.getString(KEY_GROUP_HISTORY, ""));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupAdapter(AutoCompleteTextView view, String historyStr) {
|
|
||||||
List<String> items = new ArrayList<>();
|
|
||||||
if (!historyStr.isEmpty()) {
|
|
||||||
items.addAll(Arrays.asList(historyStr.split(SEPARATOR)));
|
|
||||||
}
|
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
|
|
||||||
android.R.layout.simple_dropdown_item_1line, items);
|
|
||||||
view.setAdapter(adapter);
|
|
||||||
view.setThreshold(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void saveToHistory(String ip, String account, String password, String groupId) {
|
|
||||||
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
editor.putString(KEY_IP_HISTORY, addToHistory(prefs.getString(KEY_IP_HISTORY, ""), ip));
|
|
||||||
editor.putString(KEY_ACCOUNT_HISTORY, addToHistory(prefs.getString(KEY_ACCOUNT_HISTORY, ""), account));
|
|
||||||
editor.putString(KEY_PASSWORD_HISTORY, addToHistory(prefs.getString(KEY_PASSWORD_HISTORY, ""), password));
|
|
||||||
editor.putString(KEY_GROUP_HISTORY, addToHistory(prefs.getString(KEY_GROUP_HISTORY, ""), groupId));
|
|
||||||
editor.apply();
|
|
||||||
loadHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String addToHistory(String existing, String newValue) {
|
|
||||||
LinkedHashSet<String> set = new LinkedHashSet<>();
|
|
||||||
if (!existing.isEmpty()) {
|
|
||||||
set.addAll(Arrays.asList(existing.split(SEPARATOR)));
|
|
||||||
}
|
|
||||||
set.remove(newValue);
|
|
||||||
set.add(newValue);
|
|
||||||
return String.join(SEPARATOR, set);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,9 @@ class LoginActivity : AppCompatActivity() {
|
||||||
|
|
||||||
// 4. 退出按钮
|
// 4. 退出按钮
|
||||||
btnExit.setOnClickListener {
|
btnExit.setOnClickListener {
|
||||||
com.stand.standapp.utils.AppKiller.killApp(this)
|
fullCleanup()
|
||||||
|
finishAffinity()
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid())
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 彩蛋:点击底部版权 10 次进入开发者模式
|
// 5. 彩蛋:点击底部版权 10 次进入开发者模式
|
||||||
|
|
@ -146,17 +148,18 @@ class LoginActivity : AppCompatActivity() {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag("PTT").e(e, "PTT init error")
|
Timber.tag("PTT").e(e, "PTT init error")
|
||||||
}
|
}
|
||||||
|
Thread {
|
||||||
Toast.makeText(this@LoginActivity, "PTT初始化中...", Toast.LENGTH_SHORT).show()
|
|
||||||
|
|
||||||
// 使用 Handler 替代硬编码 Thread.sleep,避免阻塞或随意开辟线程
|
|
||||||
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
||||||
try {
|
try {
|
||||||
|
Thread.sleep(500)
|
||||||
|
runOnUiThread {
|
||||||
|
Toast.makeText(this@LoginActivity, "PTT初始化中", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
Thread.sleep(3000)
|
||||||
com.example.kingway.ptt.SendAtUtil.toLogin("gzzdweb01", "123456", "61.243.1.123", 1, 1)
|
com.example.kingway.ptt.SendAtUtil.toLogin("gzzdweb01", "123456", "61.243.1.123", 1, 1)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.tag("PTT").e(e, "PTT login error")
|
Timber.tag("PTT").e(e, "PTT login error")
|
||||||
}
|
}
|
||||||
}, 1500)
|
}.start()
|
||||||
|
|
||||||
// 2. 启动硬件 GPIO 监听
|
// 2. 启动硬件 GPIO 监听
|
||||||
try {
|
try {
|
||||||
|
|
@ -239,9 +242,57 @@ class LoginActivity : AppCompatActivity() {
|
||||||
.setTitle("提示")
|
.setTitle("提示")
|
||||||
.setMessage("确定要退出应用吗?")
|
.setMessage("确定要退出应用吗?")
|
||||||
.setPositiveButton("确定") { _, _ ->
|
.setPositiveButton("确定") { _, _ ->
|
||||||
com.stand.standapp.utils.AppKiller.killApp(this)
|
fullCleanup()
|
||||||
|
finishAffinity()
|
||||||
|
Process.killProcess(Process.myPid())
|
||||||
}
|
}
|
||||||
.setNegativeButton("取消", null)
|
.setNegativeButton("取消", null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun fullCleanup() {
|
||||||
|
pttLogoutAndDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pttLogoutAndDestroy() {
|
||||||
|
try {
|
||||||
|
com.example.kingway.ptt.SendAtUtil.toCancelLogin()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "PTT cancel failed")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
com.example.kingway.ptt.SendAtUtil.toLogout()
|
||||||
|
Timber.tag("LoginActivity").i("PTT logout sent")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "PTT logout failed")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
com.stand.standapp.utils.GpioManager.stopListening()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "GPIO stop failed")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
com.stand.standapp.utils.GpioManager.stopListening()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "GPIO stop failed")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// MyApplication.destroy()
|
||||||
|
Timber.tag("LoginActivity").i("PTT native destroyed")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "PTT destroy failed")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// PrinterManager.shutdown()
|
||||||
|
Timber.tag("LoginActivity").i("Printer shut down")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "Printer shutdown failed")
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
com.stand.standapp.utils.LogManager.shutdown()
|
||||||
|
Timber.tag("LoginActivity").i("LogManager shut down")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.tag("LoginActivity").e(e, "LogManager shutdown failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,25 +58,19 @@ class MainActivity : AppCompatActivity() {
|
||||||
val ctx = instance ?: return@runOnUiThread
|
val ctx = instance ?: return@runOnUiThread
|
||||||
androidx.appcompat.app.AlertDialog.Builder(ctx)
|
androidx.appcompat.app.AlertDialog.Builder(ctx)
|
||||||
.setTitle("账号冲突")
|
.setTitle("账号冲突")
|
||||||
.setMessage("您的 PTT 账号已在其他设备登录,当前设备已被迫下线。\n请点击确认重启应用。")
|
.setMessage("您的 PTT 账号已在其他设备登录,当前设备已被迫下线。\n请点击确认退出并重新登录。")
|
||||||
.setCancelable(false)
|
.setCancelable(false)
|
||||||
.setPositiveButton("确认") { _, _ ->
|
.setPositiveButton("确认") { _, _ ->
|
||||||
com.stand.standapp.utils.AppKiller.killApp(ctx, restart = true)
|
try {
|
||||||
}
|
com.example.kingway.ptt.SendAtUtil.toLogout()
|
||||||
.show()
|
// com.example.kingway.ptt.MyApplication.destroy()
|
||||||
}
|
com.stand.standapp.utils.GpioManager.stopListening()
|
||||||
}
|
} catch (_: Exception) {}
|
||||||
|
val pm = ctx.packageManager
|
||||||
@JvmStatic
|
val intent = pm.getLaunchIntentForPackage(ctx.packageName)
|
||||||
fun showPttUpdateDialog() {
|
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
instance?.runOnUiThread {
|
ctx.startActivity(intent)
|
||||||
val ctx = instance ?: return@runOnUiThread
|
android.os.Process.killProcess(android.os.Process.myPid())
|
||||||
androidx.appcompat.app.AlertDialog.Builder(ctx)
|
|
||||||
.setTitle("账号更新")
|
|
||||||
.setMessage("您的 PTT 账号信息已被更新,需要重新登录以应用更改。\n请点击确认重启应用。")
|
|
||||||
.setCancelable(false)
|
|
||||||
.setPositiveButton("确认") { _, _ ->
|
|
||||||
com.stand.standapp.utils.AppKiller.killApp(ctx, restart = true)
|
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
@ -231,81 +225,17 @@ class MainActivity : AppCompatActivity() {
|
||||||
val result = handleBridgeCall(payload, prompt.defaultValue ?: "")
|
val result = handleBridgeCall(payload, prompt.defaultValue ?: "")
|
||||||
return GeckoResult.fromValue(prompt.confirm(result))
|
return GeckoResult.fromValue(prompt.confirm(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认处理(显示对话框)
|
// 默认处理(显示对话框)
|
||||||
val res = GeckoResult<GeckoSession.PromptDelegate.PromptResponse>()
|
return null
|
||||||
runOnUiThread {
|
|
||||||
val input = android.widget.EditText(this@MainActivity)
|
|
||||||
input.setText(prompt.defaultValue)
|
|
||||||
androidx.appcompat.app.AlertDialog.Builder(this@MainActivity)
|
|
||||||
.setTitle(prompt.title ?: "")
|
|
||||||
.setMessage(prompt.message)
|
|
||||||
.setView(input)
|
|
||||||
.setPositiveButton("确定") { _, _ -> res.complete(prompt.confirm(input.text.toString())) }
|
|
||||||
.setNegativeButton("取消") { _, _ -> res.complete(prompt.dismiss()) }
|
|
||||||
.setOnCancelListener { res.complete(prompt.dismiss()) }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAlertPrompt(
|
override fun onAlertPrompt(
|
||||||
session: GeckoSession,
|
session: GeckoSession,
|
||||||
prompt: GeckoSession.PromptDelegate.AlertPrompt
|
prompt: GeckoSession.PromptDelegate.AlertPrompt
|
||||||
): GeckoResult<GeckoSession.PromptDelegate.PromptResponse>? {
|
): GeckoResult<GeckoSession.PromptDelegate.PromptResponse>? {
|
||||||
val res = GeckoResult<GeckoSession.PromptDelegate.PromptResponse>()
|
Timber.tag("WebConsole").d("Alert: ${prompt.message}")
|
||||||
runOnUiThread {
|
|
||||||
androidx.appcompat.app.AlertDialog.Builder(this@MainActivity)
|
|
||||||
.setMessage(prompt.message)
|
|
||||||
.setPositiveButton("确定") { _, _ -> res.complete(prompt.dismiss()) }
|
|
||||||
.setOnCancelListener { res.complete(prompt.dismiss()) }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAuthPrompt(
|
|
||||||
session: GeckoSession,
|
|
||||||
prompt: GeckoSession.PromptDelegate.AuthPrompt
|
|
||||||
): GeckoResult<GeckoSession.PromptDelegate.PromptResponse>? {
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onChoicePrompt(
|
|
||||||
session: GeckoSession,
|
|
||||||
prompt: GeckoSession.PromptDelegate.ChoicePrompt
|
|
||||||
): GeckoResult<GeckoSession.PromptDelegate.PromptResponse>? {
|
|
||||||
val res = GeckoResult<GeckoSession.PromptDelegate.PromptResponse>()
|
|
||||||
runOnUiThread {
|
|
||||||
val choices = prompt.choices.map { it.label }.toTypedArray()
|
|
||||||
androidx.appcompat.app.AlertDialog.Builder(this@MainActivity)
|
|
||||||
.setTitle(prompt.title)
|
|
||||||
.setSingleChoiceItems(choices, -1) { dialog, which ->
|
|
||||||
dialog.dismiss()
|
|
||||||
res.complete(prompt.confirm(prompt.choices[which]))
|
|
||||||
}
|
|
||||||
.setOnCancelListener { res.complete(prompt.dismiss()) }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onButtonPrompt(
|
|
||||||
session: GeckoSession,
|
|
||||||
prompt: GeckoSession.PromptDelegate.ButtonPrompt
|
|
||||||
): GeckoResult<GeckoSession.PromptDelegate.PromptResponse>? {
|
|
||||||
val res = GeckoResult<GeckoSession.PromptDelegate.PromptResponse>()
|
|
||||||
runOnUiThread {
|
|
||||||
androidx.appcompat.app.AlertDialog.Builder(this@MainActivity)
|
|
||||||
.setTitle(prompt.title ?: "提示")
|
|
||||||
.setMessage(prompt.message)
|
|
||||||
.setPositiveButton("确定") { _, _ -> res.complete(prompt.confirm(GeckoSession.PromptDelegate.ButtonPrompt.Type.POSITIVE)) }
|
|
||||||
.setNegativeButton("取消") { _, _ -> res.complete(prompt.dismiss()) }
|
|
||||||
.setOnCancelListener { res.complete(prompt.dismiss()) }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,7 +263,9 @@ class MainActivity : AppCompatActivity() {
|
||||||
.setTitle("提示")
|
.setTitle("提示")
|
||||||
.setMessage("确定要退出应用吗?")
|
.setMessage("确定要退出应用吗?")
|
||||||
.setPositiveButton("确定") { _, _ ->
|
.setPositiveButton("确定") { _, _ ->
|
||||||
com.stand.standapp.utils.AppKiller.killApp(this)
|
pttLogoutAndDestroy()
|
||||||
|
finishAffinity()
|
||||||
|
android.os.Process.killProcess(android.os.Process.myPid())
|
||||||
}
|
}
|
||||||
.setNegativeButton("取消", null)
|
.setNegativeButton("取消", null)
|
||||||
.show()
|
.show()
|
||||||
|
|
|
||||||
|
|
@ -57,9 +57,6 @@ class MyApplication : Application() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
mApp = this
|
mApp = this
|
||||||
pageName = packageName
|
pageName = packageName
|
||||||
|
|
||||||
com.stand.standapp.utils.AppKiller.setupUncaughtExceptionHandler(this)
|
|
||||||
|
|
||||||
initPTT()
|
initPTT()
|
||||||
// 第一步:初始化日志
|
// 第一步:初始化日志
|
||||||
LogManager.init(this)
|
LogManager.init(this)
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,8 @@ object PrinterManager {
|
||||||
mContext?.let {
|
mContext?.let {
|
||||||
if (!hasPaper) {
|
if (!hasPaper) {
|
||||||
android.widget.Toast.makeText(it, "⚠️ 警告:打印机缺纸!", android.widget.Toast.LENGTH_LONG).show()
|
android.widget.Toast.makeText(it, "⚠️ 警告:打印机缺纸!", android.widget.Toast.LENGTH_LONG).show()
|
||||||
} else {
|
|
||||||
android.widget.Toast.makeText(it, "打印机状态:正常 (有纸)", android.widget.Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知前端页面
|
|
||||||
com.stand.standapp.MainActivity.executeJs("if(window.onPaperStateChanged) { window.onPaperStateChanged($hasPaper); }")
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
package com.stand.standapp.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
object AppKiller {
|
|
||||||
|
|
||||||
private var isKilling = false
|
|
||||||
|
|
||||||
fun killApp(context: Context, isCrash: Boolean = false, restart: Boolean = false) {
|
|
||||||
if (isKilling) return
|
|
||||||
isKilling = true
|
|
||||||
|
|
||||||
Timber.tag("AppKiller").i("Starting app cleanup. isCrash: $isCrash, restart: $restart")
|
|
||||||
|
|
||||||
try {
|
|
||||||
GpioManager.stopListening()
|
|
||||||
Timber.tag("AppKiller").i("GPIO stopped")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.tag("AppKiller").e(e, "GPIO stop failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
com.example.kingway.ptt.SendAtUtil.toCancelLogin()
|
|
||||||
com.example.kingway.ptt.SendAtUtil.toLogout()
|
|
||||||
Timber.tag("AppKiller").i("PTT logged out")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.tag("AppKiller").e(e, "PTT logout failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
com.stand.standapp.printer.PrinterManager.getFactory()?.ClosePort()
|
|
||||||
Timber.tag("AppKiller").i("Printer port closed")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.tag("AppKiller").e(e, "Printer close failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.tag("AppKiller").i("Cleanup finished. Killing process.")
|
|
||||||
|
|
||||||
if (!isCrash) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(200)
|
|
||||||
} catch (e: Exception) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// LogManager.shutdown() 会关闭其内部的线程池
|
|
||||||
// 调用后绝对不能再使用 Timber 打印日志,否则会抛出 RejectedExecutionException 导致死循环
|
|
||||||
LogManager.shutdown()
|
|
||||||
} catch (e: Exception) {}
|
|
||||||
|
|
||||||
if (restart) {
|
|
||||||
try {
|
|
||||||
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
||||||
if (intent != null) {
|
|
||||||
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK or android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
val flags = android.app.PendingIntent.FLAG_ONE_SHOT or android.app.PendingIntent.FLAG_IMMUTABLE
|
|
||||||
val pendingIntent = android.app.PendingIntent.getActivity(context, 223344, intent, flags)
|
|
||||||
val mgr = context.getSystemService(Context.ALARM_SERVICE) as android.app.AlarmManager
|
|
||||||
mgr.set(android.app.AlarmManager.RTC, System.currentTimeMillis() + 500, pendingIntent)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
android.os.Process.killProcess(android.os.Process.myPid())
|
|
||||||
System.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setupUncaughtExceptionHandler(context: Context) {
|
|
||||||
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
|
|
||||||
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
|
||||||
Timber.tag("AppKiller").e(throwable, "Uncaught exception detected! Force cleanup.")
|
|
||||||
killApp(context, true)
|
|
||||||
defaultHandler?.uncaughtException(thread, throwable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -12,7 +12,6 @@ import timber.log.Timber
|
||||||
*/
|
*/
|
||||||
object GpioManager {
|
object GpioManager {
|
||||||
private var mZysjSystemManager: ZysjSystemManager? = null
|
private var mZysjSystemManager: ZysjSystemManager? = null
|
||||||
@Volatile
|
|
||||||
private var mIsRunning = false
|
private var mIsRunning = false
|
||||||
private var mThread: Thread? = null
|
private var mThread: Thread? = null
|
||||||
private var lastGpioValue = 1 // 默认松开状态 (1)
|
private var lastGpioValue = 1 // 默认松开状态 (1)
|
||||||
|
|
@ -55,7 +54,6 @@ object GpioManager {
|
||||||
|
|
||||||
fun stopListening() {
|
fun stopListening() {
|
||||||
mIsRunning = false
|
mIsRunning = false
|
||||||
mThread?.interrupt()
|
|
||||||
mThread = null
|
mThread = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,97 +5,18 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="com.example.kingway.ptt.pttActivity">
|
tools:context="com.example.kingway.ptt.pttActivity">
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="16dp">
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
>
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="服务器IP"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:id="@+id/etIp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="服务器IP"
|
|
||||||
android:inputType="text"
|
|
||||||
android:text="220.154.131.231"
|
|
||||||
android:dropDownWidth="match_parent"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="账号"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:id="@+id/etAccount"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="账号"
|
|
||||||
android:inputType="text"
|
|
||||||
android:text="test004"
|
|
||||||
android:dropDownWidth="match_parent"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="密码"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:id="@+id/etPassword"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="密码"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:text="123456"
|
|
||||||
android:dropDownWidth="match_parent"
|
|
||||||
android:layout_marginBottom="8dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="群组ID"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:layout_marginBottom="4dp"/>
|
|
||||||
|
|
||||||
<AutoCompleteTextView
|
|
||||||
android:id="@+id/etGroupId"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="群组ID"
|
|
||||||
android:inputType="text"
|
|
||||||
android:text="00000001"
|
|
||||||
android:dropDownWidth="match_parent"
|
|
||||||
android:layout_marginBottom="16dp"/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/login"
|
android:id="@+id/login"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="默认登录(gzzdweb01)"/>
|
android:text="login"/>
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnCustomLogin"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="自定义登录"/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/pttBtn"
|
android:id="@+id/pttBtn"
|
||||||
|
|
@ -150,7 +71,5 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="login1" />
|
android:text="login1" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue