diff --git a/.gitignore b/.gitignore index 3c946bb..cfdd83a 100644 --- a/.gitignore +++ b/.gitignore @@ -13,16 +13,17 @@ .externalNativeBuild .cxx local.properties +/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/ -/第三方/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 -/app/release/ +/.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 diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index c338959..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,84 +0,0 @@ -# StandAPP 智能调度终端 - 开发指南 (AGENTS.md) - -本文档为 StandAPP 项目的开发规范与技术参考,旨在帮助开发者快速理解项目架构、构建流程及核心业务逻辑。 - -## 1. 项目概况 -StandAPP 是一款基于 Kotlin 和 Jetpack Compose 开发的 Android 应用。集成了 `AgentWeb`(网页交互)、打印服务、自动更新以及核心的 **PTT (Push-to-Talk) 对讲模块**。 - -- **主包名:** `com.stand.standapp` -- **PTT 包名:** `com.example.kingway.ptt` (严禁修改,与 Native 层硬绑定) -- **技术栈:** Kotlin, Jetpack Compose, Gradle (Kotlin DSL), OkHttp, AgentWeb, NDK (C++). -- **SDK 版本:** Compile SDK 36, Target SDK 34, Min SDK 24. - -## 2. 构建、检查与测试 - -### 构建命令 -- **编译 Debug APK:** `./gradlew assembleDebug` -- **清理项目:** `./gradlew clean` -- **编译 Release APK:** `./gradlew assembleRelease` - -### 检查与测试 -- **运行 Lint 检查:** `./gradlew lint` -- **运行单元测试:** `./gradlew test` -- **运行单项测试:** `./gradlew :app:testDebugUnitTest --tests "包名.类名"` - -## 3. 代码风格与规范 - -- **缩进:** 4 个空格。 -- **命名:** 类名使用 `PascalCase`;函数和变量使用 `camelCase`;常量使用 `SCREAMING_SNAKE_CASE`。 -- **Compose:** 优先使用 Material 3 组件;Composable 函数首字母大写;Modifier 必须作为第一个可选参数。 -- **错误处理:** 必须使用 `try-catch` 包裹 IO、网络、JSON 解析等易错操作,并使用 `Log.e` 记录异常。 - -## 4. PTT (对讲) 核心业务逻辑解析 - -PTT 模块是本项目的通讯核心,依赖底层 `libptt.so` 原生引擎。其业务流程高度依赖 **十六进制 AT 指令** 交互。 - -### 4.1 核心组件与关键方法 - -#### **A. 指令发送:`SendAtUtil` (指令工厂)** -封装了所有发往 Native 引擎的控制指令。 -- `toLogin(account, pwd, ip, location, tempCall)`: **登录接口**。将账号、密码、服务器 IP、GPS 开启标志及单呼标志拼接为 `010000` 开头的十六进制指令。 -- `speakPlay()`: **开始对讲**。发送 `0B0000` 指令,触发底层 POC 引擎进入发送模式并准备处理语音数据。 -- `speakRelease()`: **停止对讲**。发送 `0C0000` 指令通知引擎结束通话,并同步停止本地录音线程。 -- `sendTCP()` / `sendUDP()`: **心跳维持**。登录成功后,必须每 **40秒** 调用一次。`570000` 为 TCP 心跳,`580000` 为 UDP 心跳。 -- `getGroupInfo()`: **查询群组**。发送 `0D0000` 指令。 -- `getGroupMemberInfo(groupId)`: **查询成员**。发送 `0E0000` + 群组 ID。 -- `jumpGroup(groupId)`: **切换群组**。发送 `090000` + 群组 ID,用于即时切换监听的群组。 - -#### **B. 指令解析:`CallBackResolution` (响应处理器)** -解析 Native 引擎通过 JNI 异步返回的十六进制数据。 -- `at_OEM_AT_cb(lisHex)`: **解析入口**。识别首字节指令码: - - `82`: 登录/离线状态变更。 - - `80`: 接收群组详细信息(ID、名称、成员数)。 - - `81`: 接收群成员在线状态及视频能力。 - - `83`: 语音发起者信息(当前谁在说话)。 - - `8b`: 播音状态变更(引擎开始或停止播放对方声音)。 - - `8d`: 经纬度数据通知。 -- `at_Play_TTS_cb(sData)`: **TTS 状态解析**。解析中文状态描述。当识别到“已登录”时,需触发心跳定时器。 -- `isLoginConflict(state)`: **冲突检测**。逻辑:1 分钟内若状态在“离线”和“登录中”切换超过 5 次,则判断为登录冲突。 - -#### **C. 录音管理:`AudioRecordManager` (音频采集)** -- `startRecord()`: 启动录音循环。设置优先级为 `THREAD_PRIORITY_URGENT_AUDIO`。 -- `stopRecord()`: 释放 `AudioRecord` 资源。 -- **技术参数**: 采样率 8000Hz, 16bit, 单声道。每读取 320 字节 PCM 原始数据即调用 `pttNativeSendRecordCmd` 推送至 Native 层。 - -#### **D. JNI 桥接:`pttNative` (底层交互)** -- `OEM_Play_cb(byte[] bytes)`: **播放回调**。底层接收到的音频流会实时回调此方法,数据需直接写入 `AudioTrack` 实例进行播放。 -- `pttNativePocTaskStart()`: **引擎初始化**。 - -### 4.2 业务技术流程 -1. **初始化**: 调用 `MyApplication.init(context)` -> 加载 SO 库 -> 启动 Native 任务。 -2. **认证**: 调用 `toLogin` -> 异步等待 `82` 回调或 TTS “已登录”提示。 -3. **在线维持**: 登录成功后启动周期为 40s 的任务发送 TCP/UDP 心跳。 -4. **对讲过程**: - - **按下 PTT**: `speakPlay` -> 收到成功反馈 -> `AudioRecordManager` 持续采音并推送。 - - **松开 PTT**: `speakRelease` -> 停止录音 -> 发送结束指令。 -5. **UI 更新**: 引擎回调均在 **子线程**。所有 UI 操作(Toast、Dialog、TextView 更新)必须切回主线程。 - -## 5. 开发约束与安全 -1. **包名与类名**: `com.example.kingway.ptt.pttNative` 严禁改名或移动,否则会导致 JNI 方法找不到实现导致崩溃。 -2. **权限管理**: 必须先通过 `XXPermissions` 获取 `RECORD_AUDIO` 权限后再执行对讲。 -3. **线程规范**: 回调处理严禁阻塞。耗时操作必须异步执行。 - ---- -*本文档由 AI 助手维护,旨在确保业务逻辑的一致性。* diff --git a/PTT_API_DOC.md b/PTT_API_DOC.md deleted file mode 100644 index 5127fec..0000000 --- a/PTT_API_DOC.md +++ /dev/null @@ -1,175 +0,0 @@ -# PTT 平台外部接口详细文档 - -**基础路径**: `/api/external/ptt` -**说明**: 该控制器是与第三方 PTT 语音平台对接的核心入口,涵盖了频道管理、成员状态、录音存取及语音识别等全量功能。 - ---- - -## 1. 频道与会话管理 - -### 三、创建频道 -* **接口**: `POST /group` -* **描述**: 在平台创建一个新的普通频道,支持邀请账号列表或用户 UID 列表。 -* **参数**: - * `gname` (Query, 必填): 群组/频道名称。 - * `accounts` (Body, 可选): 参与成员账号列表 (`List`)。 - * `uids` (Query, 可选): 参与成员 UID 列表 (`List`)。 -* **返回**: `Result` - -### 四、删除频道 -* **接口**: `DELETE /group/{gid}` -* **描述**: 删除指定的普通频道。 -* **参数**: - * `gid` (Path, 必填): 频道 ID。 -* **返回**: `Result` - -### 五、修改频道 -* **接口**: `PUT /group/{gid}` -* **描述**: 修改频道的名称信息。 -* **参数**: - * `gid` (Path, 必填): 频道 ID。 - * `newName` (Query, 必填): 新的频道名称。 -* **返回**: `Result` - -### 六、拉成员入频道 -* **接口**: `POST /group/{gid}/members` -* **描述**: 将指定的账号列表加入到特定的频道中。 -* **参数**: - * `gid` (Path, 必填): 频道 ID。 - * `accounts` (Body, 必填): 待加入的成员账号列表 (`List`)。 -* **返回**: `Result` - -### 七、成员踢出频道 -* **接口**: `DELETE /group/{gid}/members` -* **描述**: 将指定的账号列表从频道中移除。 -* **参数**: - * `gid` (Path, 必填): 频道 ID。 - * `accounts` (Body, 必填): 待移除的成员账号列表 (`List`)。 -* **返回**: `Result` - -### 二十、创建临时会话 -* **接口**: `POST /temp-group` -* **描述**: 发起一个新的临时群组通话。 -* **参数**: - * `gname` (Query, 必填): 群组名称。 - * `accounts` (Body, 必填): 参与成员账号列表 (`List`)。 - * `creatorid` (Query, 必填): 创建者 UID。 -* **返回**: `Result` - -### 二十一、删除临时会话 -* **接口**: `DELETE /temp-group/{gid}` -* **描述**: 注销/结束一个临时群组。 -* **参数**: - * `gid` (Path, 必填): 临时群组 ID。 -* **返回**: `Result` - ---- - -## 2. 列表与成员查询 - -### 八、频道成员列表 -* **接口**: `GET /group/{gid}/members` -* **描述**: 查询指定普通频道内的成员列表,支持搜索和分页。 -* **参数**: - * `gid` (Path, 必填): 频道 ID。 - * `page`, `limit` (Query, 可选): 分页参数。 - * `search` (Query, 可选): 搜索关键词(用户名/账号)。 -* **返回**: `Result>>` - -### 九、频道列表查询 -* **接口**: `GET /groups` -* **描述**: 获取系统内所有的普通频道列表。 -* **参数**: `page`, `limit` (Query, 可选)。 -* **返回**: `Result>>` - -### 十、临时会话列表查询 -* **接口**: `GET /temp-groups` -* **描述**: 获取所有的临时群组列表。 -* **参数**: `page`, `limit` (Query, 可选)。 -* **返回**: `Result>>` - -### 十一、成员可见频道列表查询 -* **接口**: `GET /user/groups` -* **描述**: 查询指定成员有权限查看的频道。 -* **参数**: `account`, `uid`, `search` (Query, 可选)。 -* **返回**: `Result>` - -### 二十九、查询某个临时群组下的成员 -* **接口**: `GET /temp-group/{tempgid}/members` -* **描述**: 获取指定临时群组内的所有成员详情。 -* **参数**: `tempgid` (Path, 必填)。 -* **返回**: `Result>` - -### 三十一、分页查询群组成员 -* **接口**: `GET /group/{gid}/members-paged` -* **描述**: 按在线状态分页查询群组成员。 -* **参数**: - * `gid` (Path, 必填)。 - * `condition` (Query, 可选): 在线状态筛选 (1:在线, 2:离线, 3:在线不在组)。 - * `page`, `limit` (Query, 可选)。 -* **返回**: `Result>>` - ---- - -## 3. 录音记录与处理 (核心功能) - -### 十二、查询频道内成员ptt录音 -* **接口**: `POST /group/{gid}/records` -* **描述**: 获取普通频道内指定成员在特定时间段的 PTT 录音记录。 -* **参数**: - * `gid` (Path, 必填)。 - * `uids` (Body, 可选): 筛选的成员 UID 列表。 - * `start`, `end` (Query, 可选): 时间范围 (yyyy-MM-dd HH:mm:ss)。 - * `page`, `limit` (Query, 可选)。 -* **返回**: `Result>>` - -### 十三、查询临时会话下的录音 -* **接口**: `POST /temp-group/{tempgid}/records` -* **描述**: 获取临时会话下特定时间段的 PTT 录音记录。 -* **参数**: 同上,针对临时群组 ID。 -* **返回**: `Result>>` - -### 获取录音转文字 (新增集成) -* **接口**: `GET /record/text` -* **描述**: 根据录音地址获取并转为文字。 -* **特性**: - 1. 内部自动处理音频下载与本地缓存。 - 2. 已转义过的记录将直接从数据库返回,避免重复调用 ASR。 -* **参数**: `path` (Query, 必填): 录音文件远程相对路径。 -* **返回**: `Result` (识别后的文本内容)。 - -### 播放录音 (流式处理) -* **接口**: `GET /record/play` -* **描述**: 在线播放录音文件,首次播放会自动缓存到服务器本地。 -* **特性**: - 1. **流式输出**: 直接写入 Response 输出流,无内存占用压力。 - 2. **拖动支持**: 支持 HTTP Range 请求,可在浏览器进度条中任意拖动。 - 3. **优先播放**: 默认 `inline` 模式,谷歌浏览器直接调用内置播放器。 -* **参数**: `path` (Query, 必填): 录音文件远程相对路径。 -* **返回**: 二进制音频流 (`audio/wav`)。 - ---- - -## 4. 系统工具 - -### 手动执行平台登录 -* **接口**: `POST /login` -* **描述**: 刷新 PTT 平台的会话 Token 并重新登录。用于故障恢复或手动同步。 -* **返回**: `Result` - -### 二、退出登录 -* **接口**: `POST /logout` -* **描述**: 注销当前 PTT 会话。 -* **返回**: `Result` - ---- - -## 响应规范 -除 `playRecord` 这种流式接口外,所有接口均遵循统一响应体: -```json -{ - "code": 200, // 状态码 (200: 成功) - "msg": "操作成功", // 提示信息 - "data": { ... } // 业务载荷 -} -``` diff --git a/app/.gitignore b/app/.gitignore index 0b8938f..42afabf 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,5 +1 @@ -/build -/release/baselineProfiles/0/app-release.dm -/release/baselineProfiles/1/app-release.dm -/release/app-release-V1.0.3.apk -/release/output-metadata.json +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 70b5b4a..6cd5de0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,7 +6,7 @@ plugins { android { namespace = "com.stand.standapp" compileSdk = 36 - + defaultConfig { applicationId = "com.stand.standapp" minSdk = 24 @@ -15,9 +15,8 @@ android { versionName = "1.0.5" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - ndk { - abiFilters.addAll(listOf("armeabi-v7a", "arm64-v8a")) + abiFilters += listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64") } } @@ -49,7 +48,6 @@ 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) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ce5ae38..c1b6a2b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,7 +6,6 @@ - @@ -23,7 +22,6 @@ android:usesCleartextTraffic="true" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/Theme.StandAPP"> - - - - - WebSocket 实时推送 未连接 - +
ID: -
@@ -102,7 +102,7 @@
-
@@ -384,7 +384,7 @@ item.className = 'log-item'; item.innerHTML = `[${time}]${msg}`; logList.insertBefore(item, logList.firstChild); - + // 限制 DOM 节点数量,避免页面卡顿(虽然存储 5000 条,但显示建议限制在 200 条) if (logList.childNodes.length > 200) { logList.removeChild(logList.lastChild); @@ -412,13 +412,13 @@ ws.onclose = null; // 清除之前的重连逻辑 ws.close(); } - + const rawUrl = document.getElementById('ws-url-input').value; const finalUrl = rawUrl.replace("{clientId}", clientId); - + addLog("正在尝试连接: " + finalUrl, false); isManualClose = false; // 启动连接时重置标记 - + try { ws = new WebSocket(finalUrl); @@ -441,7 +441,7 @@ updateWSStatus(false); addLog("连接关闭", false); stopHeartbeat(); - + // 只有非手动断开(如网络异常)时才自动重连 if (!isManualClose) { addLog("异常断开,5秒后自动重连...", false); diff --git a/app/src/main/java/com/example/kingway/ptt/AudioRecordManager.java b/app/src/main/java/com/example/kingway/ptt/AudioRecordManager.java deleted file mode 100644 index 81e812f..0000000 --- a/app/src/main/java/com/example/kingway/ptt/AudioRecordManager.java +++ /dev/null @@ -1,177 +0,0 @@ -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(); - } - } - -} diff --git a/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java b/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java deleted file mode 100644 index bc9c56b..0000000 --- a/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java +++ /dev/null @@ -1,470 +0,0 @@ -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 memberInfoDtos = new ArrayList<>(); - private static int speckType = 1; - private static String talkId = ""; - private static String talkName = ""; - private static ArrayList 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 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 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 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 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 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 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; - } -} diff --git a/app/src/main/java/com/example/kingway/ptt/CallBackUtil.java b/app/src/main/java/com/example/kingway/ptt/CallBackUtil.java deleted file mode 100644 index eec4c49..0000000 --- a/app/src/main/java/com/example/kingway/ptt/CallBackUtil.java +++ /dev/null @@ -1,263 +0,0 @@ -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 - *

