StandAPP/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java

619 lines
29 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.example.kingway.ptt;
import android.util.Log;
import timber.log.Timber;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**解析接收到的AT指令*/
public class CallBackResolution {
private static String TAG = "PTT_RX";
private static String indexGroupId = "";//用户所在群组Id
private static String indexGroupName = "";//用户所在群组名称
private static final int MAX_GROUP_MEMBERS = 500;
private static final int MAX_GROUP_INFOS = 100;
private static final Map<String, GroupMemberInfoDto> memberInfoDtos = new LinkedHashMap<>(MAX_GROUP_MEMBERS);
private static int speckType = 1;
private static String talkId = "";
private static String talkName = "";
private static final Map<String, GroupInfoDto> groupInfos = new LinkedHashMap<>(MAX_GROUP_INFOS);
private static String oldLoginSate = "00";
private static int logNum = 0;
private static long offlineTime = 0;
private static boolean loginState = false;
private static String pttLoginStatus = "00"; // 00-未登录 01-登录中 02-已登录
private static String pttLoginId = "";
private static ScheduledExecutorService keepAliveExecutor = null;
public static String getPttLoginStatus() {
return pttLoginStatus;
}
public static String getPttLoginId() {
return pttLoginId;
}
public static void resetPttLoginStatus() {
pttLoginStatus = "00";
pttLoginId = "";
}
public static String getCurrentGroupId() {
return indexGroupId;
}
public static String getCurrentGroupName() {
return indexGroupName;
}
public static void at_OEM_AT_cb(List<String> lisHex){
if (lisHex.size() == 0) {
return;
}
String ngx = "\\u";
try {
Timber.tag(TAG).i( "lisHex-->" + lisHex.toString());
String firstHex = lisHex.get(0);
String errorNum = lisHex.get(1);
Timber.tag(TAG).d( "at_OEM_AT_cb firstHex: " + firstHex + ", errorNum: " + errorNum);
switch (firstHex){
case "82"://登录状态通知指令
String loginId = lisHex.get(2) + "" + lisHex.get(3) + "" + lisHex.get(4) + "" + lisHex.get(5);
pttLoginStatus = errorNum;
pttLoginId = loginId;
Timber.tag(TAG).d("PTT login status: "+pttLoginStatus+", id: "+pttLoginId);
// 实时推送状态到前端
CallBackUtil.callBackLoinState(errorNum, loginId);
loginState = isLoginConflict(errorNum);
CallBackUtil.callBackLoginConflict(loginState);
if (loginState) {
Timber.tag(TAG).w("Login conflict detected, showing conflict dialog");
com.stand.standapp.MainActivity.executeJs("updatePttLoginStatus('" + pttLoginStatus + "','" + pttLoginId + "')");
com.stand.standapp.MainActivity.showPttConflictDialog();
}
if ("00".equals(errorNum)) {
com.stand.standapp.MainActivity.executeJs("updatePttLoginStatus('" + pttLoginStatus + "','" + pttLoginId + "')");
}
if ("02".equals(errorNum)) {
com.stand.standapp.MainActivity.onPttLoginSuccess();
com.stand.standapp.MainActivity.executeJs("updatePttLoginStatus('" + pttLoginStatus + "','" + pttLoginId + "')");
}
break;
case "a2"://账号有视频功能
CallBackUtil.callBackHaveVideo();
break;
case "0d"://查询群组返回状态
if (errorNum.equals("00")){
CallBackUtil.callBackGroupInfo(groupInfos);
}
//CallBackUtil.callBackOnGroupSuccess(errorNum.equals("01")? false : true);
break;
case "0e"://查询群组成员返回状态
CallBackUtil.callBackOnMemberSuccess(errorNum.equals("01")? false : true, memberInfoDtos);
break;
case "0b"://发起讲话返回状态
Timber.tag(TAG).d( "at_OEM_AT_cb ptt speak...");
CallBackUtil.callBackSpeakPlaySuccess(errorNum.equals("00")? true : false);
break;
case "0c"://结束讲话返回状态
Timber.tag(TAG).d( "at_OEM_AT_cb speak end...");
CallBackUtil.callBackSpeakReleaseSuccess(errorNum.equals("00")? true : false);
break;
case "80"://返回群组信息
Timber.tag(TAG).d( "at_OEM_AT_cb get group info: " + lisHex);
StringBuilder strGroup = new StringBuilder();
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 groupCount = Integer.valueOf((lisHex.get(10) + "" + lisHex.get(11)), 16);
try {
int numB = 13;
int size = lisHex.size() - 12;
size = (size / 2) - 1;
for (int i = 0; i < size; i++) {
if (i == 0) {
strGroup.append(ngx).append(lisHex.get(i + numB)).append(lisHex.get(i + numB - 1));
} else {
strGroup.append(ngx).append(lisHex.get(i + numB + 1)).append(lisHex.get(i + numB));
numB = numB + 1;
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = "";
tempTxt = unicodeToString(strGroup.toString());
Timber.tag(TAG).i( " 组名称 " + tempTxt);
CallBackUtil.callBackGroupInfo(groupId, tempTxt, groupNo, groupCount);
Timber.tag(TAG).d( "at_OEM_AT_cb group info groupId: " + groupId + ", groupNo: " + groupNo+", groupCount: " +groupCount);
if (1 == groupNo) groupInfos.clear();
GroupInfoDto groupInfo = new GroupInfoDto(groupId, tempTxt, groupNo, groupCount);
groupInfos.put(groupId, groupInfo);
}
break;
case "81"://返回群成员信息
StringBuilder groupMemberName = new StringBuilder();
String status = lisHex.get(1);
boolean haveVideo = lisHex.get(10).equals("01") ? true : false;
String memberId = lisHex.get(6) + "" + lisHex.get(7) + "" + lisHex.get(8) + "" + lisHex.get(9);
int memberNo = Integer.valueOf((lisHex.get(4) + "" + lisHex.get(5)), 16);
try {
int numB = 12;
int size = lisHex.size() - 11;
size = (size / 2) - 1;
for (int i = 0; i < size; i++) {
if (i == 0) {
groupMemberName.append(ngx).append(lisHex.get(i + numB)).append(lisHex.get(i + numB - 1));
} else {
groupMemberName.append(ngx).append(lisHex.get(i + numB + 1)).append(lisHex.get(i + numB));
numB = numB + 1;
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = unicodeToString(groupMemberName.toString());
GroupMemberInfoDto infoDto = new GroupMemberInfoDto(memberId, tempTxt, status, memberNo, haveVideo);
if (memberNo == 1) memberInfoDtos.clear();
memberInfoDtos.put(memberId, infoDto);
}
break;
case "83"://讲话用户信息通知
if (lisHex.get(1).equals("00")) {
speckType = 0;
} else {
speckType = 1;
}
talkId = lisHex.get(2) + "" + lisHex.get(3) + "" + lisHex.get(4) + "" + lisHex.get(5);
List<String> stringsSpker = new ArrayList<>();
try {
int numA = 6;
int numB = 7;
int size = lisHex.size() - 6;
size = (size / 2) - 1;
for (int i = 0; i < size; i++) {
if (i == 0) {
stringsSpker.add(ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numA));
} else {
int start = i + numB;
int end = i + numB + 1;
stringsSpker.add(ngx + lisHex.get(end) + "" + lisHex.get(start));
numB = numB + 1;
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = "";
for (int i = 0; i < stringsSpker.size(); i++) {
tempTxt += unicode(stringsSpker.get(i));
}
talkName = tempTxt;
CallBackUtil.callBackNotifySpker(speckType, indexGroupId, indexGroupName, talkId, talkName);
}
break;
case "8b"://通知音频播放的状态
int notifyPlayStatus = Integer.parseInt(lisHex.get(2));
if (notifyPlayStatus == 0){
talkId = "";
talkName = "";
}
CallBackUtil.callBackNotifyPlayStatus(notifyPlayStatus, speckType, indexGroupId, indexGroupName, talkId, talkName);
break;
case "84"://提示信息通知
List<String> stringsTalk = new ArrayList<>();
try {
int numA = 2;
int numB = 3;
int size = lisHex.size() - 2;
size = (size / 2) - 1;
for (int i = 0; i < size; i++) {
if (i == 0) {
stringsTalk.add(ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numA));
} else {
int start = i + numB;
int end = i + numB + 1;
stringsTalk.add(ngx + lisHex.get(end) + "" + lisHex.get(start));
numB = numB + 1;
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = "";
for (int i = 0; i < stringsTalk.size(); i++) {
tempTxt += unicode(stringsTalk.get(i));
}
int code=-1;
if(tempTxt.equals("退出临时呼叫")){
code=0;
} else if (tempTxt.startsWith("临时呼叫临时群组")) {
code=1;
} else if (tempTxt.equals("呼出失败")||tempTxt.equals("呼叫失败")) {
code=2;
} else if (tempTxt.equals("呼叫成功")||tempTxt.equals("呼出成功")) {
code=3;
}else{
code=-1;
}
Timber.tag(TAG).i( " 临时讲话 信息通知的指令 " + tempTxt + " code=" + code);
CallBackUtil.callBackTempCallNotify(tempTxt);
// 处理临时呼叫页面逻辑
if (code == 1) {
// 临时呼叫临时群组 → 打开临时呼叫页面
try {
android.content.Intent intent = new android.content.Intent(
com.stand.standapp.MyApplication.getAppInstance(),
com.stand.standapp.TempCallActivity.class
);
// 从84消息中提取临时群名称格式为"临时呼叫XXX"
String tempGroupName = "临时会话";
if (tempTxt.startsWith("临时呼叫") && tempTxt.length() > "临时呼叫".length()) {
tempGroupName = tempTxt.substring("临时呼叫".length());
}
intent.putExtra("group_name", tempGroupName);
intent.addFlags(
android.content.Intent.FLAG_ACTIVITY_NEW_TASK |
android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP
);
com.stand.standapp.MyApplication.getAppInstance().startActivity(intent);
} catch (Exception e) {
Timber.tag(TAG).e( "Failed to open TempCallActivity: " + e.getMessage());
}
} else if (code == 0) {
// 退出临时呼叫 → 关闭临时呼叫页面
if (com.stand.standapp.TempCallActivity.isOpen()) {
com.stand.standapp.TempCallActivity.closeWithMessage("临时呼叫已结束");
} else if (com.stand.standapp.TempCallActivity.isMinimizedState()) {
// 最小化状态下收到结束消息,清除状态并通知前端
com.stand.standapp.TempCallActivity.clearMinimizedState();
}
} else if (code == 2) {
// 呼叫失败 → 关闭临时呼叫页面
// if (com.stand.standapp.TempCallActivity.isOpen()) {
// com.stand.standapp.TempCallActivity.closeWithMessage("呼叫失败");
// } else if (com.stand.standapp.TempCallActivity.isMinimizedState()) {
// // 最小化状态下收到失败消息,清除状态并通知前端
// com.stand.standapp.TempCallActivity.clearMinimizedState();
// }
}
}
break;
case "86"://用户进入群组信息通知
String userGroupId = lisHex.get(2) + "" + lisHex.get(3) + "" + lisHex.get(4) + "" + lisHex.get(5);
List<String> stringsJumpGroup = new ArrayList<>();
try {
int numA = 6;
int numB = 7;
int size = lisHex.size() - 6;
size = (size / 2) - 1;
for (int i = 0; i < size; i++) {
if (i == 0) {
stringsJumpGroup.add(ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numA));
} else {
int start = i + numB;
int end = i + numB + 1;
stringsJumpGroup.add(ngx + lisHex.get(end) + "" + lisHex.get(start));
numB = numB + 1;
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String userGroupName = "";
for (int i = 0; i < stringsJumpGroup.size(); i++) {
userGroupName += unicode(stringsJumpGroup.get(i));
}
Timber.tag(TAG).i("进入群组 " + userGroupName);
indexGroupId = userGroupId;
indexGroupName = userGroupName;
CallBackUtil.callBackNotifyUpdateGroup(userGroupId, userGroupName);
}
break;
case "8d"://用户定位信息通知
if (lisHex.get(1).equals("00")) {
String str = "";
String locationMemberId = lisHex.get(2) + "" + lisHex.get(3) + "" + lisHex.get(4) + "" + lisHex.get(5);
for (int i = 0; i < lisHex.size() - 1; i++) {
if (i > 5) {
str = str + "" + lisHex.get(i).toString();
}
}
String address = hexString2String(str);
address = address.substring(0,address.lastIndexOf(":"));
try {
String[] strLis = address.split(",");
double latitude = Double.parseDouble(strLis[1]);
double longitude = Double.parseDouble(strLis[0]);
String time = strLis[2];
CallBackUtil.callBackNotifyLocationStatus(0, locationMemberId, latitude, longitude, time);
} catch (Exception e) {
Timber.tag(TAG).e( "获取定位Exception-->" + e.getMessage());
}
} else {
CallBackUtil.callBackNotifyLocationStatus(1, "", 0.0, 0.0, "");
}
break;
case "11"://上报位置信息
CallBackUtil.callBackNotifyUploadLocation(errorNum.equals("00")? true : false);
break;
case "02"://获取写码账号信息
String str = "";
for (int i = 4; i < lisHex.size() - 2; i++) {
str = str + "" + lisHex.get(i);
}
String cmdTxt = hexString2String(str);
CallBackUtil.callBackNotifyCodeInfo(cmdTxt);
break;
case "48"://获取写码密码信息
String strPwd = "";
for (int i = 4; i < lisHex.size() - 2; i++) {
strPwd = strPwd + "" + lisHex.get(i);
}
String cmdPwdTxt = hexString2String(strPwd);
CallBackUtil.callBackNotifyPwdInfo(cmdPwdTxt);
break;
case "45"://进入写码模式通知
CallBackUtil.callBackNotifyCodeOpenStatus(errorNum.equals("00")? true : false);
break;
case "52"://退出写码模式操作
CallBackUtil.callBackNotifyCodeExiteStatus(errorNum.equals("00")? true : false);
break;
case "47"://上传经销商密码
CallBackUtil.callBackNotifyUploadAgentPwd(errorNum.equals("00")? true : false);
break;
case "53"://上报本机告警信号
CallBackUtil.callBackRequestReportAlarm(errorNum.equals("00")? true : false);
break;
case "54"://停止本机告警信号
CallBackUtil.callBackRequestStopAlarm(errorNum.equals("00")? true : false);
break;
case "8e"://收到群成员告警信号
if (lisHex.get(1).equals("00")) {
List<String> strName = new ArrayList<>();
double latitude = 0.0, longitude = 0.0;
try {
String string = "";
for (int i = 4; i < lisHex.size(); i++) {
string += lisHex.get(i).toString();
}
String address = hexString2String(string);
Timber.tag(TAG).i( "address-->" + address);
String[] strLis = address.split(",");
latitude = Double.parseDouble(strLis[0]);
longitude = Double.parseDouble(strLis[1]);
int size = lisHex.size() - (8 + strLis[0].length() + strLis[1].length());
size = size / 2;
int numA = 4 + strLis[0].length() + strLis[1].length() + 3;
int numB = 4 + strLis[0].length() + strLis[1].length() + 4;
for (int i = 0; i < size; i++) {
if (i == 0) {
strName.add(ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numA));
} else {
int start = i + numB;
int end = i + numB + 1;
strName.add(ngx + lisHex.get(end) + "" + lisHex.get(start));
numB = numB + 1;
}
}
} catch (Exception e) {
} finally {
String tempTxt = "";
for (int i = 0; i < strName.size(); i++) {
if (strName.get(i).equals("\\u0000")) break;
tempTxt += unicode(strName.get(i));
}
CallBackUtil.callBackEnotifyAlarm(tempTxt, latitude, longitude);
}
}
break;
case "8f"://收到群成员停止警告信息
CallBackUtil.callBackEnotifyStopAlarm();
break;
case "78"://上传NFC信息
CallBackUtil.callBackRequestNfc(errorNum.equals("00")? true : false);
break;
}
}catch (Exception e){
Timber.tag(TAG).e("at_OEM_AT_cb Exception " + e.getMessage());
}
}
/**
* 判断登录状态冲突, 1min内超过5次状态切换则视为登录冲突
* state
* 0 : 离线
* 1 : 登陆中
* 2 : 登陆成功
* 3 : 注销中
* 说明:第一次登录 通知状态 先01再02
* 离线再上线 通知状态 先00再01
* tip1min内会出现40次左右0-1切换
*/
public static boolean isLoginConflict(String state) {
Timber.tag(TAG).d( " isLoginConflict state: " + state + ",oldLoginSate: " + oldLoginSate + ", logNum: " + logNum);
if (oldLoginSate.equals("00") && state.equals("01")) {
Timber.tag(TAG).d( "isLoginConflict first login...");
logNum++;
oldLoginSate = state;
} else if (oldLoginSate.equals("01") && state.equals("00")) {
Timber.tag(TAG).d( "isLoginConflict login state changed...");
logNum++;
oldLoginSate = state;
}
if (1 == logNum) {
offlineTime = System.currentTimeMillis();
}
Timber.tag(TAG).d( "isLoginConflict logNum: " + logNum + ", offlineTime: " + offlineTime);
if (System.currentTimeMillis() - offlineTime <= 60 * 1000) {
if (logNum > 5) {
Timber.tag(TAG).d("isLoginConflict login state changed: " + logNum + " in 1min");
return true;
}
} else {
Timber.tag(TAG).d("isLoginConflict maybe network change, reset...");
logNum = 0;
offlineTime = System.currentTimeMillis();
return false;
}
return false;
}
public static void at_Play_TTS_cb(String sData){
try {
String[] txt = sData.split("\"");
String data = txt[1];
Timber.tag(TAG).d( "at_Play_TTS_cb sData: " + sData);
String ngx = "\\u";
List<String> strings = new ArrayList<>();
try {
String str = "";
for (int i = 0; i < data.length(); i++) {
if (i == 0) {
str = ngx + data.charAt(i);
} else if (i % 4 == 0) {
strings.add(str);
str = ngx + data.charAt(i);
} else if (i == data.length() - 1) {
str = str + data.charAt(i);
strings.add(str);
} else {
str = str + data.charAt(i);
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
StringBuilder sb = new StringBuilder();
for (int k = 0; k < strings.size(); k++) {
sb.append(unicode(strings.get(k)));
}
String tempTxt = sb.toString();
Timber.tag(TAG).i("at_Play_TTS_cb " + tempTxt);
if (tempTxt.contains("已登录")) {
String showName = tempTxt.substring(3, tempTxt.length());
CallBackUtil.callBackLogin(true, showName);
if (keepAliveExecutor != null && !keepAliveExecutor.isShutdown()) {
keepAliveExecutor.shutdownNow();
}
keepAliveExecutor = Executors.newSingleThreadScheduledExecutor();
Runnable task = () -> {
Timber.tag(TAG).d( "callBackLogin send tcp and udp");
SendAtUtil.sendUDP();
SendAtUtil.sendTCP();
};
keepAliveExecutor.scheduleAtFixedRate(task, 0, 40, TimeUnit.SECONDS);
} else if (tempTxt.contains("账号已更新")) {
Timber.tag(TAG).w("TTS reports account updated: '%s'", tempTxt);
} else if (tempTxt.contains("账号或密码错误")) {
CallBackUtil.callBackLogin(false, "");
Timber.tag(TAG).e("Account/password error, showing dialog. TTS: '%s'", tempTxt);
com.stand.standapp.MainActivity.showPttUpdateDialog("您的 PTT 账号信息账号或密码错误。\n请点击确认重启应用。","账号或密码错误");
}else if (tempTxt.contains("请配置账号")) {
Timber.tag(TAG).w("TTS reports no account configured: '%s'", tempTxt);
}
}
} catch (Exception e) {
Timber.tag(TAG).e(e, "Decode hex failed");
}
}
public static String unicode(String str) {
Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
Matcher matcher = pattern.matcher(str);
char ch;
while (matcher.find()) {
String group = matcher.group(2);
ch = (char) Integer.parseInt(group, 16);
String group1 = matcher.group(1);
str = str.replace(group1, ch + "");
}
return str;
}
public static String unicodeToString(String hex) {
int t = hex.length() / 6;
StringBuilder str = new StringBuilder();
for (int i = 0; i < t; i++) {
String s = hex.substring(i * 6, (i + 1) * 6);
// 高位需要补上00再转
String s1 = s.substring(2, 4) + "00";
// 低位直接转
String s2 = s.substring(4);
// 将16进制的string转为int
int n = Integer.valueOf(s1, 16) + Integer.valueOf(s2, 16);
// 将int转换为字符
char[] chars = Character.toChars(n);
str.append(new String(chars));
}
return str.toString();
}
public static String hexString2String(String src) {
String temp = "";
for (int i = 0; i < src.length() / 2; i++) {
temp = temp + (char) Integer.valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue();
}
return temp;
}
}