冲突
This commit is contained in:
844143714@qq,com 2026-05-21 20:43:23 +08:00
parent dbb430b088
commit d022fa5f2d
9 changed files with 175 additions and 85 deletions

View File

@ -3,12 +3,10 @@ package com.example.kingway.ptt;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import timber.log.Timber;
import com.stand.standapp.MyApplication;
import java.util.Arrays;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class AudioRecordManager {
private final String TAG = "AudioRecordManager";
@ -32,14 +30,17 @@ public class AudioRecordManager {
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize * 2);
Timber.tag(TAG).d("AudioRecord初始化: source=MIC, freq=%dHz, minBuf=%d", FREQUENCY, bufferSize * 2);
}
public void setRecording(){
Timber.tag(TAG).d("setRecording -> startRecording()");
if (mRecorder == null) initAudio();
if(mRecorder.getRecordingState() != 3) mRecorder.startRecording();
}
public void stopAudio(){
Timber.tag(TAG).d("stopAudio -> stop()");
if (mRecorder != null){
mRecorder.stop();
}
@ -51,6 +52,7 @@ public class AudioRecordManager {
}
public void destroyAudio(){
Timber.tag(TAG).d("destroyAudio -> 释放AudioRecord");
if (mRecorder != null){
mRecorder.stop();
mRecorder.release();
@ -82,12 +84,13 @@ public class AudioRecordManager {
try {
recordThread.interrupt();
} catch (Exception e) {
Timber.tag(TAG).e(e, "录音线程interrupt失败");
recordThread = null;
}
}
recordThread = null;
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "destroyThread异常");
} finally {
recordThread = null;
}
@ -97,6 +100,7 @@ public class AudioRecordManager {
* 启动录音线程
*/
private void startThread() {
Timber.tag(TAG).d("启动录音线程");
destroyThread();
isStart = true;
if (mRecorder == null) {
@ -119,10 +123,12 @@ public class AudioRecordManager {
int bytesRecord;
byte[] tempBuffer = new byte[bufferSize];
if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
Timber.tag(TAG).w("AudioRecord未初始化(state=%d),终止录音", mRecorder.getState());
stopRecord();
return;
}
mRecorder.startRecording();
Timber.tag(TAG).d("录音线程: startRecording() 成功");
int count = 0;
while (isStart) {
if (null != mRecorder) {
@ -132,15 +138,16 @@ public class AudioRecordManager {
continue;
}
if (bytesRecord != 0 && bytesRecord != -1) {
Log.i(TAG, "tempBuffer->" + Arrays.toString(tempBuffer));
Timber.tag(TAG).v("录音帧: %d bytes", bytesRecord);
MyApplication.getAppInstance().getpNative().pttNativeSendRecordCmd(tempBuffer);
} else {
Timber.tag(TAG).w("录音循环退出: bytesRecord=%d", bytesRecord);
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "录音线程异常");
}
}
@ -150,12 +157,13 @@ public class AudioRecordManager {
* 启动录音
*/
public void startRecord() {
Timber.tag(TAG).d("startRecord() 被调用");
try {
stopRecord();
startThread();
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "startRecord异常");
}
}
@ -163,6 +171,7 @@ public class AudioRecordManager {
* 停止录音
*/
public void stopRecord() {
Timber.tag(TAG).d("stopRecord() 被调用");
try {
destroyThread();
if (mRecorder != null) {
@ -172,10 +181,10 @@ public class AudioRecordManager {
}
mRecorder.release();
mRecorder = null;
} catch (Exception e) { }
} catch (Exception e) { Timber.tag(TAG).e(e, "释放AudioRecord失败"); }
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "stopRecord异常");
} finally {
}
}

View File

@ -2,6 +2,8 @@ package com.example.kingway.ptt;
import android.util.Log;
import timber.log.Timber;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
@ -55,22 +57,23 @@ public class CallBackResolution {
String ngx = "\\u";
try {
Log.i(TAG, "lisHex-->" + lisHex.toString());
Timber.tag(TAG).i( "lisHex-->" + lisHex.toString());
String firstHex = lisHex.get(0);
String errorNum = lisHex.get(1);
Log.d(TAG, "at_OEM_AT_cb firstHex: " + firstHex + ", errorNum: " + errorNum);
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;
Log.d(TAG,"PTT login status: "+pttLoginStatus+", id: "+pttLoginId);
Timber.tag(TAG).d("PTT login status: "+pttLoginStatus+", id: "+pttLoginId);
// 实时推送状态到前端
com.stand.standapp.MainActivity.executeJs("updatePttLoginStatus('" + pttLoginStatus + "','" + 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.showPttConflictDialog();
}
if ("02".equals(errorNum)) {
@ -94,17 +97,17 @@ public class CallBackResolution {
break;
case "0b"://发起讲话返回状态
Log.d(TAG, "at_OEM_AT_cb ptt speak...");
Timber.tag(TAG).d( "at_OEM_AT_cb ptt speak...");
CallBackUtil.callBackSpeakPlaySuccess(errorNum.equals("00")? true : false);
break;
case "0c"://结束讲话返回状态
Log.d(TAG, "at_OEM_AT_cb speak end...");
Timber.tag(TAG).d( "at_OEM_AT_cb speak end...");
CallBackUtil.callBackSpeakReleaseSuccess(errorNum.equals("00")? true : false);
break;
case "80"://返回群组信息
Log.d(TAG, "at_OEM_AT_cb get group info: " + lisHex);
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);
@ -123,13 +126,13 @@ public class CallBackResolution {
}
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = "";
tempTxt = unicodeToString(strGroup.toString());
Log.i(TAG, " 组名称 " + tempTxt);
Timber.tag(TAG).i( " 组名称 " + tempTxt);
CallBackUtil.callBackGroupInfo(groupId, tempTxt, groupNo, groupCount);
Log.d(TAG, "at_OEM_AT_cb group info groupId: " + groupId + ", groupNo: " + groupNo+", groupCount: " +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.add(groupInfo);
@ -156,7 +159,7 @@ public class CallBackResolution {
}
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = unicodeToString(groupMemberName.toString());
GroupMemberInfoDto infoDto = new GroupMemberInfoDto(memberId, tempTxt, status, memberNo, haveVideo);
@ -192,7 +195,7 @@ public class CallBackResolution {
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = "";
for (int i = 0; i < stringsSpker.size(); i++) {
@ -232,7 +235,7 @@ public class CallBackResolution {
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String tempTxt = "";
for (int i = 0; i < stringsTalk.size(); i++) {
@ -251,7 +254,7 @@ public class CallBackResolution {
code=-1;
}
Log.i(TAG, " 临时讲话 信息通知的指令 " + tempTxt + " code=" + code);
Timber.tag(TAG).i( " 临时讲话 信息通知的指令 " + tempTxt + " code=" + code);
CallBackUtil.callBackTempCallNotify(tempTxt);
// 处理临时呼叫页面逻辑
@ -274,7 +277,7 @@ public class CallBackResolution {
);
com.stand.standapp.MyApplication.getAppInstance().startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "Failed to open TempCallActivity: " + e.getMessage());
Timber.tag(TAG).e( "Failed to open TempCallActivity: " + e.getMessage());
}
} else if (code == 0) {
// 退出临时呼叫 关闭临时呼叫页面
@ -318,13 +321,13 @@ public class CallBackResolution {
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "Decode hex failed");
} finally {
String userGroupName = "";
for (int i = 0; i < stringsJumpGroup.size(); i++) {
userGroupName += unicode(stringsJumpGroup.get(i));
}
Log.i(TAG,"进入群组 " + userGroupName);
Timber.tag(TAG).i("进入群组 " + userGroupName);
indexGroupId = userGroupId;
indexGroupName = userGroupName;
CallBackUtil.callBackNotifyUpdateGroup(userGroupId, userGroupName);
@ -351,7 +354,7 @@ public class CallBackResolution {
String time = strLis[2];
CallBackUtil.callBackNotifyLocationStatus(0, locationMemberId, latitude, longitude, time);
} catch (Exception e) {
Log.e(TAG, "获取定位Exception-->" + e.getMessage());
Timber.tag(TAG).e( "获取定位Exception-->" + e.getMessage());
}
} else {
CallBackUtil.callBackNotifyLocationStatus(1, "", 0.0, 0.0, "");
@ -410,7 +413,7 @@ public class CallBackResolution {
string += lisHex.get(i).toString();
}
String address = hexString2String(string);
Log.i(TAG, "address-->" + address);
Timber.tag(TAG).i( "address-->" + address);
String[] strLis = address.split(",");
latitude = Double.parseDouble(strLis[0]);
@ -452,7 +455,7 @@ public class CallBackResolution {
break;
}
}catch (Exception e){
Log.e(TAG,"at_OEM_AT_cb Exception " + e.getMessage());
Timber.tag(TAG).e("at_OEM_AT_cb Exception " + e.getMessage());
}
}
@ -469,26 +472,30 @@ public class CallBackResolution {
* tip1min内会出现40次左右0-1切换
*/
public static boolean isLoginConflict(String state) {
Log.d(TAG, " isLoginConflict state: " + state + ",oldLoginSate: " + oldLoginSate + ", logNum: " + logNum);
Timber.tag(TAG).d( " isLoginConflict state: " + state + ",oldLoginSate: " + oldLoginSate + ", logNum: " + logNum);
if (oldLoginSate.equals("00") && state.equals("01")) {
Log.d(TAG, "isLoginConflict first login...");
Timber.tag(TAG).d( "isLoginConflict first login...");
logNum++;
oldLoginSate = state;
} else if (oldLoginSate.equals("01") && state.equals("00")) {
Log.d(TAG, "isLoginConflict login state changed...");
Timber.tag(TAG).d( "isLoginConflict login state changed...");
logNum++;
oldLoginSate = state;
}
if (1 == logNum) {
offlineTime = System.currentTimeMillis();
}
Log.d(TAG, "isLoginConflict logNum: " + logNum + ", offlineTime: " + offlineTime);
if (logNum > 4) {
Log.d(TAG, "isLoginConflict current time: " + System.currentTimeMillis() + ", diffTime: " + (System.currentTimeMillis() - offlineTime));
if (System.currentTimeMillis() - offlineTime >= 60 * 1000) {
Log.d(TAG, "isLoginConflict login state changed: " + logNum + " in 1min");
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;
}
@ -497,7 +504,7 @@ public class CallBackResolution {
try {
String[] txt = sData.split("\"");
String data = txt[1];
Log.d(TAG, "at_Play_TTS_cb sData: " + sData);
Timber.tag(TAG).d( "at_Play_TTS_cb sData: " + sData);
String ngx = "\\u";
List<String> strings = new ArrayList<>();
@ -521,38 +528,37 @@ public class CallBackResolution {
}
} catch (Exception e) {
e.printStackTrace();
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();
Log.i(TAG,"at_Play_TTS_cb " + tempTxt);
Timber.tag(TAG).i("at_Play_TTS_cb " + tempTxt);
if (tempTxt.contains("已登录")) {
String showName = tempTxt.substring(3, tempTxt.length());
CallBackUtil.callBackLogin(true, showName);
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable task = () -> {
Log.d(TAG, "callBackLogin send tcp and udp");
Timber.tag(TAG).d( "callBackLogin send tcp and udp");
SendAtUtil.sendUDP();
SendAtUtil.sendTCP();
};
executor.scheduleAtFixedRate(task, 0, 40, TimeUnit.SECONDS);
} else if (tempTxt.contains("账号已更新")) {
// com.stand.standapp.MainActivity.showPttUpdateDialog("您的 PTT 账号信息已被更新,需要重新登录以应用更改。\n请点击确认重启应用。","账号更新");
Log.d(TAG, "--------------------------1");
Log.d(TAG, tempTxt);
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("请配置账号")) {
// com.stand.standapp.MainActivity.showPttUpdateDialog("首次登录信息更新。\n请点击确认重启应用。","登录信息更新");
Timber.tag(TAG).w("TTS reports no account configured: '%s'", tempTxt);
}
}
} catch (Exception e) {
e.printStackTrace();
Timber.tag(TAG).e(e, "Decode hex failed");
}
}

View File

@ -1,6 +1,6 @@
package com.example.kingway.ptt;
import android.util.Log;
import timber.log.Timber;
import java.util.ArrayList;
import java.util.List;
@ -18,7 +18,7 @@ public class CallBackUtil {
* name 用户名
*/
public static void callBackLogin(boolean loginState, String name) {
Log.i(TAG, "callBackLogin loginState " + loginState + " name " + name);
Timber.tag(TAG).d("callBackLogin loginState=%s name=%s", loginState, name);
}
/**
@ -34,7 +34,7 @@ public class CallBackUtil {
* loginId 用户ID登录成功id有效
*/
public static void callBackLoinState(String state, String loginId) {
Log.d(TAG, "callBackLoinState state: " + state + ", loginId: " + loginId);
Timber.tag(TAG).d("callBackLoinState state=%s loginId=%s", state, loginId);
}
/**
@ -43,13 +43,14 @@ public class CallBackUtil {
* false登录不冲突
* */
public static void callBackLoginConflict(boolean loginConflict) {
Log.d(TAG, "callBackLoginConflict: " + loginConflict);
Timber.tag(TAG).d("callBackLoginConflict: %s", loginConflict);
}
/**
* 登录账号有视频功能通知
*/
public static boolean callBackHaveVideo() {
Timber.tag(TAG).d("callBackHaveVideo invoked");
return true;
}
@ -60,7 +61,7 @@ public class CallBackUtil {
* state true-成功 false-失败
*/
public static void callBackOnGroupSuccess(boolean state) {
Timber.tag(TAG).d("callBackOnGroupSuccess state=%s", state);
}
/**
@ -80,7 +81,7 @@ public class CallBackUtil {
}
}
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('MemberList', " + arr.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
@ -88,13 +89,16 @@ public class CallBackUtil {
* state true-成功 false-失败
*/
public static void callBackSpeakPlaySuccess(boolean state) {
Log.d(TAG, "callBackSpeakPlaySuccess state: " + state);
if (state) AudioRecordManager.getInstance().startRecord();
Timber.tag(TAG).d("callBackSpeakPlaySuccess state=%s", state);
if (state) {
AudioRecordManager.getInstance().startRecord();
Timber.tag(TAG).d("AudioRecord started");
}
try {
org.json.JSONObject obj = new org.json.JSONObject();
obj.put("success", state);
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('SpeakResult', " + obj.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
@ -106,7 +110,7 @@ public class CallBackUtil {
org.json.JSONObject obj = new org.json.JSONObject();
obj.put("success", state);
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('SpeakEnd', " + obj.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
@ -116,7 +120,9 @@ public class CallBackUtil {
* groupNo 表示此组为第几个组
* groupCount 表示此组用户成员数
*/
public static void callBackGroupInfo(String groupId, String groupName, int groupNo, int groupCount) {}
public static void callBackGroupInfo(String groupId, String groupName, int groupNo, int groupCount) {
Timber.tag(TAG).d("callBackGroupInfo groupId=%s name=%s no=%d count=%d", groupId, groupName, groupNo, groupCount);
}
/**
* 返回群组集合信息
@ -133,7 +139,7 @@ public class CallBackUtil {
arr.put(obj);
}
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('GroupList', " + arr.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
@ -143,6 +149,7 @@ public class CallBackUtil {
* talkName 讲话用户名字
* */
public static void callBackNotifySpker ( int speckType, String groupId, String groupName, String talkId, String talkName){
Timber.tag(TAG).d("callBackNotifySpker speckType=%d groupId=%s talkId=%s talkName=%s", speckType, groupId, talkId, talkName);
try {
org.json.JSONObject obj = new org.json.JSONObject();
obj.put("speckType", speckType);
@ -151,10 +158,11 @@ public class CallBackUtil {
obj.put("talkId", talkId);
obj.put("talkName", talkName);
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('SpeakerUpdate', " + obj.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
public static void callBackNotifyPlayStatus ( int playStatus, int speckType, String groupId, String groupName, String talkId, String talkName){
Timber.tag(TAG).d("callBackNotifyPlayStatus playStatus=%d talkId=%s", playStatus, talkId);
try {
org.json.JSONObject obj = new org.json.JSONObject();
obj.put("playStatus", playStatus);
@ -164,24 +172,26 @@ public class CallBackUtil {
obj.put("talkId", talkId);
obj.put("talkName", talkName);
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('PlayStatus', " + obj.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
public static void callBackTempCallNotify (String tempCallNotify){
Timber.tag(TAG).d("callBackTempCallNotify msg=%s", 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) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
public static void callBackNotifyUpdateGroup (String userGroupId, String userGroupName){
Timber.tag(TAG).d("callBackNotifyUpdateGroup groupId=%s groupName=%s", userGroupId, 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) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
@ -193,6 +203,7 @@ public class CallBackUtil {
* time 时间
*/
public static void callBackNotifyLocationStatus ( int status, String userId,double latitude, double longitude, String time){
Timber.tag(TAG).d("callBackNotifyLocationStatus status=%d userId=%s lat=%f lng=%f", status, userId, latitude, longitude);
try {
org.json.JSONObject obj = new org.json.JSONObject();
obj.put("status", status);
@ -201,7 +212,7 @@ public class CallBackUtil {
obj.put("longitude", longitude);
obj.put("time", time);
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('LocationStatus', " + obj.toString() + ");}");
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
@ -209,7 +220,7 @@ public class CallBackUtil {
* status true成功 false失败
* */
public static void callBackNotifyUploadLocation ( boolean status){
Timber.tag(TAG).d("callBackNotifyUploadLocation status=%s", status);
}
/**
@ -221,7 +232,7 @@ public class CallBackUtil {
* inv=11勾选单呼0不勾选单呼
* */
public static void callBackNotifyCodeInfo (String codeInfo){
Timber.tag(TAG).d("callBackNotifyCodeInfo: %s", codeInfo);
}
/**
@ -231,7 +242,7 @@ public class CallBackUtil {
* pwd=111111 账号密码111111
* */
public static void callBackNotifyPwdInfo (String pwdInfo){
Timber.tag(TAG).d("callBackNotifyPwdInfo: %s", pwdInfo);
}
/**
@ -239,7 +250,7 @@ public class CallBackUtil {
* status true成功false失败
* */
public static void callBackNotifyCodeOpenStatus ( boolean status){
Timber.tag(TAG).d("callBackNotifyCodeOpenStatus status=%s", status);
}
/**
@ -247,7 +258,7 @@ public class CallBackUtil {
* status true成功false失败
* */
public static void callBackNotifyCodeExiteStatus ( boolean status){
Timber.tag(TAG).d("callBackNotifyCodeExiteStatus status=%s", status);
}
/**
@ -255,7 +266,7 @@ public class CallBackUtil {
* status true成功false失败
* */
public static void callBackNotifyUploadAgentPwd ( boolean status){
Timber.tag(TAG).d("callBackNotifyUploadAgentPwd status=%s", status);
}
/**
@ -263,7 +274,7 @@ public class CallBackUtil {
* status true成功false失败
* */
public static void callBackRequestReportAlarm ( boolean status){
Timber.tag(TAG).d("callBackRequestReportAlarm status=%s", status);
}
/**
@ -271,7 +282,7 @@ public class CallBackUtil {
* status true成功false失败
* */
public static void callBackRequestStopAlarm ( boolean status){
Timber.tag(TAG).d("callBackRequestStopAlarm status=%s", status);
}
/**
@ -281,19 +292,21 @@ public class CallBackUtil {
* longitude 经度
* */
public static void callBackEnotifyAlarm (String memberName,double latitude, double longitude){
Timber.tag(TAG).w("callBackEnotifyAlarm memberName=%s lat=%f lng=%f", memberName, latitude, 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) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "Failed to push PTT event to JS"); }
}
/**
* 收到群成员停止警告通知
* */
public static void callBackEnotifyStopAlarm () {
Timber.tag(TAG).d("callBackEnotifyStopAlarm invoked");
com.stand.standapp.MainActivity.executeJs("if(window.onPttEvent){window.onPttEvent('StopAlarm', {});}");
}
@ -302,7 +315,7 @@ public class CallBackUtil {
* status true成功false失败
* */
public static void callBackRequestNfc ( boolean status){
Timber.tag(TAG).d("callBackRequestNfc status=%s", status);
}
}

View File

@ -48,6 +48,7 @@ public class SendAtUtil {
* tempCall 是否开启单呼 1开启单呼0关闭单呼
* */
public static void toLogin(String account, String pwd, String ip, int location, int tempCall){
Timber.tag("PTT").i("Login: account=%s, ip=%s, gps=%d, tempCall=%d", account, ip, location, tempCall);
String ipHex = str2HexStr("ip=" + ip + ";");
String accountHex = str2HexStr("id=" + account + ";");
String pwdHex = str2HexStr("pwd=" + pwd + ";");
@ -62,22 +63,26 @@ public class SendAtUtil {
* 按下PTT开始呼叫
* */
public static void speakPlay(){
Timber.tag("PTT").i("PTT speak START (press)");
send("0B0000");
}
/** 松开PTT结束呼叫*/
public static void speakRelease(){
Timber.tag("PTT").i("PTT speak STOP (release)");
AudioRecordManager.getInstance().stopRecord();
send("0C0000");
}
/** 发送TCP包 登录成功后需要定时发送TCP包*/
public static void sendTCP(){
Timber.tag("PTT").v("TCP keepalive sent");
send("570000\r\n");
}
/** 发送UDP包 登录后需要定时发送UDP包*/
public static void sendUDP(){
Timber.tag("PTT").v("UDP keepalive sent");
send("580000\r\n");
}
@ -97,6 +102,7 @@ public class SendAtUtil {
* groupId 十六进制群组Id
* */
public static void jumpGroup(String groupId){
Timber.tag("PTT").i("Jump to group: %s", groupId);
send("090000" + groupId);
}

View File

@ -5,7 +5,7 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.util.Log;
import timber.log.Timber;
import java.util.ArrayList;
import java.util.List;
@ -15,8 +15,9 @@ public class pttNative {
static {
try {
System.loadLibrary("ptt");
Timber.tag(TAG).d("Native library 'ptt' loaded successfully");
} catch (Throwable e) {
Log.e(TAG, "Failed to load ptt library: " + e.getMessage());
Timber.tag(TAG).e(e, "Failed to load ptt library");
}
}
@ -36,7 +37,7 @@ public class pttNative {
//Callback Function for Recv the message from POC Lib
public void OEM_AT_cb(String sData)
{
Log.e(TAG,"Poc2 AT Callback: " + sData);
Timber.tag(TAG).d("Poc2 AT Callback: %s", sData);
int len, i, j=0;
char[] bytes = sData.toCharArray();
@ -50,12 +51,15 @@ public class pttNative {
lisHex.add(temp.length() == 1 ? "0" + temp : temp);
}
}
} else {
Timber.tag(TAG).w("OEM_AT_cb: unexpected format, len=%d, data=%s", bytes.length, sData);
}
CallBackResolution.at_OEM_AT_cb(lisHex);
}
public void OEM_Play_cb(byte[] bytes){
Timber.tag(TAG).d("OEM_Play_cb: %d bytes", bytes.length);
if (mAudioTrack == null) {
initAudioTrack();
}
@ -68,27 +72,27 @@ public class pttNative {
public void OEM_Play_Start_cb()
{
Log.e("Play Start!!!!\r\n","");
Timber.tag(TAG).e("Play Start!!!!");
}
public void OEM_Play_Stop_cb()
{
Log.e("Play Stop!!!!\r\n","");
Timber.tag(TAG).e("Play Stop!!!!");
}
public void OEM_Record_Start_cb()
{
Log.e("Record Start!!!!\r\n","");
Timber.tag(TAG).e("Record Start!!!!");
}
public void OEM_Record_Stop_cb()
{
Log.e("Record Stop!!!!\r\n","");
Timber.tag(TAG).e("Record Stop!!!!");
}
public void OEM_Play_TTS_cb(String sData)
{
Log.e(TAG , "Play TTS " + sData);
Timber.tag(TAG).e("Play TTS " + sData);
CallBackResolution.at_Play_TTS_cb(sData);
}
@ -112,16 +116,17 @@ public class pttNative {
private native int OEM_Set_Package_Name(String name);
public void pttNativeSendRecordCmd(byte[] data) {
Timber.tag(TAG).d("pttNativeSendRecordCmd: %d bytes", data.length);
OEM_Push_Record_Data(data);
}
public void pttNativeSendAtCmd(String strAtCmd)
{
try {
Log.i(TAG,"strAtCmd " + strAtCmd);
Timber.tag(TAG).i("AT Send: %s", strAtCmd);
OEM_AT_Send(strAtCmd);
} catch (Throwable e) {
Log.e(TAG, "Native AT Send failed: " + e.getMessage());
Timber.tag(TAG).e( "Native AT Send failed: " + e.getMessage());
}
}
@ -130,7 +135,7 @@ public class pttNative {
try {
OEM_Poc_Task_Start();
} catch (Throwable e) {
Log.e(TAG, "Native PocTaskStart failed");
Timber.tag(TAG).e(e, "Native PocTaskStart failed");
}
}
@ -139,6 +144,7 @@ public class pttNative {
try {
return OEM_Set_AT_CallBack("OEM_AT_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set AT callback");
return -1;
}
}
@ -148,6 +154,7 @@ public class pttNative {
try {
return OEM_Set_Play_CallBack("OEM_Play_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set Play callback");
return -1;
}
}
@ -157,6 +164,7 @@ public class pttNative {
try {
return OEM_Set_Play_Start_Callback("OEM_Play_Start_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set PlayStart callback");
return -1;
}
}
@ -166,6 +174,7 @@ public class pttNative {
try {
return OEM_Set_Play_Stop_Callback("OEM_Play_Stop_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set PlayStop callback");
return -1;
}
}
@ -175,6 +184,7 @@ public class pttNative {
try {
return OEM_Set_Record_Start_Callback("OEM_Record_Start_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set RecordStart callback");
return -1;
}
}
@ -184,6 +194,7 @@ public class pttNative {
try {
return OEM_Set_Record_Stop_Callback("OEM_Record_Stop_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set RecordStop callback");
return -1;
}
}
@ -193,6 +204,7 @@ public class pttNative {
try {
return OEM_Set_TTS_Callback("OEM_Play_TTS_cb");
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set TTS callback");
return -1;
}
}
@ -202,17 +214,20 @@ public class pttNative {
try {
return OEM_Set_Package_Name(name);
} catch (Throwable e) {
Timber.tag(TAG).e(e, "Failed to set package callback");
return -1;
}
}
public pttNative() {
Timber.tag(TAG).d("pttNative instance created");
initAudioTrack();
}
private AudioTrack mAudioTrack;
public void initAudioTrack() {
int mAudioMinBufSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
Timber.tag(TAG).d("AudioTrack init: minBufSize=%d", mAudioMinBufSize);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) // 通信场景
@ -245,7 +260,7 @@ public class pttNative {
}
mAudioTrack.release();
mAudioTrack = null;
} catch (Exception e) {}
} catch (Exception e) { Timber.tag(TAG).e(e, "AudioTrack destroy failed"); }
}
}

View File

@ -107,6 +107,8 @@ class AndroidInterface(private val activity: MainActivity) {
SendAtUtil.speakPlay()
Timber.tag("PTT").d("Speak Play")
MainActivity.executeJs("updatePttTalkStatus(true)")
} else {
Timber.tag("PTT").w("Speak Play ignored: already speaking")
}
}
@ -125,11 +127,13 @@ class AndroidInterface(private val activity: MainActivity) {
json.put("groupName", com.example.kingway.ptt.CallBackResolution.getCurrentGroupName())
json.toString()
} catch (e: Exception) {
Timber.tag("PTT").e(e, "pttGetCurrentGroup failed")
"{\"groupId\":\"\",\"groupName\":\"\"}"
}
}
fun pttSpeakRelease() {
Timber.tag("PTT").d("pttSpeakRelease called, isSpeaking=%s", isSpeaking)
if (isSpeaking) {
val now = System.currentTimeMillis()
val duration = now - lastSpeakTime
@ -152,34 +156,42 @@ class AndroidInterface(private val activity: MainActivity) {
SendAtUtil.speakRelease()
Timber.tag("PTT").d("Real Speak Release")
MainActivity.executeJs("updatePttTalkStatus(false)")
} else {
Timber.tag("PTT").w("realRelease skipped: already released")
}
}
fun pttForceRelease() {
val wasSpeaking = isSpeaking
isSpeaking = false
SendAtUtil.speakRelease()
Timber.tag("PTT").d("Force Release")
Timber.tag("PTT").d("Force Release (wasSpeaking=%s)", wasSpeaking)
}
fun pttGetGroupInfo() {
Timber.tag("PTT").d("pttGetGroupInfo called")
SendAtUtil.getGroupInfo()
}
fun pttGetGroupMemberInfo(gid: String) {
Timber.tag("PTT").d("pttGetGroupMemberInfo: gid=%s", gid)
SendAtUtil.getGroupMemberInfo(gid)
}
fun simulatePttConflict() {
Timber.tag("PTT").w("simulatePttConflict: showing conflict dialog via JS bridge")
Handler(Looper.getMainLooper()).post {
MainActivity.showPttConflictDialog()
}
}
fun pttSendAtCmd(cmd: String) {
Timber.tag("PTT").d("pttSendAtCmd: %s", cmd)
SendAtUtil.sendRawCmd(cmd)
}
fun pttJumpGroup(gid: String) {
Timber.tag("PTT").d("pttJumpGroup: gid=%s", gid)
SendAtUtil.jumpGroup(gid)
}
@ -358,6 +370,7 @@ class AndroidInterface(private val activity: MainActivity) {
val packageInfo = activity.packageManager.getPackageInfo(activity.packageName, 0)
packageInfo.versionName ?: "1.0.0"
} catch (e: Exception) {
Timber.tag("AndroidInterface").e(e, "getVersionName failed")
"1.0.0"
}
}
@ -383,6 +396,7 @@ class AndroidInterface(private val activity: MainActivity) {
json.toString()
} catch (e: Exception) {
Timber.tag("AndroidInterface").e(e, "getAboutInfo failed")
"{ \"appName\": \"消防值守台\", \"versionName\": \"1.0.0\" }"
}
}
@ -405,6 +419,7 @@ class AndroidInterface(private val activity: MainActivity) {
val prefs = activity.getSharedPreferences("msg_records", Context.MODE_PRIVATE)
prefs.getString("history", "") ?: ""
} catch (e: Exception) {
Timber.tag("Storage").e(e, "getMessages failed")
""
}
}
@ -442,6 +457,7 @@ class AndroidInterface(private val activity: MainActivity) {
}
fun showRestartDialog(message: String, title: String) {
Timber.tag("AndroidInterface").w("showRestartDialog from JS: title=%s msg=%s", title, message)
Handler(Looper.getMainLooper()).post {
com.stand.standapp.MainActivity.showPttUpdateDialog(message, title)
}

View File

@ -106,6 +106,7 @@ class LoginActivity : AppCompatActivity() {
}
private fun performLogin(username: String, password: String, remember: Boolean, officialMode: Boolean) {
Timber.tag("Login").d("开始登录: username=%s, server=%s", username, AppConfig.getServerUrl(this))
val serverUrl = AppConfig.getServerUrl(this)
val loginUrl = "$serverUrl/api/auth/login"
@ -128,6 +129,7 @@ class LoginActivity : AppCompatActivity() {
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Timber.tag("Login").e(e, "登录网络请求失败")
runOnUiThread {
loadingDialog.dismiss()
Toast.makeText(applicationContext, "网络异常: ${e.message}", Toast.LENGTH_SHORT).show()
@ -160,12 +162,14 @@ class LoginActivity : AppCompatActivity() {
Toast.makeText(applicationContext, "登录成功", Toast.LENGTH_SHORT).show()
// 获取独立的 PTT 账号并初始化
Timber.tag("PTT").d("开始PTT初始化流程: username=%s, hasToken=%b", username, accessToken.isNotEmpty())
initPttWithAccount(username, accessToken)
// 2. 启动硬件 GPIO 监听
try {
com.stand.standapp.utils.GpioManager.init(this@LoginActivity)
com.stand.standapp.utils.GpioManager.startListening()
Timber.tag("GPIO").d("GPIO初始化并启动监听成功")
} catch (e: Exception) {
Timber.tag("GPIO").e(e, "GPIO init error")
}
@ -312,6 +316,7 @@ class LoginActivity : AppCompatActivity() {
private fun initPttWithAccount(username: String, accessToken: String) {
if (accessToken.isEmpty()) {
Timber.tag("PTT").w("accessToken为空使用默认PTT密码: username=%s", username)
doPttInit(username, "123456")
return
}
@ -339,8 +344,10 @@ class LoginActivity : AppCompatActivity() {
val data = json.optJSONObject("data")
val pttUsername = data?.optString("cardNumber", username) ?: username
val pttPassword = data?.optString("pocPassword", "123456") ?: "123456"
Timber.tag("PTT").d("获取到服务器PTT账号: cardNumber=%s", pttUsername)
doPttInit(pttUsername, pttPassword)
} else {
Timber.tag("PTT").w("获取PTT账号失败, code=%d", json.optInt("code"))
doPttInit(username, "123456")
}
} catch (e: Exception) {
@ -352,21 +359,26 @@ class LoginActivity : AppCompatActivity() {
}
private fun doPttInit(pttUsername: String, pttPassword: String) {
Timber.tag("PTT").d("PTT初始化开始: username=%s", pttUsername)
android.os.Handler(android.os.Looper.getMainLooper()).post {
Toast.makeText(applicationContext, "PTT初始化中...", Toast.LENGTH_SHORT).show()
}
// 1. PTT 引擎强制重置后登录 (销毁旧 session确保干净状态)
try {
Timber.tag("PTT").d("发送PTT取消登录指令")
com.example.kingway.ptt.SendAtUtil.toCancelLogin()
Timber.tag("PTT").d("发送PTT注销指令")
com.example.kingway.ptt.SendAtUtil.toLogout()
} catch (e: Exception) {
Timber.tag("PTT").e(e, "PTT init error")
}
// 使用 Handler 替代硬编码 Thread.sleep避免阻塞或随意开辟线程
Timber.tag("PTT").d("等待1500ms以确保旧session完全清理")
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
try {
Timber.tag("PTT").d("发送PTT登录指令: username=%s, ip=%s", pttUsername, "220.154.131.231")
com.example.kingway.ptt.SendAtUtil.toLogin(pttUsername, pttPassword, "220.154.131.231", 1, 1)
} catch (e: Exception) {
Timber.tag("PTT").e(e, "PTT login error")

View File

@ -49,6 +49,7 @@ class MainActivity : AppCompatActivity() {
// 硬件按键触发
@JvmStatic
fun onHardwarePttDown() {
Timber.tag("MainActivity").d("onHardwarePttDown triggered")
instance?.runOnUiThread {
instance?.pttInterface?.pttSpeakPlay()
executeJs("onHardwarePttDown()")
@ -57,6 +58,7 @@ class MainActivity : AppCompatActivity() {
@JvmStatic
fun onHardwarePttUp() {
Timber.tag("MainActivity").d("onHardwarePttUp triggered")
instance?.runOnUiThread {
instance?.pttInterface?.pttSpeakRelease()
executeJs("onHardwarePttUp()")
@ -65,6 +67,7 @@ class MainActivity : AppCompatActivity() {
@JvmStatic
fun showPttConflictDialog() {
Timber.tag("PTT").w("showPttConflictDialog: account conflict detected")
if (isConflictDialogShowing) return
isConflictDialogShowing = true
instance?.runOnUiThread {
@ -83,10 +86,12 @@ class MainActivity : AppCompatActivity() {
@JvmStatic
fun showPttUpdateDialog(msg: String, title: String) {
Timber.tag("PTT").w("showPttUpdateDialog: title=%s msg=%s", title, msg)
if (isConflictDialogShowing) return
val ctx = instance
if (ctx == null) {
pendingDialog = title to msg
Timber.tag("PTT").d("Pending dialog stored (instance null): title=%s", title)
return
}
isConflictDialogShowing = true
@ -106,6 +111,7 @@ class MainActivity : AppCompatActivity() {
@JvmStatic
fun showPendingDialogIfNeeded() {
val pending = pendingDialog ?: return
Timber.tag("PTT").d("Flushing pending dialog: %s", pending.second)
pendingDialog = null
showPttUpdateDialog(pending.second, pending.first)
}

View File

@ -78,10 +78,14 @@ object GpioManager {
MainActivity.executeJs("updatePttTalkStatus(true)")
// 同步 TempCallActivity
TempCallActivity.instance?.onGpioPressed()
Timber.tag("GPIO").d("Notified TempCallActivity: onGpioPressed")
} else {
Timber.tag("GPIO").w("GPIO pressed but isSpeaking already true — ignoring")
}
} else {
// 硬件松开 PTT
Timber.tag("GPIO").d("GPIO released, isSpeaking=%s", isSpeaking)
if (isSpeaking) {
val now = System.currentTimeMillis()
val duration = now - lastSpeakTime
@ -109,6 +113,9 @@ object GpioManager {
MainActivity.executeJs("updatePttTalkStatus(false)")
// 同步 TempCallActivity
TempCallActivity.instance?.onGpioReleased()
Timber.tag("GPIO").d("Notified TempCallActivity: onGpioReleased")
} else {
Timber.tag("GPIO").w("GPIO realRelease skipped: already released")
}
}
}