- * 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 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){ - - } - -} diff --git a/app/src/main/java/com/example/kingway/ptt/GroupInfoDto.java b/app/src/main/java/com/example/kingway/ptt/GroupInfoDto.java deleted file mode 100644 index 9b4924e..0000000 --- a/app/src/main/java/com/example/kingway/ptt/GroupInfoDto.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.example.kingway.ptt; - -/** - *

- *     desc   :  组
- * 
- */ - -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 + '\'' + - '}'; - } -} diff --git a/app/src/main/java/com/example/kingway/ptt/GroupMemberInfoDto.java b/app/src/main/java/com/example/kingway/ptt/GroupMemberInfoDto.java deleted file mode 100644 index be661bd..0000000 --- a/app/src/main/java/com/example/kingway/ptt/GroupMemberInfoDto.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.example.kingway.ptt; - -import java.io.Serializable; - -/** - *
- *     desc   :  组 成员
- * 
- */ - -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 + - '}'; - } -} diff --git a/app/src/main/java/com/example/kingway/ptt/MyApplication.java b/app/src/main/java/com/example/kingway/ptt/MyApplication.java deleted file mode 100644 index 935b839..0000000 --- a/app/src/main/java/com/example/kingway/ptt/MyApplication.java +++ /dev/null @@ -1,45 +0,0 @@ -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(); - } -} diff --git a/app/src/main/java/com/example/kingway/ptt/SendAtUtil.java b/app/src/main/java/com/example/kingway/ptt/SendAtUtil.java deleted file mode 100644 index e0be413..0000000 --- a/app/src/main/java/com/example/kingway/ptt/SendAtUtil.java +++ /dev/null @@ -1,96 +0,0 @@ -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(); - } -} diff --git a/app/src/main/java/com/example/kingway/ptt/pttActivity.java b/app/src/main/java/com/example/kingway/ptt/pttActivity.java deleted file mode 100644 index be0d3d2..0000000 --- a/app/src/main/java/com/example/kingway/ptt/pttActivity.java +++ /dev/null @@ -1,83 +0,0 @@ -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"); - } - }); - } -} diff --git a/app/src/main/java/com/example/kingway/ptt/pttNative.java b/app/src/main/java/com/example/kingway/ptt/pttNative.java deleted file mode 100644 index 9b311f6..0000000 --- a/app/src/main/java/com/example/kingway/ptt/pttNative.java +++ /dev/null @@ -1,208 +0,0 @@ -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 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) {} - } - } - -} diff --git a/app/src/main/java/com/stand/standapp/AndroidInterface.kt b/app/src/main/java/com/stand/standapp/AndroidInterface.kt index e93fc77..0aa1e30 100644 --- a/app/src/main/java/com/stand/standapp/AndroidInterface.kt +++ b/app/src/main/java/com/stand/standapp/AndroidInterface.kt @@ -27,10 +27,10 @@ class AndroidInterface(private val activity: MainActivity) { Toast.makeText(activity, "WebView 未就绪", Toast.LENGTH_SHORT).show() return@post } - + val printAdapter = webView.createPrintDocumentAdapter("Document") val jobName = activity.getString(R.string.app_name) + " Print Job" - + printManager.print(jobName, printAdapter, PrintAttributes.Builder().build()) } catch (e: Exception) { Log.e("Print", "系统打印失败", e) @@ -173,7 +173,7 @@ class AndroidInterface(private val activity: MainActivity) { fun onPageFinished() { Handler(Looper.getMainLooper()).post { try { -// activity.cancelTimeoutTimer() + activity.cancelTimeoutTimer() activity.findViewById(R.id.loading_layout)?.visibility = android.view.View.GONE } catch (e: Exception) { Log.e("UI", "隐藏加载布局失败", e) diff --git a/app/src/main/java/com/stand/standapp/LoginActivity.kt b/app/src/main/java/com/stand/standapp/LoginActivity.kt index 0957fc3..9742259 100644 --- a/app/src/main/java/com/stand/standapp/LoginActivity.kt +++ b/app/src/main/java/com/stand/standapp/LoginActivity.kt @@ -23,7 +23,7 @@ class LoginActivity : AppCompatActivity() { val etPassword = findViewById(R.id.et_password) val cbRemember = findViewById(R.id.cb_remember) val btnLogin = findViewById