ptt
This commit is contained in:
parent
69140b73c8
commit
803fcb38d2
|
|
@ -157,49 +157,35 @@
|
|||
<!-- PTT 业务测试模块 -->
|
||||
<div class="ws-card ptt-card">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<strong style="color: #047857;">PTT 业务接口测试</strong>
|
||||
<strong style="color: #047857;">PTT 业务功能测试</strong>
|
||||
<span id="ptt-status-tag" class="ws-status ws-disconnected">PTT 未登录</span>
|
||||
</div>
|
||||
|
||||
<div class="ws-input-group" style="margin-top: 10px;">
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<div style="flex: 1;">
|
||||
<span class="ptt-label">POC 账号:</span>
|
||||
<input type="text" id="ptt-account" class="ws-input" value="test">
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<span class="ptt-label">频道 ID:</span>
|
||||
<input type="text" id="ptt-gid" class="ws-input" value="126953">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<div style="flex: 1;">
|
||||
<span class="ptt-label">频道/群组名称:</span>
|
||||
<input type="text" id="ptt-name" class="ws-input" value="测试频道">
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<span class="ptt-label">录音相对路径:</span>
|
||||
<input type="text" id="ptt-record-path" class="ws-input" placeholder="path/to/record.wav">
|
||||
<span class="ptt-label">群组 ID (十六进制):</span>
|
||||
<input type="text" id="ptt-gid" class="ws-input" value="00000001">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- PTT 核心对讲按钮 -->
|
||||
<div style="margin-top: 15px;">
|
||||
<button id="ptt-speak-btn" class="btn btn-red"
|
||||
style="height: 80px; font-size: 20px; background: #94a3b8; cursor: not-allowed;"
|
||||
disabled>长按对讲 (PTT)</button>
|
||||
</div>
|
||||
|
||||
<div class="ptt-btn-grid">
|
||||
<button class="btn ptt-btn btn-green" onclick="pttCreateGroup()">创建频道</button>
|
||||
<button class="btn ptt-btn btn-red" onclick="pttDeleteGroup()">删除频道</button>
|
||||
<button class="btn ptt-btn" style="background: #3b82f6;" onclick="pttAddMembers()">拉人入频道</button>
|
||||
<button class="btn ptt-btn btn-orange" onclick="pttKickMembers()">踢出频道</button>
|
||||
<button class="btn ptt-btn" style="background: #6366f1;" onclick="pttGetGroups()">频道列表</button>
|
||||
<button class="btn ptt-btn" style="background: #8b5cf6;" onclick="pttGetTempGroups()">临时列表</button>
|
||||
<button class="btn ptt-btn" style="background: #ec4899;" onclick="pttGetRecords()">查询录音</button>
|
||||
<button class="btn ptt-btn" style="background: #06b6d4;" onclick="pttGetRecordText()">录音转文字</button>
|
||||
<button class="btn ptt-btn btn-orange" style="background: #f59e0b;" onclick="pttPlayRecord()">播放录音</button>
|
||||
<button class="btn ptt-btn btn-green" onclick="pttCreateTempGroup()">创建临时频道</button>
|
||||
<button class="btn ptt-btn btn-red" onclick="pttDeleteTempGroup()">删除临时频道</button>
|
||||
<button class="btn ptt-btn" style="background: #4f46e5;" onclick="pttGetTempMembers()">查临时成员</button>
|
||||
<button class="btn ptt-btn ptt-action-btn" style="background: #6366f1;" onclick="pttGetGroups()" disabled>获取群组</button>
|
||||
<button class="btn ptt-btn ptt-action-btn" style="background: #8b5cf6;" onclick="pttGetMembers()" disabled>获取成员</button>
|
||||
<button class="btn ptt-btn ptt-action-btn" style="background: #3b82f6;" onclick="pttJumpGroup()" disabled>切换群组</button>
|
||||
<button class="btn ptt-btn ptt-action-btn btn-orange" onclick="pttForceRelease()" disabled>强制释放</button>
|
||||
</div>
|
||||
|
||||
<div id="ptt-result" class="log-container" style="background: #0f172a; margin-top: 10px; height: 120px;">
|
||||
<div class="log-item">PTT 接口响应将在此展示...</div>
|
||||
<div class="log-item">等待 PTT 登录成功后激活功能...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -637,121 +623,109 @@
|
|||
|
||||
setTimeout(loadHistory, 500);
|
||||
|
||||
// === PTT 接口测试逻辑 ===
|
||||
var PTT_BASE = "http://127.0.0.1:8080/api/external/ptt";
|
||||
// === PTT 业务测试逻辑 ===
|
||||
var pttBtn = document.getElementById('ptt-speak-btn');
|
||||
var pttStatusTag = document.getElementById('ptt-status-tag');
|
||||
var pttResult = document.getElementById('ptt-result');
|
||||
|
||||
function showPttResult(msg) {
|
||||
var container = document.getElementById('ptt-result');
|
||||
var time = new Date().toLocaleTimeString();
|
||||
var item = document.createElement('div');
|
||||
item.className = 'log-item';
|
||||
var displayMsg = typeof msg === 'string' ? msg : JSON.stringify(msg, null, 2);
|
||||
item.innerHTML = '<span class="log-time">[' + time + ']</span><span class="log-msg">' + displayMsg + '</span>';
|
||||
container.insertBefore(item, container.firstChild);
|
||||
item.innerHTML = '<span class="log-time">[' + time + ']</span><span class="log-msg">' + msg + '</span>';
|
||||
pttResult.insertBefore(item, pttResult.firstChild);
|
||||
}
|
||||
|
||||
// 移除 async/await,使用 Promise
|
||||
function pttRequest(path, method, query, body) {
|
||||
if (method === undefined) method = 'GET';
|
||||
if (query === undefined) query = {};
|
||||
if (body === undefined) body = null;
|
||||
// PTT 登录成功回调 (由 Android 调用)
|
||||
function onPttLoginSuccess() {
|
||||
pttStatusTag.innerText = "PTT 已登录";
|
||||
pttStatusTag.className = "ws-status ws-connected";
|
||||
|
||||
var url = PTT_BASE + path;
|
||||
var qParams = [];
|
||||
for (var key in query) {
|
||||
qParams.push(encodeURIComponent(key) + "=" + encodeURIComponent(query[key]));
|
||||
// 激活按钮
|
||||
pttBtn.disabled = false;
|
||||
pttBtn.style.background = "#ef4444";
|
||||
pttBtn.style.cursor = "pointer";
|
||||
|
||||
var actionBtns = document.querySelectorAll('.ptt-action-btn');
|
||||
for (var i = 0; i < actionBtns.length; i++) {
|
||||
actionBtns[i].disabled = false;
|
||||
}
|
||||
if (qParams.length > 0) url += "?" + qParams.join("&");
|
||||
|
||||
var options = {
|
||||
method: method,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
};
|
||||
if (body) options.body = JSON.stringify(body);
|
||||
showPttResult("PTT 业务初始化成功,对讲功能已就绪");
|
||||
}
|
||||
|
||||
fetch(url, options)
|
||||
.then(function(response) {
|
||||
if (path === '/record/play') {
|
||||
showPttResult("流式播放接口已调用,请查看网络请求或直接点击预览链接");
|
||||
return;
|
||||
// 修复 PTT 按键逻辑:支持 TouchEvents 并处理异常中断
|
||||
if (pttBtn) {
|
||||
var isDown = false;
|
||||
|
||||
var startSpeak = function(e) {
|
||||
if (pttBtn.disabled) return;
|
||||
e.preventDefault();
|
||||
if (!isDown) {
|
||||
isDown = true;
|
||||
pttBtn.style.background = "#b91c1c";
|
||||
pttBtn.innerText = "正在通话...";
|
||||
if (window.android && window.android.pttSpeakPlay) {
|
||||
window.android.pttSpeakPlay();
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data) showPttResult(data);
|
||||
})
|
||||
.catch(function(e) {
|
||||
showPttResult("请求失败: " + e.message);
|
||||
});
|
||||
}
|
||||
showPttResult("申请话权...");
|
||||
}
|
||||
};
|
||||
|
||||
function pttCreateGroup() {
|
||||
var name = document.getElementById('ptt-name').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/group', 'POST', { gname: name }, [account]);
|
||||
}
|
||||
var stopSpeak = function(e) {
|
||||
if (isDown) {
|
||||
isDown = false;
|
||||
pttBtn.style.background = "#ef4444";
|
||||
pttBtn.innerText = "长按对讲 (PTT)";
|
||||
if (window.android && window.android.pttSpeakRelease) {
|
||||
window.android.pttSpeakRelease();
|
||||
}
|
||||
showPttResult("释放话权");
|
||||
}
|
||||
};
|
||||
|
||||
function pttDeleteGroup() {
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
pttRequest('/group/' + gid, 'DELETE');
|
||||
}
|
||||
// 绑定事件
|
||||
pttBtn.addEventListener('touchstart', startSpeak);
|
||||
pttBtn.addEventListener('mousedown', startSpeak);
|
||||
|
||||
function pttAddMembers() {
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/group/' + gid + '/members', 'POST', {}, [account]);
|
||||
}
|
||||
|
||||
function pttKickMembers() {
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/group/' + gid + '/members', 'DELETE', {}, [account]);
|
||||
// 关键:除了 end/up,还要处理 cancel 和 leave
|
||||
pttBtn.addEventListener('touchend', stopSpeak);
|
||||
pttBtn.addEventListener('touchcancel', stopSpeak);
|
||||
pttBtn.addEventListener('mouseup', stopSpeak);
|
||||
pttBtn.addEventListener('mouseleave', stopSpeak);
|
||||
}
|
||||
|
||||
function pttGetGroups() {
|
||||
pttRequest('/groups', 'GET', { page: 1, limit: 10 });
|
||||
if (window.android && window.android.pttGetGroupInfo) {
|
||||
window.android.pttGetGroupInfo();
|
||||
showPttResult("已请求群组列表");
|
||||
}
|
||||
}
|
||||
|
||||
function pttGetTempGroups() {
|
||||
pttRequest('/temp-groups', 'GET', { page: 1, limit: 10 });
|
||||
}
|
||||
|
||||
function pttGetRecords() {
|
||||
function pttGetMembers() {
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
var now = new Date();
|
||||
var end = now.getFullYear() + '-' + (now.getMonth() + 1) + '-' + now.getDate() + ' ' + now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
|
||||
var start = "2024-01-01 00:00:00";
|
||||
pttRequest('/group/' + gid + '/records', 'POST', { start: start, end: end, page: 1, limit: 5 }, []);
|
||||
if (window.android && window.android.pttGetGroupMemberInfo) {
|
||||
window.android.pttGetGroupMemberInfo(gid);
|
||||
showPttResult("已请求群组 [" + gid + "] 成员");
|
||||
}
|
||||
}
|
||||
|
||||
function pttGetRecordText() {
|
||||
var path = document.getElementById('ptt-record-path').value;
|
||||
if (!path) return alert("请输入录音路径");
|
||||
pttRequest('/record/text', 'GET', { path: path });
|
||||
}
|
||||
|
||||
function pttPlayRecord() {
|
||||
var path = document.getElementById('ptt-record-path').value;
|
||||
if (!path) return alert("请输入录音路径");
|
||||
var url = PTT_BASE + '/record/play?path=' + encodeURIComponent(path);
|
||||
window.open(url, '_blank');
|
||||
showPttResult("已尝试打开播放链接: " + url);
|
||||
}
|
||||
|
||||
function pttCreateTempGroup() {
|
||||
var name = document.getElementById('ptt-name').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/temp-group', 'POST', { gname: name, creatorid: 1001 }, [account]);
|
||||
}
|
||||
|
||||
function pttDeleteTempGroup() {
|
||||
function pttJumpGroup() {
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
pttRequest('/temp-group/' + gid, 'DELETE');
|
||||
if (window.android && window.android.pttJumpGroup) {
|
||||
window.android.pttJumpGroup(gid);
|
||||
showPttResult("尝试切换至群组: " + gid);
|
||||
}
|
||||
}
|
||||
|
||||
function pttGetTempMembers() {
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
pttRequest('/temp-group/' + gid + '/members', 'GET');
|
||||
function pttForceRelease() {
|
||||
if (window.android && window.android.pttForceRelease) {
|
||||
window.android.pttForceRelease();
|
||||
isDown = false;
|
||||
pttBtn.style.background = "#ef4444";
|
||||
pttBtn.innerText = "长按对讲 (PTT)";
|
||||
showPttResult("执行强制释放");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ public class CallBackResolution {
|
|||
CallBackUtil.callBackLoinState(errorNum, loginId);
|
||||
loginState = isLoginConflict(errorNum);
|
||||
CallBackUtil.callBackLoginConflict(loginState);
|
||||
if ("02".equals(errorNum)) {
|
||||
com.stand.standapp.MainActivity.onPttLoginSuccess();
|
||||
}
|
||||
break;
|
||||
|
||||
case "a2"://账号有视频功能
|
||||
|
|
@ -122,6 +125,8 @@ public class CallBackResolution {
|
|||
GroupMemberInfoDto infoDto = new GroupMemberInfoDto(memberId, tempTxt, status, memberNo, haveVideo);
|
||||
if (memberNo == 1) memberInfoDtos.clear();
|
||||
memberInfoDtos.add(infoDto);
|
||||
//输出群成员信息memberInfoDtos
|
||||
Timber.tag(TAG).d("at_OEM_AT_cb group member info: " + infoDto);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,79 @@ import com.stand.standapp.R
|
|||
import com.stand.standapp.utils.LogManager
|
||||
import com.stand.standapp.printer.PrinterManager
|
||||
import timber.log.Timber
|
||||
import com.example.kingway.ptt.SendAtUtil
|
||||
import org.json.JSONObject
|
||||
|
||||
class AndroidInterface(private val activity: MainActivity) {
|
||||
|
||||
private var isSpeaking = false
|
||||
|
||||
/**
|
||||
* PTT 登录
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttLogin(account: String, pwd: String, ip: String) {
|
||||
SendAtUtil.toLogin(account, pwd, ip, 1, 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* PTT 开始说话 (带状态保护)
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttSpeakPlay() {
|
||||
if (!isSpeaking) {
|
||||
isSpeaking = true
|
||||
SendAtUtil.speakPlay()
|
||||
Timber.tag("PTT").d("Speak Play")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PTT 停止说话
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttSpeakRelease() {
|
||||
if (isSpeaking) {
|
||||
isSpeaking = false
|
||||
SendAtUtil.speakRelease()
|
||||
Timber.tag("PTT").d("Speak Release")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制释放 PTT (用于异常恢复)
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttForceRelease() {
|
||||
isSpeaking = false
|
||||
SendAtUtil.speakRelease()
|
||||
Timber.tag("PTT").d("Force Release")
|
||||
}
|
||||
|
||||
/**
|
||||
* PTT 获取群组
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttGetGroupInfo() {
|
||||
SendAtUtil.getGroupInfo()
|
||||
}
|
||||
|
||||
/**
|
||||
* PTT 获取成员
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttGetGroupMemberInfo(gid: String) {
|
||||
SendAtUtil.getGroupMemberInfo(gid)
|
||||
}
|
||||
|
||||
/**
|
||||
* PTT 切换群组
|
||||
*/
|
||||
@JavascriptInterface
|
||||
fun pttJumpGroup(gid: String) {
|
||||
SendAtUtil.jumpGroup(gid)
|
||||
}
|
||||
|
||||
/**
|
||||
* JS 调用:获取打印机当前连接状态
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -131,7 +131,17 @@ class LoginActivity : AppCompatActivity() {
|
|||
|
||||
Toast.makeText(this@LoginActivity, "登录成功", Toast.LENGTH_SHORT).show()
|
||||
|
||||
try {
|
||||
// 确保 PTT 引擎已初始化
|
||||
com.example.kingway.ptt.MyApplication.init(this@LoginActivity)
|
||||
// 异步初始化 PTT 登录 (区分 Android 业务登录)
|
||||
com.example.kingway.ptt.SendAtUtil.toLogin("gzzdweb01", "123456", "61.243.1.123", 1, 1)
|
||||
} catch (e: Exception) {
|
||||
Timber.tag("Login").e(e, "检查更新失败")
|
||||
}
|
||||
|
||||
// 初始化打印机 (异步)
|
||||
|
||||
PrinterManager.init(this@LoginActivity)
|
||||
|
||||
startActivity(Intent(this@LoginActivity, MainActivity::class.java))
|
||||
|
|
|
|||
|
|
@ -20,7 +20,27 @@ import com.stand.standapp.UpdateManager
|
|||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private var instance: MainActivity? = null
|
||||
|
||||
@JvmStatic
|
||||
fun executeJs(script: String) {
|
||||
instance?.let { activity ->
|
||||
activity.runOnUiThread {
|
||||
activity.mAgentWeb?.jsAccessEntrace?.quickCallJs(script)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onPttLoginSuccess() {
|
||||
Timber.tag("MainActivity").d("onPttLoginSuccess called from Native")
|
||||
executeJs("onPttLoginSuccess")
|
||||
}
|
||||
}
|
||||
|
||||
private var mAgentWeb: AgentWeb? = null
|
||||
|
||||
private val mHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||
private val TIMEOUT_MS = 30000L
|
||||
|
||||
|
|
@ -30,8 +50,9 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
instance = this
|
||||
try {
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
val container = findViewById<LinearLayout>(R.id.container)
|
||||
|
|
|
|||
|
|
@ -39,16 +39,19 @@ object PrinterManager {
|
|||
if (sf != null) return
|
||||
mContext = context.applicationContext
|
||||
|
||||
// 全异步初始化,不阻塞主线程跳转
|
||||
executor.execute {
|
||||
// Serialport_Factory 构造函数内部会创建 Handler,必须在有 Looper 的线程(如主线程)调用
|
||||
mHandler.post {
|
||||
try {
|
||||
Timber.tag("Printer").i("开始异步初始化打印机组件...")
|
||||
Timber.tag("Printer").i("正在主线程初始化打印机组件...")
|
||||
sf = Serialport_Factory.getSerialport_Factory(mContext, mHandler)
|
||||
|
||||
// 启动自动重连定时任务
|
||||
executor.scheduleWithFixedDelay({
|
||||
checkAndConnect()
|
||||
}, 0, 10, TimeUnit.SECONDS)
|
||||
// 成功创建 factory 后,将后续耗时的连接任务丢给子线程
|
||||
executor.execute {
|
||||
// 启动自动重连定时任务
|
||||
executor.scheduleWithFixedDelay({
|
||||
checkAndConnect()
|
||||
}, 0, 10, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
Timber.tag("Printer").i("打印机组件初始化任务已分发")
|
||||
} catch (e: Exception) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue