diff --git a/.gitignore b/.gitignore index cfdd83a..7da7882 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 860a3cc..73834c1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,17 +6,18 @@ plugins { android { namespace = "com.stand.standapp" compileSdk = 36 - + defaultConfig { 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) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a3e1563..ce5ae38 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + @@ -22,6 +23,7 @@ android:usesCleartextTraffic="true" android:networkSecurityConfig="@xml/network_security_config" android:theme="@style/Theme.StandAPP"> + + + WebSocket 实时推送 未连接 - +
ID: -
@@ -102,7 +103,7 @@
-
@@ -171,70 +172,9 @@
测试签收单

单号: SN20260315888

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

-

物料: 工业级调度终端 x 5

+
+

物料: 工业级调度终端 x 5

+

时间:

[ 此处为二维码/条码预览 ] @@ -260,6 +200,8 @@
diff --git a/app/src/main/java/com/example/kingway/ptt/AudioRecordManager.java b/app/src/main/java/com/example/kingway/ptt/AudioRecordManager.java new file mode 100644 index 0000000..81e812f --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/AudioRecordManager.java @@ -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(); + } + } + +} diff --git a/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java b/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java new file mode 100644 index 0000000..bc9c56b --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/CallBackResolution.java @@ -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 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 new file mode 100644 index 0000000..eec4c49 --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/CallBackUtil.java @@ -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 + *

+ * 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 new file mode 100644 index 0000000..9b4924e --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/GroupInfoDto.java @@ -0,0 +1,69 @@ +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 new file mode 100644 index 0000000..be661bd --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/GroupMemberInfoDto.java @@ -0,0 +1,80 @@ +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 new file mode 100644 index 0000000..935b839 --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/MyApplication.java @@ -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(); + } +} diff --git a/app/src/main/java/com/example/kingway/ptt/SendAtUtil.java b/app/src/main/java/com/example/kingway/ptt/SendAtUtil.java new file mode 100644 index 0000000..e0be413 --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/SendAtUtil.java @@ -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(); + } +} diff --git a/app/src/main/java/com/example/kingway/ptt/pttActivity.java b/app/src/main/java/com/example/kingway/ptt/pttActivity.java new file mode 100644 index 0000000..be0d3d2 --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/pttActivity.java @@ -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"); + } + }); + } +} diff --git a/app/src/main/java/com/example/kingway/ptt/pttNative.java b/app/src/main/java/com/example/kingway/ptt/pttNative.java new file mode 100644 index 0000000..9b311f6 --- /dev/null +++ b/app/src/main/java/com/example/kingway/ptt/pttNative.java @@ -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 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 0aa1e30..c4369ba 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) diff --git a/app/src/main/java/com/stand/standapp/LoginActivity.kt b/app/src/main/java/com/stand/standapp/LoginActivity.kt index 9742259..0957fc3 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