修复问题
This commit is contained in:
parent
5715981b53
commit
722ece723f
|
|
@ -13,17 +13,23 @@
|
|||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
/startopencode.bat
|
||||
/.idea/
|
||||
/第三方/ptt/
|
||||
/第三方/安卓demo和说明/POC SOS扩展指令手册V1.0.docx
|
||||
/第三方/安卓demo和说明/POC 多用户单呼扩展.docx
|
||||
/第三方/安卓demo和说明/POC应用扩展指令手册V4.0(TTS功能只需看第7部分).pdf
|
||||
/第三方/安卓demo和说明/ptt.rar
|
||||
.idea/
|
||||
.idea/
|
||||
/第三方/安卓demo和说明/POC_Manual.txt
|
||||
/第三方/安卓demo和说明/POC_SingleCall.md
|
||||
/第三方/安卓demo和说明/POC_SOS.md
|
||||
/第三方/安卓demo和说明.rar
|
||||
/AGENTS.md
|
||||
/PTT_API_DOC.md
|
||||
/app/release/baselineProfiles/0/app-release.dm
|
||||
/app/release/baselineProfiles/1/app-release.dm
|
||||
/app/release/app-release.apk
|
||||
/app/release/output-metadata.json
|
||||
/startopencode.bat
|
||||
/.idea/AndroidProjectSystem.xml
|
||||
/.idea/compiler.xml
|
||||
/.idea/deploymentTargetSelector.xml
|
||||
/.idea/encodings.xml
|
||||
/.idea/gradle.xml
|
||||
/.idea/markdown.xml
|
||||
/.idea/misc.xml
|
||||
/.idea/runConfigurations.xml
|
||||
/.idea/vcs.xml
|
||||
/.kotlin/
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@ android {
|
|||
applicationId = "com.stand.standapp"
|
||||
minSdk = 24
|
||||
targetSdk = 34
|
||||
versionCode = 7
|
||||
versionName = "1.0.6"
|
||||
versionCode = 8
|
||||
versionName = "1.0.7"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
ndk {
|
||||
abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||
abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,6 +49,7 @@ dependencies {
|
|||
implementation("com.github.Justson:Downloader:v5.0.4-androidx")
|
||||
implementation("com.google.android.material:material:1.12.0")
|
||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
implementation("com.github.getActivity:XXPermissions:18.2")
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/Theme.StandAPP">
|
||||
|
||||
<activity
|
||||
android:name=".SplashActivity"
|
||||
android:exported="true"
|
||||
|
|
@ -41,8 +43,15 @@
|
|||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.StandAPP" />
|
||||
|
||||
<activity
|
||||
android:name="com.example.kingway.ptt.pttActivity"
|
||||
android:exported="false"
|
||||
android:label="PTT示例"
|
||||
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.fileprovider"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
.btn:active { background: #2563EB; }
|
||||
.btn-green { background: #10B981; }
|
||||
.btn-orange { background: #F59E0B; }
|
||||
.btn-red { background: #ef4444; }
|
||||
.status-tag { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; margin-bottom: 10px; }
|
||||
.status-online { background: #D1FAE5; color: #065F46; }
|
||||
.status-offline { background: #FEE2E2; color: #991B1B; }
|
||||
|
|
@ -171,70 +172,9 @@
|
|||
<div style="text-align: center; font-weight: bold; font-size: 18px;">测试签收单</div>
|
||||
<hr>
|
||||
<p>单号: SN20260315888</p>
|
||||
<div id="dynamic-items">
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
<p>物料: 工业级调度终端 x 5</p>
|
||||
</div>
|
||||
<p>时间: <span id="current-time"></span></p>
|
||||
<div style="height: 40px; border: 1px solid #eee; text-align: center; line-height: 40px; color: #ccc;">
|
||||
[ 此处为二维码/条码预览 ]
|
||||
|
|
@ -260,6 +200,8 @@
|
|||
</div>
|
||||
|
||||
<script>
|
||||
// 兼容性优化:全部使用 ES5 语法
|
||||
|
||||
// 初始化界面
|
||||
document.getElementById('current-time').innerText = new Date().toLocaleString();
|
||||
|
||||
|
|
@ -291,7 +233,7 @@
|
|||
// 调用原始数据打印
|
||||
function callRawPrint() {
|
||||
if (window.android && window.android.printRawData) {
|
||||
const data = {
|
||||
var data = {
|
||||
type: "receipt",
|
||||
title: "测试签收单",
|
||||
sn: "SN20260315888",
|
||||
|
|
@ -323,15 +265,13 @@
|
|||
|
||||
function callAbout() {
|
||||
if (window.android && window.android.getAboutInfo) {
|
||||
const infoStr = window.android.getAboutInfo();
|
||||
var infoStr = window.android.getAboutInfo();
|
||||
try {
|
||||
const info = JSON.parse(infoStr);
|
||||
const html = `
|
||||
<strong>应用名称:</strong> ${info.appName}<br>
|
||||
<strong>版本号:</strong> <span style="color: #3b82f6;">${info.versionName}</span><br>
|
||||
<strong>设备型号:</strong> ${info.model}<br>
|
||||
<strong>系统版本:</strong> Android ${info.osVersion}
|
||||
`;
|
||||
var info = JSON.parse(infoStr);
|
||||
var html = '<strong>应用名称:</strong> ' + info.appName + '<br>' +
|
||||
'<strong>版本号:</strong> <span style="color: #3b82f6;">' + info.versionName + '</span><br>' +
|
||||
'<strong>设备型号:</strong> ' + info.model + '<br>' +
|
||||
'<strong>系统版本:</strong> Android ' + info.osVersion;
|
||||
document.getElementById('aboutBody').innerHTML = html;
|
||||
document.getElementById('aboutModal').style.display = 'flex';
|
||||
} catch (e) {
|
||||
|
|
@ -349,9 +289,9 @@
|
|||
|
||||
// 安卓回调函数:退出确认时执行
|
||||
function addExitText() {
|
||||
const tag = document.getElementById('inject-status');
|
||||
var tag = document.getElementById('inject-status');
|
||||
if (tag) {
|
||||
const notice = document.createElement('div');
|
||||
var notice = document.createElement('div');
|
||||
notice.className = 'exit-notice';
|
||||
notice.innerText = "⚠️ 检测到退出尝试 (" + new Date().toLocaleTimeString() + ")";
|
||||
tag.parentNode.insertBefore(notice, tag.nextSibling);
|
||||
|
|
@ -369,35 +309,34 @@
|
|||
};
|
||||
|
||||
// === WebSocket 逻辑 ===
|
||||
let ws = null;
|
||||
let clientId = "WEB_" + Math.random().toString(36).substr(2, 9);
|
||||
let heartbeatTimer = null;
|
||||
let reconnectTimer = null;
|
||||
let isManualClose = false; // 新增:手动断开标记
|
||||
const logList = document.getElementById('log-list');
|
||||
var ws = null;
|
||||
var clientId = "WEB_" + Math.random().toString(36).substr(2, 9);
|
||||
var heartbeatTimer = null;
|
||||
var reconnectTimer = null;
|
||||
var isManualClose = false;
|
||||
var logList = document.getElementById('log-list');
|
||||
|
||||
document.getElementById('ws-client-id').innerText = clientId;
|
||||
|
||||
function addLog(msg, save = true) {
|
||||
const time = new Date().toLocaleTimeString();
|
||||
const item = document.createElement('div');
|
||||
function addLog(msg, save) {
|
||||
if (save === undefined) save = true;
|
||||
var time = new Date().toLocaleTimeString();
|
||||
var item = document.createElement('div');
|
||||
item.className = 'log-item';
|
||||
item.innerHTML = `<span class="log-time">[${time}]</span><span class="log-msg">${msg}</span>`;
|
||||
item.innerHTML = '<span class="log-time">[' + time + ']</span><span class="log-msg">' + msg + '</span>';
|
||||
logList.insertBefore(item, logList.firstChild);
|
||||
|
||||
// 限制 DOM 节点数量,避免页面卡顿(虽然存储 5000 条,但显示建议限制在 200 条)
|
||||
if (logList.childNodes.length > 200) {
|
||||
logList.removeChild(logList.lastChild);
|
||||
}
|
||||
|
||||
// 调用安卓接口持久化存储
|
||||
if (save && window.android && window.android.saveMessage) {
|
||||
window.android.saveMessage(`${time}: ${msg}`);
|
||||
window.android.saveMessage(time + ": " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
function updateWSStatus(connected) {
|
||||
const tag = document.getElementById('ws-status-tag');
|
||||
var tag = document.getElementById('ws-status-tag');
|
||||
if (connected) {
|
||||
tag.innerText = "已连接";
|
||||
tag.className = "ws-status ws-connected";
|
||||
|
|
@ -409,49 +348,46 @@
|
|||
|
||||
function connectWS() {
|
||||
if (ws) {
|
||||
ws.onclose = null; // 清除之前的重连逻辑
|
||||
ws.onclose = null;
|
||||
ws.close();
|
||||
}
|
||||
|
||||
const rawUrl = document.getElementById('ws-url-input').value;
|
||||
const finalUrl = rawUrl.replace("{clientId}", clientId);
|
||||
var rawUrl = document.getElementById('ws-url-input').value;
|
||||
var finalUrl = rawUrl.replace("{clientId}", clientId);
|
||||
|
||||
addLog("正在尝试连接: " + finalUrl, false);
|
||||
isManualClose = false; // 启动连接时重置标记
|
||||
isManualClose = false;
|
||||
|
||||
try {
|
||||
ws = new WebSocket(finalUrl);
|
||||
|
||||
ws.onopen = () => {
|
||||
ws.onopen = function() {
|
||||
updateWSStatus(true);
|
||||
addLog("连接成功", false);
|
||||
startHeartbeat();
|
||||
clearTimeout(reconnectTimer);
|
||||
};
|
||||
|
||||
ws.onmessage = (evt) => {
|
||||
ws.onmessage = function(evt) {
|
||||
addLog("收到消息: " + evt.data);
|
||||
// 如果收到打印指令,可以自动触发打印
|
||||
if (evt.data.includes("print")) {
|
||||
if (evt.data.indexOf("print") !== -1) {
|
||||
callRawPrint();
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
ws.onclose = function() {
|
||||
updateWSStatus(false);
|
||||
addLog("连接关闭", false);
|
||||
stopHeartbeat();
|
||||
|
||||
// 只有非手动断开(如网络异常)时才自动重连
|
||||
if (!isManualClose) {
|
||||
addLog("异常断开,5秒后自动重连...", false);
|
||||
reconnectTimer = setTimeout(connectWS, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (err) => {
|
||||
ws.onerror = function(err) {
|
||||
addLog("连接错误或被拒绝", false);
|
||||
console.error(err);
|
||||
};
|
||||
} catch (e) {
|
||||
addLog("连接失败: " + e.message, false);
|
||||
|
|
@ -460,7 +396,7 @@
|
|||
|
||||
function toggleWS() {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
isManualClose = true; // 标记为手动断开
|
||||
isManualClose = true;
|
||||
clearTimeout(reconnectTimer);
|
||||
reconnectTimer = null;
|
||||
ws.close();
|
||||
|
|
@ -471,11 +407,11 @@
|
|||
|
||||
function startHeartbeat() {
|
||||
stopHeartbeat();
|
||||
heartbeatTimer = setInterval(() => {
|
||||
heartbeatTimer = setInterval(function() {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send("ping");
|
||||
}
|
||||
}, 30000); // 30秒心跳
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
function stopHeartbeat() {
|
||||
|
|
@ -492,30 +428,31 @@
|
|||
}
|
||||
|
||||
function manualAddRecord() {
|
||||
const text = prompt("请输入要保存的消息内容:", "手动测试记录");
|
||||
var text = prompt("请输入要保存的消息内容:", "手动测试记录");
|
||||
if (text) {
|
||||
addLog("用户新增: " + text);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化加载历史记录
|
||||
function loadHistory() {
|
||||
if (window.android && window.android.getMessages) {
|
||||
const history = window.android.getMessages();
|
||||
logList.innerHTML = ""; // 先清空当前显示
|
||||
var history = window.android.getMessages();
|
||||
logList.innerHTML = "";
|
||||
if (history) {
|
||||
const msgs = history.split("|---|");
|
||||
addLog(`系统: 已从安卓端同步 ${msgs.length} 条记录`, false);
|
||||
msgs.forEach((msg, index) => {
|
||||
if (!msg || index > 500) return; // 界面显示限制前500条,防止卡死
|
||||
const item = document.createElement('div');
|
||||
var msgs = history.split("|---|");
|
||||
addLog("系统: 已从安卓端同步 " + msgs.length + " 条记录", false);
|
||||
for (var i = 0; i < msgs.length; i++) {
|
||||
var msg = msgs[i];
|
||||
if (!msg || i > 500) continue;
|
||||
var item = document.createElement('div');
|
||||
item.className = 'log-item';
|
||||
const parts = msg.split(": ");
|
||||
const time = parts[0];
|
||||
const content = parts.slice(1).join(": ");
|
||||
item.innerHTML = `<span class="log-time">[${time}]</span><span class="log-msg">${content}</span>`;
|
||||
var separatorIndex = msg.indexOf(": ");
|
||||
if (separatorIndex === -1) continue;
|
||||
var time = msg.substring(0, separatorIndex);
|
||||
var content = msg.substring(separatorIndex + 2);
|
||||
item.innerHTML = '<span class="log-time">[' + time + ']</span><span class="log-msg">' + content + '</span>';
|
||||
logList.appendChild(item);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
addLog("系统: 终端暂无保存的记录", false);
|
||||
}
|
||||
|
|
@ -527,24 +464,30 @@
|
|||
setTimeout(loadHistory, 500);
|
||||
|
||||
// === PTT 接口测试逻辑 ===
|
||||
const PTT_BASE = "http://127.0.0.1:8080/api/external/ptt";
|
||||
var PTT_BASE = "http://127.0.0.1:8080/api/external/ptt";
|
||||
|
||||
function showPttResult(msg) {
|
||||
const container = document.getElementById('ptt-result');
|
||||
const time = new Date().toLocaleTimeString();
|
||||
const item = document.createElement('div');
|
||||
var container = document.getElementById('ptt-result');
|
||||
var time = new Date().toLocaleTimeString();
|
||||
var item = document.createElement('div');
|
||||
item.className = 'log-item';
|
||||
let displayMsg = typeof msg === 'string' ? msg : JSON.stringify(msg, null, 2);
|
||||
item.innerHTML = `<span class="log-time">[${time}]</span><span class="log-msg">${displayMsg}</span>`;
|
||||
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);
|
||||
}
|
||||
|
||||
// 移除 async/await,使用 Promise
|
||||
function pttRequest(path, method, query, body) {
|
||||
if (!method) method = 'GET';
|
||||
if (!query) query = {};
|
||||
if (method === undefined) method = 'GET';
|
||||
if (query === undefined) query = {};
|
||||
if (body === undefined) body = null;
|
||||
|
||||
var url = PTT_BASE + path;
|
||||
var qParams = new URLSearchParams(query).toString();
|
||||
if (qParams) url += "?" + qParams;
|
||||
var qParams = [];
|
||||
for (var key in query) {
|
||||
qParams.push(encodeURIComponent(key) + "=" + encodeURIComponent(query[key]));
|
||||
}
|
||||
if (qParams.length > 0) url += "?" + qParams.join("&");
|
||||
|
||||
var options = {
|
||||
method: method,
|
||||
|
|
@ -552,43 +495,42 @@
|
|||
};
|
||||
if (body) options.body = JSON.stringify(body);
|
||||
|
||||
try {
|
||||
fetch(url, options).then(function(response) {
|
||||
fetch(url, options)
|
||||
.then(function(response) {
|
||||
if (path === '/record/play') {
|
||||
showPttResult("流式播放接口已调用");
|
||||
showPttResult("流式播放接口已调用,请查看网络请求或直接点击预览链接");
|
||||
return;
|
||||
}
|
||||
return response.json();
|
||||
}).then(function(data) {
|
||||
})
|
||||
.then(function(data) {
|
||||
if (data) showPttResult(data);
|
||||
}).catch(function(e) {
|
||||
})
|
||||
.catch(function(e) {
|
||||
showPttResult("请求失败: " + e.message);
|
||||
});
|
||||
} catch (e) {
|
||||
showPttResult("fetch异常: " + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function pttCreateGroup() {
|
||||
const name = document.getElementById('ptt-name').value;
|
||||
const account = document.getElementById('ptt-account').value;
|
||||
var name = document.getElementById('ptt-name').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/group', 'POST', { gname: name }, [account]);
|
||||
}
|
||||
|
||||
function pttDeleteGroup() {
|
||||
const gid = document.getElementById('ptt-gid').value;
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
pttRequest('/group/' + gid, 'DELETE');
|
||||
}
|
||||
|
||||
function pttAddMembers() {
|
||||
const gid = document.getElementById('ptt-gid').value;
|
||||
const account = document.getElementById('ptt-account').value;
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/group/' + gid + '/members', 'POST', {}, [account]);
|
||||
}
|
||||
|
||||
function pttKickMembers() {
|
||||
const gid = document.getElementById('ptt-gid').value;
|
||||
const account = document.getElementById('ptt-account').value;
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
var account = document.getElementById('ptt-account').value;
|
||||
pttRequest('/group/' + gid + '/members', 'DELETE', {}, [account]);
|
||||
}
|
||||
|
||||
|
|
@ -601,39 +543,40 @@
|
|||
}
|
||||
|
||||
function pttGetRecords() {
|
||||
const gid = document.getElementById('ptt-gid').value;
|
||||
const end = new Date().toISOString().replace('T', ' ').substring(0, 19);
|
||||
const start = new Date(Date.now() - 24 * 3600000).toISOString().replace('T', ' ').substring(0, 19);
|
||||
pttRequest('/group/' + gid + '/records', 'POST', { start, end, page: 1, limit: 5 }, []);
|
||||
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 }, []);
|
||||
}
|
||||
|
||||
function pttGetRecordText() {
|
||||
const path = document.getElementById('ptt-record-path').value;
|
||||
var path = document.getElementById('ptt-record-path').value;
|
||||
if (!path) return alert("请输入录音路径");
|
||||
pttRequest('/record/text', 'GET', { path: path });
|
||||
}
|
||||
|
||||
function pttPlayRecord() {
|
||||
const path = document.getElementById('ptt-record-path').value;
|
||||
var path = document.getElementById('ptt-record-path').value;
|
||||
if (!path) return alert("请输入录音路径");
|
||||
const url = PTT_BASE + '/record/play?path=' + encodeURIComponent(path);
|
||||
var url = PTT_BASE + '/record/play?path=' + encodeURIComponent(path);
|
||||
window.open(url, '_blank');
|
||||
showPttResult("已尝试打开播放链接: " + url);
|
||||
}
|
||||
|
||||
function pttCreateTempGroup() {
|
||||
const name = document.getElementById('ptt-name').value;
|
||||
const account = document.getElementById('ptt-account').value;
|
||||
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() {
|
||||
const gid = document.getElementById('ptt-gid').value;
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
pttRequest('/temp-group/' + gid, 'DELETE');
|
||||
}
|
||||
|
||||
function pttGetTempMembers() {
|
||||
const gid = document.getElementById('ptt-gid').value;
|
||||
var gid = document.getElementById('ptt-gid').value;
|
||||
pttRequest('/temp-group/' + gid + '/members', 'GET');
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.util.Log;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public class AudioRecordManager {
|
||||
private final String TAG = "AudioRecordManager";
|
||||
private static final int FREQUENCY = 8000;
|
||||
private AudioRecord mRecorder;
|
||||
private Thread recordThread;
|
||||
private boolean isStart = false;
|
||||
private static AudioRecordManager mInstance;
|
||||
private int bufferSize = 320;
|
||||
|
||||
|
||||
private AudioRecordManager() {
|
||||
initAudio();
|
||||
}
|
||||
|
||||
public void initAudio() {
|
||||
int audioSource = MediaRecorder.AudioSource.MIC;
|
||||
mRecorder = new AudioRecord(audioSource,
|
||||
FREQUENCY,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSize * 2);
|
||||
}
|
||||
|
||||
public void setRecording(){
|
||||
if (mRecorder == null) initAudio();
|
||||
if(mRecorder.getRecordingState() != 3) mRecorder.startRecording();
|
||||
}
|
||||
|
||||
public void stopAudio(){
|
||||
if (mRecorder != null){
|
||||
mRecorder.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public int getState(){
|
||||
if (mRecorder == null) initAudio();
|
||||
return mRecorder.getRecordingState();
|
||||
}
|
||||
|
||||
public void destroyAudio(){
|
||||
if (mRecorder != null){
|
||||
mRecorder.stop();
|
||||
mRecorder.release();
|
||||
mRecorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取单例引用
|
||||
*/
|
||||
public static AudioRecordManager getInstance() {
|
||||
if (mInstance == null) {
|
||||
synchronized (AudioRecordManager.class) {
|
||||
if (mInstance == null) {
|
||||
mInstance = new AudioRecordManager();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁线程方法
|
||||
*/
|
||||
private void destroyThread() {
|
||||
try {
|
||||
if (null != recordThread && Thread.State.RUNNABLE == recordThread.getState()) {
|
||||
try {
|
||||
recordThread.interrupt();
|
||||
} catch (Exception e) {
|
||||
recordThread = null;
|
||||
}
|
||||
}
|
||||
recordThread = null;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
recordThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动录音线程
|
||||
*/
|
||||
private void startThread() {
|
||||
destroyThread();
|
||||
isStart = true;
|
||||
if (mRecorder == null) {
|
||||
initAudio();
|
||||
}
|
||||
if (recordThread == null) {
|
||||
recordThread = new Thread(recordRunnable);
|
||||
recordThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 录音线程
|
||||
*/
|
||||
private Runnable recordRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
|
||||
int bytesRecord;
|
||||
byte[] tempBuffer = new byte[bufferSize];
|
||||
if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||
stopRecord();
|
||||
return;
|
||||
}
|
||||
mRecorder.startRecording();
|
||||
while (isStart) {
|
||||
if (null != mRecorder) {
|
||||
bytesRecord = mRecorder.read(tempBuffer, 0, bufferSize);
|
||||
if (bytesRecord == AudioRecord.ERROR_INVALID_OPERATION
|
||||
|| bytesRecord == AudioRecord.ERROR_BAD_VALUE) {
|
||||
continue;
|
||||
}
|
||||
if (bytesRecord != 0 && bytesRecord != -1) {
|
||||
Log.i(TAG, "tempBuffer->" + Arrays.toString(tempBuffer));
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendRecordCmd(tempBuffer);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* 启动录音
|
||||
*/
|
||||
public void startRecord() {
|
||||
try {
|
||||
stopRecord();
|
||||
startThread();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止录音
|
||||
*/
|
||||
public void stopRecord() {
|
||||
try {
|
||||
isStart = false;
|
||||
destroyThread();
|
||||
if (mRecorder != null) {
|
||||
try {
|
||||
if (mRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
|
||||
mRecorder.stop();
|
||||
}
|
||||
mRecorder.release();
|
||||
mRecorder = null;
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,470 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import android.util.Log;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**解析接收到的AT指令*/
|
||||
public class CallBackResolution {
|
||||
private static String TAG = "CallBackResolution";
|
||||
private static String indexGroupId = "";//用户所在群组Id
|
||||
private static String indexGroupName = "";//用户所在群组名称
|
||||
private static List<GroupMemberInfoDto> memberInfoDtos = new ArrayList<>();
|
||||
private static int speckType = 1;
|
||||
private static String talkId = "";
|
||||
private static String talkName = "";
|
||||
private static ArrayList<GroupInfoDto> groupInfos = new ArrayList<>();
|
||||
private static String oldLoginSate = "00";
|
||||
private static int logNum = 0;
|
||||
private static long offlineTime = 0;
|
||||
private static boolean loginState = false;
|
||||
|
||||
public static void at_OEM_AT_cb(List<String> lisHex){
|
||||
if (lisHex.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String ngx = "\\u";
|
||||
try {
|
||||
Log.i(TAG, "lisHex-->" + lisHex.toString());
|
||||
String firstHex = lisHex.get(0);
|
||||
String errorNum = lisHex.get(1);
|
||||
Log.d(TAG, "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);
|
||||
CallBackUtil.callBackLoinState(errorNum, loginId);
|
||||
loginState = isLoginConflict(errorNum);
|
||||
CallBackUtil.callBackLoginConflict(loginState);
|
||||
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"://发起讲话返回状态
|
||||
Log.d(TAG, "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...");
|
||||
CallBackUtil.callBackSpeakReleaseSuccess(errorNum.equals("00")? true : false);
|
||||
break;
|
||||
|
||||
case "80"://返回群组信息
|
||||
Log.d(TAG, "at_OEM_AT_cb get group info: " + lisHex);
|
||||
String strGroup = "";
|
||||
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 += ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numB - 1);
|
||||
} else {
|
||||
strGroup += ngx + lisHex.get(i + numB + 1) + "" + lisHex.get(i + numB);
|
||||
numB = numB + 1;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
String tempTxt = "";
|
||||
tempTxt = unicodeToString(strGroup);
|
||||
Log.i(TAG, " 组名称 " + tempTxt);
|
||||
CallBackUtil.callBackGroupInfo(groupId, tempTxt, groupNo, groupCount);
|
||||
Log.d(TAG, "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);
|
||||
}
|
||||
break;
|
||||
|
||||
case "81"://返回群成员信息
|
||||
String groupMemberName = "";
|
||||
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 += ngx + lisHex.get(i + numB) + "" + lisHex.get(i + numB - 1);
|
||||
} else {
|
||||
groupMemberName += ngx + lisHex.get(i + numB + 1) + "" + lisHex.get(i + numB);
|
||||
numB = numB + 1;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
String tempTxt = unicodeToString(groupMemberName);
|
||||
GroupMemberInfoDto infoDto = new GroupMemberInfoDto(memberId, tempTxt, status, memberNo, haveVideo);
|
||||
if (memberNo == 1) memberInfoDtos.clear();
|
||||
memberInfoDtos.add(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) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
String tempTxt = "";
|
||||
for (int i = 0; i < stringsSpker.size(); i++) {
|
||||
tempTxt += unicode(stringsSpker.get(i));
|
||||
}
|
||||
talkName = tempTxt;
|
||||
}
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
String tempTxt = "";
|
||||
for (int i = 0; i < stringsTalk.size(); i++) {
|
||||
tempTxt += unicode(stringsTalk.get(i));
|
||||
}
|
||||
Log.i(TAG, " 临时讲话 信息通知的指令 " + tempTxt);
|
||||
CallBackUtil.callBackTempCallNotify(tempTxt);
|
||||
}
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
String userGroupName = "";
|
||||
for (int i = 0; i < stringsJumpGroup.size(); i++) {
|
||||
userGroupName += unicode(stringsJumpGroup.get(i));
|
||||
}
|
||||
Log.i(TAG,"进入群组 " + 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) {
|
||||
Log.e(TAG, "获取定位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);
|
||||
Log.i(TAG, "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){
|
||||
Log.e(TAG,"at_OEM_AT_cb Exception " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isLoginConflict(String state) {
|
||||
if (oldLoginSate.equals("00") && state.equals("01")) {
|
||||
logNum++;
|
||||
oldLoginSate = state;
|
||||
} else if (oldLoginSate.equals("01") && state.equals("00")) {
|
||||
logNum++;
|
||||
oldLoginSate = state;
|
||||
}
|
||||
if (1 == logNum) {
|
||||
offlineTime = System.currentTimeMillis();
|
||||
}
|
||||
if (logNum > 4) {
|
||||
if (System.currentTimeMillis() - offlineTime >= 60 * 1000) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void at_Play_TTS_cb(String sData){
|
||||
try {
|
||||
String[] txt = sData.split("\"");
|
||||
String data = txt[1];
|
||||
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) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
String tempTxt = "";
|
||||
for (int k = 0; k < strings.size(); k++) {
|
||||
tempTxt += unicode(strings.get(k));
|
||||
}
|
||||
if (tempTxt.contains("已登录")) {
|
||||
String showName = tempTxt.substring(3, tempTxt.length());
|
||||
CallBackUtil.callBackLogin(true, showName);
|
||||
java.util.concurrent.ScheduledExecutorService executor = java.util.concurrent.Executors.newScheduledThreadPool(1);
|
||||
Runnable task = () -> {
|
||||
SendAtUtil.sendUDP();
|
||||
SendAtUtil.sendTCP();
|
||||
};
|
||||
executor.scheduleAtFixedRate(task, 0, 40, java.util.concurrent.TimeUnit.SECONDS);
|
||||
} else if (tempTxt.contains("账号或密码错误")) {
|
||||
CallBackUtil.callBackLogin(false, "");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String unicode(String str) {
|
||||
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
|
||||
java.util.regex.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);
|
||||
String s1 = s.substring(2, 4) + "00";
|
||||
String s2 = s.substring(4);
|
||||
int n = Integer.valueOf(s1, 16) + Integer.valueOf(s2, 16);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**指令返回类
|
||||
* 说明:操作UI请在主线程进行
|
||||
* */
|
||||
public class CallBackUtil {
|
||||
private static String TAG = "TYT_CallBackUtil";
|
||||
|
||||
|
||||
/**
|
||||
* 返回登录
|
||||
* loginState 是否登录成功
|
||||
* name 用户名
|
||||
*/
|
||||
public static void callBackLogin(boolean loginState, String name) {
|
||||
Log.i(TAG, "callBackLogin loginState " + loginState + " name " + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录状态通知
|
||||
* state
|
||||
* 0 : 离线
|
||||
* 1 : 登陆中
|
||||
* 2 : 登陆成功
|
||||
* 3 : 注销中
|
||||
* 说明:第一次登录 通知状态 先01再02,
|
||||
* 离线再上线 通知状态 先00再01
|
||||
* <p>
|
||||
* loginId 用户ID,登录成功id有效
|
||||
*/
|
||||
public static void callBackLoinState(String state, String loginId) {
|
||||
Log.d(TAG, "callBackLoinState state: " + state + ", loginId: " + loginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断账号登录是否冲突
|
||||
* true:登录冲突
|
||||
* false:登录不冲突
|
||||
* */
|
||||
public static void callBackLoginConflict(boolean loginConflict) {
|
||||
Log.d(TAG, "callBackLoginConflict: " + loginConflict);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录账号有视频功能通知
|
||||
*/
|
||||
public static boolean callBackHaveVideo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 查询群组返回
|
||||
* state true-成功, false-失败
|
||||
*/
|
||||
public static void callBackOnGroupSuccess(boolean state) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询群组成员返回
|
||||
* state true-成功, false-失败
|
||||
*/
|
||||
public static void callBackOnMemberSuccess(boolean state, List<GroupMemberInfoDto> dtos) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用发起讲话返回
|
||||
* state true-成功, false-失败
|
||||
*/
|
||||
public static void callBackSpeakPlaySuccess(boolean state) {
|
||||
Log.d(TAG, "callBackSpeakPlaySuccess state: " + state);
|
||||
if (state) AudioRecordManager.getInstance().startRecord();
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用结束讲话返回
|
||||
* state true-成功, false-失败
|
||||
*/
|
||||
public static void callBackSpeakReleaseSuccess(boolean state) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回群组信息
|
||||
* groupId 群组ID
|
||||
* groupName 群组名称
|
||||
* groupNo 表示此组为第几个组
|
||||
* groupCount 表示此组用户成员数
|
||||
*/
|
||||
public static void callBackGroupInfo(String groupId, String groupName, int groupNo, int groupCount) {}
|
||||
|
||||
/**
|
||||
* 返回群组集合信息
|
||||
* GroupInfoDto 群组信息
|
||||
* */
|
||||
public static void callBackGroupInfo (ArrayList < GroupInfoDto > groupInfoDtos) {
|
||||
for (GroupInfoDto groupInfoDto : groupInfoDtos) {
|
||||
Log.d(TAG, "callBackGroupInfo: " + groupInfoDto.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 讲话用户信息通知
|
||||
* speckType 讲话状态: 0 : 表示自己无法讲话; 1 : 表示自己可以中断讲话人的讲话,可以进行强插讲话
|
||||
* talkId 讲话用户ID
|
||||
* talkName 讲话用户名字
|
||||
* */
|
||||
public static void callBackNotifySpker ( int speckType, String groupId, String
|
||||
groupName, String talkId, String talkName){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 对方讲话通知
|
||||
* playStatus 1表示开始讲话, 0 表示结束讲话
|
||||
* speckType 自己讲话状态: 0 : 表示自己无法讲话; 1 : 表示自己可以中断讲话人的讲话,可以进行强插讲话
|
||||
* groupId 所在群组Id
|
||||
* groupName 所在群组名称
|
||||
* talkId 讲话用户Id
|
||||
* talkName 讲话用户名称
|
||||
* */
|
||||
public static void callBackNotifyPlayStatus ( int playStatus, int speckType, String
|
||||
groupId, String groupName, String talkId, String talkName){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*临时呼叫信息通知
|
||||
* tempCallNotify:
|
||||
* 临时呼叫xxx:表示被单呼,呼叫名:xxx
|
||||
* 退出临时呼叫:表示退出单呼
|
||||
* 呼叫成功:表示单呼成功
|
||||
* 呼叫失败:表示单呼失败
|
||||
* */
|
||||
public static void callBackTempCallNotify (String tempCallNotify){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户进入群组信息通知
|
||||
* userGroupId 用户所在的群组ID
|
||||
* userGroupName 用户所在的群组名称
|
||||
* */
|
||||
public static void callBackNotifyUpdateGroup (String userGroupId, String userGroupName){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户定位信息通知
|
||||
* status 0:获取成功, 1:获取失败
|
||||
* userId 用户ID
|
||||
* latitude 纬度
|
||||
* longitude 经度
|
||||
* time 时间
|
||||
*/
|
||||
public static void callBackNotifyLocationStatus ( int status, String userId,double latitude,
|
||||
double longitude, String time){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户上报定位信息通知
|
||||
* status true:成功, false:失败
|
||||
* */
|
||||
public static void callBackNotifyUploadLocation ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 空中写账号信息通知
|
||||
* codeInfo ip=106.15.8.60;id=Android3;gps=1;inv=1
|
||||
* ip=106.15.8.60 用户ip:106.15.8.60
|
||||
* id=Android3 用户账号:Android3
|
||||
* gps=1:1勾选定位,0不勾选定位
|
||||
* inv=1:1勾选单呼,0不勾选单呼
|
||||
* */
|
||||
public static void callBackNotifyCodeInfo (String codeInfo){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 空中写密码信息通知
|
||||
* pwdInfo age=123456;pwd=111111
|
||||
* age=123456 经销商密码:123456
|
||||
* pwd=111111 账号密码:111111
|
||||
* */
|
||||
public static void callBackNotifyPwdInfo (String pwdInfo){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入写码模式通知
|
||||
* status true:成功,false:失败
|
||||
* */
|
||||
public static void callBackNotifyCodeOpenStatus ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出写码模式通知
|
||||
* status true:成功,false:失败
|
||||
* */
|
||||
public static void callBackNotifyCodeExiteStatus ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传经销商密码通知
|
||||
* status true:成功,false:失败
|
||||
* */
|
||||
public static void callBackNotifyUploadAgentPwd ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 上报本机告警信号通知
|
||||
* status true:成功,false:失败
|
||||
* */
|
||||
public static void callBackRequestReportAlarm ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止本机告警信号通知
|
||||
* status true:成功,false:失败
|
||||
* */
|
||||
public static void callBackRequestStopAlarm ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到群成员警告通知
|
||||
* memberName 群成员名称
|
||||
* latitude 纬度
|
||||
* longitude 经度
|
||||
* */
|
||||
public static void callBackEnotifyAlarm (String memberName,double latitude, double longitude){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到群成员停止警告通知
|
||||
* */
|
||||
public static void callBackEnotifyStopAlarm () {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传NFC信息通知
|
||||
* status true:成功,false:失败
|
||||
* */
|
||||
public static void callBackRequestNfc ( boolean status){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* desc : 组
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
public class GroupInfoDto {
|
||||
|
||||
|
||||
private int groupNo; //表示此组为第一个组
|
||||
private int groupCount; //表示此组有 3 个用户成员
|
||||
private String groupId;
|
||||
private String groupName;
|
||||
|
||||
public GroupInfoDto() {
|
||||
|
||||
}
|
||||
|
||||
public GroupInfoDto(String groupId, String groupName, int groupNo, int groupCount) {
|
||||
this.groupId = groupId;
|
||||
this.groupName = groupName;
|
||||
this.groupNo = groupNo;
|
||||
this.groupCount = groupCount;
|
||||
}
|
||||
|
||||
public int getGroupNo() {
|
||||
return groupNo;
|
||||
}
|
||||
|
||||
public void setGroupNo(int groupNo) {
|
||||
this.groupNo = groupNo;
|
||||
}
|
||||
|
||||
public int getGroupCount() {
|
||||
return groupCount;
|
||||
}
|
||||
|
||||
public void setGroupCount(int groupCount) {
|
||||
this.groupCount = groupCount;
|
||||
}
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId == null ? "" : groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return groupName == null ? "" : groupName;
|
||||
}
|
||||
|
||||
public void setGroupName(String groupName) {
|
||||
this.groupName = groupName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GroupInfoDto{" +
|
||||
"groupNo=" + groupNo +
|
||||
", groupCount=" + groupCount +
|
||||
", groupId='" + groupId + '\'' +
|
||||
", groupName='" + groupName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* desc : 组 成员
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
public class GroupMemberInfoDto implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private int memberNo; //表示此成员为第几个用户
|
||||
private String memberId;
|
||||
private String gmemberName;
|
||||
private String status;
|
||||
private boolean haveVideo;//此成员是否有视频功能
|
||||
|
||||
public GroupMemberInfoDto(){}
|
||||
|
||||
public GroupMemberInfoDto(String memberId, String gmemberName, String status, int memberNo, boolean haveVideo) {
|
||||
this.memberId = memberId;
|
||||
this.gmemberName = gmemberName;
|
||||
this.memberNo = memberNo;
|
||||
this.status = status;
|
||||
this.haveVideo = haveVideo;
|
||||
}
|
||||
|
||||
public int getMemberNo() {
|
||||
return memberNo;
|
||||
}
|
||||
|
||||
public void setMemberNo(int memberNo) {
|
||||
this.memberNo = memberNo;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status == null ? "" : status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMemberId() {
|
||||
return memberId == null ? "" : memberId;
|
||||
}
|
||||
|
||||
public void setMemberId(String memberId) {
|
||||
this.memberId = memberId;
|
||||
}
|
||||
|
||||
public String getGmemberName() {
|
||||
return gmemberName == null ? "" : gmemberName;
|
||||
}
|
||||
|
||||
public void setGmemberName(String gmemberName) {
|
||||
this.gmemberName = gmemberName;
|
||||
}
|
||||
|
||||
public boolean isHaveVideo() {
|
||||
return haveVideo;
|
||||
}
|
||||
|
||||
public void setHaveVideo(boolean haveVideo) {
|
||||
this.haveVideo = haveVideo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GroupMemberInfoDto{" +
|
||||
"memberNo=" + memberNo +
|
||||
", memberId='" + memberId + '\'' +
|
||||
", gmemberName='" + gmemberName + '\'' +
|
||||
", status='" + status + '\'' +
|
||||
", haveVideo=" + haveVideo +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
public class MyApplication {
|
||||
private static Context mContext;
|
||||
public static String pageName = "";
|
||||
public static pttNative pNative;
|
||||
private static MyApplication mAppInstance = new MyApplication();
|
||||
|
||||
public static void init(Context context) {
|
||||
if (mContext != null) return;
|
||||
mContext = context.getApplicationContext();
|
||||
pageName = mContext.getPackageName();
|
||||
initPTT();
|
||||
}
|
||||
|
||||
public static MyApplication getAppInstance() {
|
||||
if (pNative == null && mContext != null) {
|
||||
initPTT();
|
||||
}
|
||||
return mAppInstance;
|
||||
}
|
||||
|
||||
public pttNative getpNative() {
|
||||
return pNative;
|
||||
}
|
||||
|
||||
public static void initPTT() {
|
||||
if (pNative != null) return;
|
||||
pNative = new pttNative();
|
||||
|
||||
pNative.pttNativeSetPackageName(pageName);
|
||||
pNative.pttNativeSetAtCallback();
|
||||
pNative.pttNativeSetPlayCallback();
|
||||
pNative.pttNativeSetPalyStartCallback();
|
||||
pNative.pttNativeSetPlayStopCallback();
|
||||
pNative.pttNativeSetRecordStartCallback();
|
||||
pNative.pttNativeSetRecordStopCallback();
|
||||
pNative.pttNativeSetTTSCallback();
|
||||
|
||||
pNative.pttNativePocTaskStart();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
/**
|
||||
* 指令发送类
|
||||
* */
|
||||
public class SendAtUtil {
|
||||
|
||||
/**
|
||||
* 账号登录接口
|
||||
* account 账号
|
||||
* pwd 密码
|
||||
* ip 服务器ip
|
||||
* location 是否开启定位 1-开启,0关闭
|
||||
* tempCall 是否开启单呼 1开启单呼,0关闭单呼
|
||||
* */
|
||||
public static void toLogin(String account, String pwd, String ip, int location, int tempCall){
|
||||
String ipHex = str2HexStr("ip=" + ip + ";");
|
||||
String accountHex = str2HexStr("id=" + account + ";");
|
||||
String pwdHex = str2HexStr("pwd=" + pwd + ";");
|
||||
String locationHex = str2HexStr("gps=" + location + ";");
|
||||
String tempCallHex = str2HexStr("inv=" + tempCall + ";");
|
||||
String loginAt = "010000" + ipHex + "" + accountHex + "" + pwdHex + locationHex + tempCallHex + "\r\n";
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd(loginAt);
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("00000000\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 按下PTT开始呼叫
|
||||
* */
|
||||
public static void speakPlay(){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("0B0000");
|
||||
}
|
||||
|
||||
/** 松开PTT结束呼叫*/
|
||||
public static void speakRelease(){
|
||||
AudioRecordManager.getInstance().stopRecord();
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("0C0000");
|
||||
}
|
||||
|
||||
/** 发送TCP包 登录成功后需要定时发送TCP包*/
|
||||
public static void sendTCP(){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("570000\r\n");
|
||||
}
|
||||
|
||||
/** 发送UDP包 登录后需要定时发送UDP包*/
|
||||
public static void sendUDP(){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("580000\r\n");
|
||||
}
|
||||
|
||||
/** 请求群组信息 */
|
||||
public static void getGroupInfo(){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("0D0000");
|
||||
}
|
||||
|
||||
/** 请求群组成员信息
|
||||
* groupId 十六进制群组Id
|
||||
* */
|
||||
public static void getGroupMemberInfo(String groupId){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("0E0000" + groupId);
|
||||
}
|
||||
|
||||
/** 切换群组
|
||||
* groupId 十六进制群组Id
|
||||
* */
|
||||
public static void jumpGroup(String groupId){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("090000" + groupId);
|
||||
}
|
||||
|
||||
/** 请求好友信息 */
|
||||
public static void getFriendInfo(){
|
||||
MyApplication.getAppInstance().getpNative().pttNativeSendAtCmd("0E000000000000\r\n");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 字符串转换成十六进制字符串
|
||||
*
|
||||
* @param str 待转换的ASCII字符串
|
||||
* @return String
|
||||
*/
|
||||
public static String str2HexStr(String str) {
|
||||
|
||||
char[] chars = "0123456789ABCDEF".toCharArray();
|
||||
StringBuilder sb = new StringBuilder("");
|
||||
byte[] bs = str.getBytes();
|
||||
int bit;
|
||||
|
||||
for (int i = 0; i < bs.length; i++) {
|
||||
bit = (bs[i] & 0x0f0) >> 4;
|
||||
sb.append(chars[bit]);
|
||||
bit = bs[i] & 0x0f;
|
||||
sb.append(chars[bit]);
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.hjq.permissions.Permission;
|
||||
import com.hjq.permissions.XXPermissions;
|
||||
import com.stand.standapp.R;
|
||||
|
||||
public class pttActivity extends AppCompatActivity {
|
||||
private Button pttBtn;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_ptt);
|
||||
pttBtn = (Button) findViewById(R.id.pttBtn);
|
||||
|
||||
// 初始化 PTT
|
||||
MyApplication.init(this);
|
||||
|
||||
XXPermissions.with(this)
|
||||
.permission(Permission.RECORD_AUDIO)
|
||||
.request(null);
|
||||
pttBtn.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
pttBtn.setBackgroundColor(Color.RED);
|
||||
SendAtUtil.speakPlay();
|
||||
} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||
pttBtn.setBackgroundColor(Color.BLUE);
|
||||
SendAtUtil.speakRelease();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
SendAtUtil.toLogin("test012", "123456", "106.15.8.60", 1, 1);
|
||||
}
|
||||
});
|
||||
|
||||
/** 获取群组 */
|
||||
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
SendAtUtil.getGroupInfo();
|
||||
}
|
||||
});
|
||||
|
||||
/** 获取群成员 */
|
||||
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
SendAtUtil.getGroupMemberInfo("0002b199");
|
||||
}
|
||||
});
|
||||
|
||||
/** 切换群组 */
|
||||
findViewById(R.id.btnJumpGroup).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
SendAtUtil.jumpGroup("0002b199");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package com.example.kingway.ptt;
|
||||
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.AudioTrack;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class pttNative {
|
||||
private static String TAG = "pttNative";
|
||||
static {
|
||||
System.loadLibrary("ptt");
|
||||
}
|
||||
|
||||
public static char Char2ASCII(char c)
|
||||
{
|
||||
return (char) (c <= 9 ? (c+0x30) : (c+0x37));
|
||||
}
|
||||
|
||||
public static char Ascii2Char(char nib_h, char nib_l)
|
||||
{
|
||||
nib_h = (nib_h <= '9')?((char)(nib_h-0x30)):(((nib_h<='F')?((char)(nib_h-0x37)):((char)(nib_h-0x57))));
|
||||
nib_l = (nib_l <= '9')?((char)(nib_l-0x30)):(((nib_l<='F')?((char)(nib_l-0x37)):((char)(nib_l-0x57))));
|
||||
return (char)((nib_h<<4)+nib_l);
|
||||
}
|
||||
|
||||
|
||||
//Callback Function for Recv the message from POC Lib
|
||||
public void OEM_AT_cb(String sData)
|
||||
{
|
||||
Log.e(TAG,"Poc2 AT Callback: " + sData);
|
||||
int len, i;
|
||||
char[] bytes = sData.toCharArray();
|
||||
|
||||
List<String> lisHex = new ArrayList<>();
|
||||
if (bytes.length > 5 && bytes[0] == '+' && bytes[1] == 'P' && bytes[2] == 'O' && bytes[3] == 'C' && bytes[4] == ':') {
|
||||
len = bytes.length - 5;
|
||||
if (len >= 2) {
|
||||
for (i = 0; i < len - 1; i = i + 2) {
|
||||
String temp = Integer.toHexString(Ascii2Char(bytes[i + 5], bytes[i + 6]));
|
||||
|
||||
lisHex.add(temp.length() == 1 ? "0" + temp : temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CallBackResolution.at_OEM_AT_cb(lisHex);
|
||||
}
|
||||
|
||||
public void OEM_Play_cb(byte[] bytes){
|
||||
if (mAudioTrack == null) {
|
||||
initAudioTrack();
|
||||
}
|
||||
|
||||
if (mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING){
|
||||
mAudioTrack.play();
|
||||
}
|
||||
mAudioTrack.write(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
public void OEM_Play_Start_cb()
|
||||
{
|
||||
Log.e("Play Start!!!!\r\n","");
|
||||
}
|
||||
|
||||
public void OEM_Play_Stop_cb()
|
||||
{
|
||||
Log.e("Play Stop!!!!\r\n","");
|
||||
}
|
||||
|
||||
public void OEM_Record_Start_cb()
|
||||
{
|
||||
Log.e("Record Start!!!!\r\n","");
|
||||
}
|
||||
|
||||
public void OEM_Record_Stop_cb()
|
||||
{
|
||||
Log.e("Record Stop!!!!\r\n","");
|
||||
}
|
||||
|
||||
public void OEM_Play_TTS_cb(String sData)
|
||||
{
|
||||
Log.e(TAG , "Play TTS " + sData);
|
||||
CallBackResolution.at_Play_TTS_cb(sData);
|
||||
}
|
||||
|
||||
//Define the Function of the AT Command send
|
||||
private native void OEM_AT_Send(String strAtCmd);
|
||||
private native int OEM_Set_AT_CallBack(String cb);
|
||||
|
||||
private native void OEM_Push_Record_Data(byte[] data);
|
||||
private native int OEM_Set_Play_CallBack(String cb);
|
||||
|
||||
private native int OEM_Set_Play_Start_Callback(String cb);
|
||||
private native int OEM_Set_Play_Stop_Callback(String cb);
|
||||
|
||||
private native int OEM_Set_Record_Start_Callback(String cb);
|
||||
private native int OEM_Set_Record_Stop_Callback(String cb);
|
||||
|
||||
private native int OEM_Set_TTS_Callback(String cb);
|
||||
|
||||
private native void OEM_Poc_Task_Start();
|
||||
|
||||
private native int OEM_Set_Package_Name(String name);
|
||||
|
||||
public void pttNativeSendRecordCmd(byte[] data) {
|
||||
OEM_Push_Record_Data(data);
|
||||
}
|
||||
|
||||
public void pttNativeSendAtCmd(String strAtCmd)
|
||||
{
|
||||
Log.i(TAG,"strAtCmd " + strAtCmd);
|
||||
OEM_AT_Send(strAtCmd);
|
||||
}
|
||||
|
||||
public void pttNativePocTaskStart()
|
||||
{
|
||||
OEM_Poc_Task_Start();
|
||||
}
|
||||
|
||||
public int pttNativeSetAtCallback()
|
||||
{
|
||||
return OEM_Set_AT_CallBack("OEM_AT_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetPlayCallback()
|
||||
{
|
||||
return OEM_Set_Play_CallBack("OEM_Play_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetPalyStartCallback()
|
||||
{
|
||||
return OEM_Set_Play_Start_Callback("OEM_Play_Start_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetPlayStopCallback()
|
||||
{
|
||||
return OEM_Set_Play_Stop_Callback("OEM_Play_Stop_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetRecordStartCallback()
|
||||
{
|
||||
return OEM_Set_Record_Start_Callback("OEM_Record_Start_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetRecordStopCallback()
|
||||
{
|
||||
return OEM_Set_Record_Stop_Callback("OEM_Record_Stop_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetTTSCallback()
|
||||
{
|
||||
return OEM_Set_TTS_Callback("OEM_Play_TTS_cb");
|
||||
}
|
||||
|
||||
public int pttNativeSetPackageName(String name)
|
||||
{
|
||||
return OEM_Set_Package_Name(name);
|
||||
}
|
||||
|
||||
public pttNative() {
|
||||
initAudioTrack();
|
||||
}
|
||||
|
||||
private AudioTrack mAudioTrack;
|
||||
public void initAudioTrack() {
|
||||
int mAudioMinBufSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
|
||||
|
||||
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_MEDIA)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
|
||||
.build();
|
||||
|
||||
AudioFormat audioFormat = new AudioFormat.Builder()
|
||||
.setSampleRate(8000)
|
||||
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.build();
|
||||
|
||||
mAudioTrack = new AudioTrack(
|
||||
audioAttributes,
|
||||
audioFormat,
|
||||
mAudioMinBufSize * 4,
|
||||
AudioTrack.MODE_STREAM,
|
||||
AudioManager.AUDIO_SESSION_ID_GENERATE
|
||||
);
|
||||
mAudioTrack.setVolume(1.0f);
|
||||
|
||||
}
|
||||
|
||||
public void destory() {
|
||||
if (mAudioTrack != null) {
|
||||
try {
|
||||
if (mAudioTrack.getState() == AudioRecord.STATE_INITIALIZED) {
|
||||
mAudioTrack.stop();
|
||||
}
|
||||
mAudioTrack.release();
|
||||
mAudioTrack = null;
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ class LoginActivity : AppCompatActivity() {
|
|||
val etPassword = findViewById<EditText>(R.id.et_password)
|
||||
val cbRemember = findViewById<CheckBox>(R.id.cb_remember)
|
||||
val btnLogin = findViewById<Button>(R.id.btn_login)
|
||||
// val btnPttExample = findViewById<Button>(R.id.btn_ptt_example)
|
||||
val btnPttExample = findViewById<Button>(R.id.btn_ptt_example)
|
||||
val ivSettings = findViewById<ImageView>(R.id.iv_settings)
|
||||
|
||||
// 1. 加载保存的状态和凭据
|
||||
|
|
@ -53,15 +53,15 @@ class LoginActivity : AppCompatActivity() {
|
|||
performLogin(username, password, cbRemember.isChecked)
|
||||
}
|
||||
|
||||
// // 4. PTT 示例按钮点击事件
|
||||
// btnPttExample.setOnClickListener {
|
||||
// try {
|
||||
// val intent = Intent(this, com.example.kingway.ptt.pttActivity::class.java)
|
||||
// startActivity(intent)
|
||||
// } catch (e: Exception) {
|
||||
// Toast.makeText(this, "打开 PTT 示例失败: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
// }
|
||||
// }
|
||||
// 4. PTT 示例按钮点击事件
|
||||
btnPttExample.setOnClickListener {
|
||||
try {
|
||||
val intent = Intent(this, com.example.kingway.ptt.pttActivity::class.java)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(this, "打开 PTT 示例失败: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -129,6 +129,15 @@
|
|||
android:textColor="#FFFFFF"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_ptt_example"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@drawable/shape_button_login"
|
||||
android:text="PTT 示例"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue