Merge branch 'fanpeng' into 'develop'
Fanpeng See merge request StarlockTeam/wx-starlock!48
This commit is contained in:
commit
2b17f261d4
2
App.vue
2
App.vue
@ -31,7 +31,7 @@
|
|||||||
return 'XHJ'
|
return 'XHJ'
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
return 'DEV'
|
return 'PRE'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@ -8,7 +8,6 @@
|
|||||||
</view>
|
</view>
|
||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:filter="filter"
|
:filter="filter"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
:minDate="minDate"
|
:minDate="minDate"
|
||||||
:show="show"
|
:show="show"
|
||||||
|
|||||||
@ -21,10 +21,10 @@ export const keysType = {
|
|||||||
},
|
},
|
||||||
6: {
|
6: {
|
||||||
name: '掌静脉',
|
name: '掌静脉',
|
||||||
icon: 'https://oss-lock.xhjcn.ltd/mp/icon_palm_vein_white.png'
|
icon: 'https://oss-lock.xhjcn.ltd/mp/icon_palm_vein_1.png'
|
||||||
},
|
},
|
||||||
7: {
|
7: {
|
||||||
name: '遥控',
|
name: '遥控',
|
||||||
icon: 'https://oss-lock.xhjcn.ltd/mp/icon_remote_white.png'
|
icon: 'https://oss-lock.xhjcn.ltd/mp/icon_remote_1.png'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
216
manifest.json
216
manifest.json
@ -1,114 +1,114 @@
|
|||||||
{
|
{
|
||||||
"name": "星星锁Lite",
|
"name" : "星星锁Lite",
|
||||||
"appid": "__UNI__933D519",
|
"appid" : "__UNI__933D519",
|
||||||
"description": "",
|
"description" : "",
|
||||||
"versionName": "1.3.0",
|
"versionName" : "1.3.1",
|
||||||
"versionCode": "37",
|
"versionCode" : "39",
|
||||||
"mp-weixin": {
|
"mp-weixin" : {
|
||||||
"appid": "wx9829a39e65550757",
|
"appid" : "wx9829a39e65550757",
|
||||||
"setting": {
|
"setting" : {
|
||||||
"urlCheck": true,
|
"urlCheck" : true,
|
||||||
"minified": true
|
"minified" : true
|
||||||
},
|
|
||||||
"permission": {
|
|
||||||
"scope.bluetooth": {
|
|
||||||
"desc": "蓝牙将用于控制和管理您的智能门锁"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"usingComponents": true,
|
|
||||||
"lazyCodeLoading": "requiredComponents",
|
|
||||||
"optimization": {
|
|
||||||
"subPackages": true
|
|
||||||
},
|
|
||||||
"plugins": {
|
|
||||||
"wmpf-voip": {
|
|
||||||
"version": "latest",
|
|
||||||
"provider": "wxf830863afde621eb",
|
|
||||||
"genericsImplementation": {
|
|
||||||
"call-page-plugin": {
|
|
||||||
"custombox": "pages/main/customBox"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"vueVersion": "3",
|
|
||||||
"app-plus": {
|
|
||||||
"distribute": {
|
|
||||||
"icons": {
|
|
||||||
"android": {
|
|
||||||
"hdpi": "unpackage/res/icons/72x72.png",
|
|
||||||
"xhdpi": "unpackage/res/icons/96x96.png",
|
|
||||||
"xxhdpi": "unpackage/res/icons/144x144.png",
|
|
||||||
"xxxhdpi": "unpackage/res/icons/192x192.png"
|
|
||||||
},
|
},
|
||||||
"ios": {
|
"permission" : {
|
||||||
"appstore": "unpackage/res/icons/1024x1024.png",
|
"scope.bluetooth" : {
|
||||||
"ipad": {
|
"desc" : "蓝牙将用于控制和管理您的智能门锁"
|
||||||
"app": "unpackage/res/icons/76x76.png",
|
}
|
||||||
"app@2x": "unpackage/res/icons/152x152.png",
|
},
|
||||||
"notification": "unpackage/res/icons/20x20.png",
|
"usingComponents" : true,
|
||||||
"notification@2x": "unpackage/res/icons/40x40.png",
|
"lazyCodeLoading" : "requiredComponents",
|
||||||
"proapp@2x": "unpackage/res/icons/167x167.png",
|
"optimization" : {
|
||||||
"settings": "unpackage/res/icons/29x29.png",
|
"subPackages" : true
|
||||||
"settings@2x": "unpackage/res/icons/58x58.png",
|
},
|
||||||
"spotlight": "unpackage/res/icons/40x40.png",
|
"plugins" : {
|
||||||
"spotlight@2x": "unpackage/res/icons/80x80.png"
|
"wmpf-voip" : {
|
||||||
},
|
"version" : "latest",
|
||||||
"iphone": {
|
"provider" : "wxf830863afde621eb",
|
||||||
"app@2x": "unpackage/res/icons/120x120.png",
|
"genericsImplementation" : {
|
||||||
"app@3x": "unpackage/res/icons/180x180.png",
|
"call-page-plugin" : {
|
||||||
"notification@2x": "unpackage/res/icons/40x40.png",
|
"custombox" : "pages/main/customBox"
|
||||||
"notification@3x": "unpackage/res/icons/60x60.png",
|
}
|
||||||
"settings@2x": "unpackage/res/icons/58x58.png",
|
}
|
||||||
"settings@3x": "unpackage/res/icons/87x87.png",
|
}
|
||||||
"spotlight@2x": "unpackage/res/icons/80x80.png",
|
|
||||||
"spotlight@3x": "unpackage/res/icons/120x120.png"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"android": {
|
|
||||||
"permissions": [
|
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
|
||||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
|
||||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.BLUETOOTH\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.BLUETOOTH_SCAN\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.BLUETOOTH_CONNECT\" />",
|
|
||||||
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
|
||||||
"<uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>"
|
|
||||||
],
|
|
||||||
"targetSdkVersion": 34,
|
|
||||||
"abiFilters": ["armeabi-v7a", "arm64-v8a"]
|
|
||||||
},
|
|
||||||
"ios": {
|
|
||||||
"dSYMs": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"modules": {
|
"vueVersion" : "3",
|
||||||
"Bluetooth": {},
|
"app-plus" : {
|
||||||
"VideoPlayer": {},
|
"distribute" : {
|
||||||
"Camera": {},
|
"icons" : {
|
||||||
"Record": {}
|
"android" : {
|
||||||
},
|
"hdpi" : "unpackage/res/icons/72x72.png",
|
||||||
"splashscreen": {
|
"xhdpi" : "unpackage/res/icons/96x96.png",
|
||||||
"waiting": false
|
"xxhdpi" : "unpackage/res/icons/144x144.png",
|
||||||
|
"xxxhdpi" : "unpackage/res/icons/192x192.png"
|
||||||
|
},
|
||||||
|
"ios" : {
|
||||||
|
"appstore" : "unpackage/res/icons/1024x1024.png",
|
||||||
|
"ipad" : {
|
||||||
|
"app" : "unpackage/res/icons/76x76.png",
|
||||||
|
"app@2x" : "unpackage/res/icons/152x152.png",
|
||||||
|
"notification" : "unpackage/res/icons/20x20.png",
|
||||||
|
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||||
|
"proapp@2x" : "unpackage/res/icons/167x167.png",
|
||||||
|
"settings" : "unpackage/res/icons/29x29.png",
|
||||||
|
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||||
|
"spotlight" : "unpackage/res/icons/40x40.png",
|
||||||
|
"spotlight@2x" : "unpackage/res/icons/80x80.png"
|
||||||
|
},
|
||||||
|
"iphone" : {
|
||||||
|
"app@2x" : "unpackage/res/icons/120x120.png",
|
||||||
|
"app@3x" : "unpackage/res/icons/180x180.png",
|
||||||
|
"notification@2x" : "unpackage/res/icons/40x40.png",
|
||||||
|
"notification@3x" : "unpackage/res/icons/60x60.png",
|
||||||
|
"settings@2x" : "unpackage/res/icons/58x58.png",
|
||||||
|
"settings@3x" : "unpackage/res/icons/87x87.png",
|
||||||
|
"spotlight@2x" : "unpackage/res/icons/80x80.png",
|
||||||
|
"spotlight@3x" : "unpackage/res/icons/120x120.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"android" : {
|
||||||
|
"permissions" : [
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||||
|
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
|
||||||
|
"<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" />",
|
||||||
|
"<uses-permission android:name=\"android.permission.BLUETOOTH\" />",
|
||||||
|
"<uses-permission android:name=\"android.permission.BLUETOOTH_SCAN\" />",
|
||||||
|
"<uses-permission android:name=\"android.permission.BLUETOOTH_CONNECT\" />",
|
||||||
|
"<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"/>"
|
||||||
|
],
|
||||||
|
"targetSdkVersion" : 34,
|
||||||
|
"abiFilters" : [ "armeabi-v7a", "arm64-v8a" ]
|
||||||
|
},
|
||||||
|
"ios" : {
|
||||||
|
"dSYMs" : false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"modules" : {
|
||||||
|
"Bluetooth" : {},
|
||||||
|
"VideoPlayer" : {},
|
||||||
|
"Camera" : {},
|
||||||
|
"Record" : {}
|
||||||
|
},
|
||||||
|
"splashscreen" : {
|
||||||
|
"waiting" : false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,6 @@
|
|||||||
>
|
>
|
||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:hasInput="false"
|
:hasInput="false"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
:show="showStartDate"
|
:show="showStartDate"
|
||||||
v-model="defaultStartDate"
|
v-model="defaultStartDate"
|
||||||
@ -101,7 +100,6 @@
|
|||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:hasInput="false"
|
:hasInput="false"
|
||||||
:show="showEndDate"
|
:show="showEndDate"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
v-model="defaultEndDate"
|
v-model="defaultEndDate"
|
||||||
mode="date"
|
mode="date"
|
||||||
@ -113,7 +111,6 @@
|
|||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:hasInput="false"
|
:hasInput="false"
|
||||||
:show="showStartTime"
|
:show="showStartTime"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
v-model="defaultStartTime"
|
v-model="defaultStartTime"
|
||||||
mode="time"
|
mode="time"
|
||||||
@ -125,7 +122,6 @@
|
|||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:hasInput="false"
|
:hasInput="false"
|
||||||
:show="showEndTime"
|
:show="showEndTime"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
v-model="defaultEndTime"
|
v-model="defaultEndTime"
|
||||||
mode="time"
|
mode="time"
|
||||||
|
|||||||
@ -47,7 +47,6 @@
|
|||||||
v-model="defaultStartDate"
|
v-model="defaultStartDate"
|
||||||
mode="datetime"
|
mode="datetime"
|
||||||
closeOnClickOverlay
|
closeOnClickOverlay
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="showStartDate = false"
|
@close="showStartDate = false"
|
||||||
@confirm="confirmDate('start', $event)"
|
@confirm="confirmDate('start', $event)"
|
||||||
@ -59,7 +58,6 @@
|
|||||||
v-model="defaultEndDate"
|
v-model="defaultEndDate"
|
||||||
mode="datetime"
|
mode="datetime"
|
||||||
closeOnClickOverlay
|
closeOnClickOverlay
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="showEndDate = false"
|
@close="showEndDate = false"
|
||||||
@confirm="confirmDate('end', $event)"
|
@confirm="confirmDate('end', $event)"
|
||||||
|
|||||||
@ -44,23 +44,126 @@
|
|||||||
/>
|
/>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
<image
|
<view v-if="!isVideoLoaded">
|
||||||
v-if="!isVideoLoaded"
|
<image
|
||||||
src="https://oss-lock.xhjcn.ltd/mp/background_monitor.png"
|
src="https://oss-lock.xhjcn.ltd/mp/background_monitor.png"
|
||||||
class="w-full h-full absolute top-0 left-0"
|
class="w-full h-full absolute top-0 left-0"
|
||||||
></image>
|
></image>
|
||||||
|
<view
|
||||||
<cover-view
|
class="fixed bottom-[calc(48rpx+env(safe-area-inset-bottom))] w-full flex justify-center"
|
||||||
v-if="(isApp || buttonInfo) && isVideoLoaded"
|
>
|
||||||
:style="{
|
<up-loading-icon
|
||||||
top: isApp ? '60px' : buttonInfo.bottom + 15 + 'px'
|
size="70rpx"
|
||||||
}"
|
:vertical="true"
|
||||||
class="bg-[rgba(0,0,0,0.35)] rounded-full px-4 py-1.5 fixed right-32"
|
textSize="28rpx"
|
||||||
>
|
text="连接中"
|
||||||
<cover-view class="text-white text-xs" @click="showQualitySelector">
|
mode="circle"
|
||||||
{{ range[index].name }}
|
></up-loading-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<cover-view v-if="isVideoLoaded">
|
||||||
|
<cover-view
|
||||||
|
:style="{
|
||||||
|
top: isApp ? '60px' : buttonInfo.bottom + 15 + 'px'
|
||||||
|
}"
|
||||||
|
class="bg-[rgba(0,0,0,0.35)] rounded-full px-4 py-1.5 fixed right-32"
|
||||||
|
>
|
||||||
|
<cover-view class="text-white text-xs" @click="showQualitySelector">
|
||||||
|
{{ range[index].name }}
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view
|
||||||
|
class="fixed bottom-[calc(32rpx+env(safe-area-inset-bottom))] left-1/2 -translate-x-1/2 w-622"
|
||||||
|
>
|
||||||
|
<cover-view
|
||||||
|
class="bg-[rgba(0,0,0,0.3)] rounded-xl p-4 shadow-lg"
|
||||||
|
style="background-color: rgba(0, 0, 0, 0.3)"
|
||||||
|
>
|
||||||
|
<cover-view class="flex items-center justify-around mx-15">
|
||||||
|
<cover-view class="relative">
|
||||||
|
<cover-image
|
||||||
|
v-show="isMute"
|
||||||
|
src="https://oss-lock.xhjcn.ltd/mp/icon_mute.png"
|
||||||
|
:class="isApp ? 'size-48' : 'size-48 p-2'"
|
||||||
|
></cover-image>
|
||||||
|
<cover-image
|
||||||
|
v-show="!isMute"
|
||||||
|
src="https://oss-lock.xhjcn.ltd/mp/icon_not_mute.png"
|
||||||
|
:class="isApp ? 'size-48' : 'size-48 p-2'"
|
||||||
|
></cover-image>
|
||||||
|
<cover-view
|
||||||
|
@click="toggleMute"
|
||||||
|
class="absolute top-0 left-0 w-full h-full size-48 p-2"
|
||||||
|
></cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="relative">
|
||||||
|
<cover-image
|
||||||
|
src="https://oss-lock.xhjcn.ltd/mp/icon_screenshot.png"
|
||||||
|
:class="isApp ? 'size-48' : 'size-48 p-2'"
|
||||||
|
></cover-image>
|
||||||
|
<cover-view
|
||||||
|
@click="handleScreenshot"
|
||||||
|
class="absolute top-0 left-0 w-full h-full"
|
||||||
|
></cover-view>
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="flex items-center justify-between text-white mt-2 px-10">
|
||||||
|
<cover-view class="flex flex-col items-center">
|
||||||
|
<cover-view
|
||||||
|
class="bg-white w-80 h-80 rounded-full flex items-center justify-center relative"
|
||||||
|
>
|
||||||
|
<cover-image
|
||||||
|
:src="
|
||||||
|
isVoice
|
||||||
|
? 'https://oss-lock.xhjcn.ltd/mp/icon_microphone.png'
|
||||||
|
: 'https://oss-lock.xhjcn.ltd/mp/icon_no_microphone.png'
|
||||||
|
"
|
||||||
|
class="w-55 h-55"
|
||||||
|
></cover-image>
|
||||||
|
<cover-view
|
||||||
|
@click="toggleVoice"
|
||||||
|
class="absolute top-0 left-0 w-full h-full"
|
||||||
|
></cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="mt-2 text-center whitespace-nowrap text-xs">{{
|
||||||
|
isVoice ? '点击停止' : '点击说话'
|
||||||
|
}}</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="flex flex-col items-center">
|
||||||
|
<cover-view
|
||||||
|
class="bg-[#eb292b] w-80 h-80 rounded-full flex items-center justify-center relative"
|
||||||
|
>
|
||||||
|
<cover-image
|
||||||
|
src="https://oss-lock.xhjcn.ltd/mp/icon_hang_up.png"
|
||||||
|
class="w-60 h-60"
|
||||||
|
></cover-image>
|
||||||
|
<cover-view
|
||||||
|
@click="handleHangUp"
|
||||||
|
class="absolute top-0 left-0 w-full h-full"
|
||||||
|
></cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="mt-2 text-center whitespace-nowrap text-xs">挂断</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="flex flex-col items-center">
|
||||||
|
<cover-view
|
||||||
|
class="bg-[#63b8af] w-80 h-80 rounded-full flex items-center justify-center relative"
|
||||||
|
>
|
||||||
|
<cover-image
|
||||||
|
src="https://oss-lock.xhjcn.ltd/mp/icon_lock_white.png"
|
||||||
|
class="w-60 h-60"
|
||||||
|
></cover-image>
|
||||||
|
<cover-view
|
||||||
|
@click="handleLock"
|
||||||
|
class="absolute top-0 left-0 w-full h-full"
|
||||||
|
></cover-view>
|
||||||
|
</cover-view>
|
||||||
|
<cover-view class="mt-2 text-center whitespace-nowrap text-xs">开锁</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
|
</cover-view>
|
||||||
</cover-view>
|
</cover-view>
|
||||||
</cover-view>
|
</cover-view>
|
||||||
|
<view class="safe-area-bottom"></view>
|
||||||
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<iot-p2p-voice
|
<iot-p2p-voice
|
||||||
@ -72,105 +175,6 @@
|
|||||||
>
|
>
|
||||||
</iot-p2p-voice>
|
</iot-p2p-voice>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
|
|
||||||
<cover-view
|
|
||||||
v-if="isVideoLoaded"
|
|
||||||
class="fixed bottom-[calc(32rpx+env(safe-area-inset-bottom))] bg-[rgba(0,0,0,0.3)] rounded-xl p-4 shadow-lg left-1/2 -translate-x-1/2 w-622"
|
|
||||||
>
|
|
||||||
<cover-view class="flex items-center justify-around mx-15">
|
|
||||||
<cover-view class="relative">
|
|
||||||
<cover-image
|
|
||||||
v-show="isMute"
|
|
||||||
src="https://oss-lock.xhjcn.ltd/mp/icon_mute.png"
|
|
||||||
:class="isApp ? 'size-48' : 'size-48 p-2'"
|
|
||||||
></cover-image>
|
|
||||||
<cover-image
|
|
||||||
v-show="!isMute"
|
|
||||||
src="https://oss-lock.xhjcn.ltd/mp/icon_not_mute.png"
|
|
||||||
:class="isApp ? 'size-48' : 'size-48 p-2'"
|
|
||||||
></cover-image>
|
|
||||||
<cover-view
|
|
||||||
@click="toggleMute"
|
|
||||||
class="absolute top-0 left-0 w-full h-full size-48 p-2"
|
|
||||||
></cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="relative">
|
|
||||||
<cover-image
|
|
||||||
src="https://oss-lock.xhjcn.ltd/mp/icon_screenshot.png"
|
|
||||||
:class="isApp ? 'size-48' : 'size-48 p-2'"
|
|
||||||
></cover-image>
|
|
||||||
<cover-view
|
|
||||||
@click="handleScreenshot"
|
|
||||||
class="absolute top-0 left-0 w-full h-full"
|
|
||||||
></cover-view>
|
|
||||||
</cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="flex items-center justify-between text-white mt-2 px-10">
|
|
||||||
<cover-view class="flex flex-col items-center">
|
|
||||||
<cover-view
|
|
||||||
class="bg-white w-80 h-80 rounded-full flex items-center justify-center relative"
|
|
||||||
>
|
|
||||||
<cover-image
|
|
||||||
:src="
|
|
||||||
isVoice
|
|
||||||
? 'https://oss-lock.xhjcn.ltd/mp/icon_microphone.png'
|
|
||||||
: 'https://oss-lock.xhjcn.ltd/mp/icon_no_microphone.png'
|
|
||||||
"
|
|
||||||
class="w-55 h-55"
|
|
||||||
></cover-image>
|
|
||||||
<cover-view
|
|
||||||
@click="toggleVoice"
|
|
||||||
class="absolute top-0 left-0 w-full h-full"
|
|
||||||
></cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="mt-2 text-center whitespace-nowrap text-xs">{{
|
|
||||||
isVoice ? '点击停止' : '点击说话'
|
|
||||||
}}</cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="flex flex-col items-center">
|
|
||||||
<cover-view
|
|
||||||
class="bg-[#eb292b] w-80 h-80 rounded-full flex items-center justify-center relative"
|
|
||||||
>
|
|
||||||
<cover-image
|
|
||||||
src="https://oss-lock.xhjcn.ltd/mp/icon_hang_up.png"
|
|
||||||
class="w-60 h-60"
|
|
||||||
></cover-image>
|
|
||||||
<cover-view
|
|
||||||
@click="handleHangUp"
|
|
||||||
class="absolute top-0 left-0 w-full h-full"
|
|
||||||
></cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="mt-2 text-center whitespace-nowrap text-xs">挂断</cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="flex flex-col items-center">
|
|
||||||
<cover-view
|
|
||||||
class="bg-[#63b8af] w-80 h-80 rounded-full flex items-center justify-center relative"
|
|
||||||
>
|
|
||||||
<cover-image
|
|
||||||
src="https://oss-lock.xhjcn.ltd/mp/icon_lock_white.png"
|
|
||||||
class="w-60 h-60"
|
|
||||||
></cover-image>
|
|
||||||
<cover-view
|
|
||||||
@click="handleLock"
|
|
||||||
class="absolute top-0 left-0 w-full h-full"
|
|
||||||
></cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<cover-view class="mt-2 text-center whitespace-nowrap text-xs">开锁</cover-view>
|
|
||||||
</cover-view>
|
|
||||||
</cover-view>
|
|
||||||
</cover-view>
|
|
||||||
<view
|
|
||||||
v-if="!isVideoLoaded"
|
|
||||||
class="fixed bottom-[calc(48rpx+env(safe-area-inset-bottom))] w-full flex justify-center"
|
|
||||||
>
|
|
||||||
<up-loading-icon
|
|
||||||
size="70rpx"
|
|
||||||
:vertical="true"
|
|
||||||
textSize="28rpx"
|
|
||||||
text="连接中"
|
|
||||||
mode="circle"
|
|
||||||
></up-loading-icon>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -250,7 +254,7 @@
|
|||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
|
|
||||||
const cleanupResources = () => {
|
const cleanupResources = async () => {
|
||||||
if (cleanupCalled.value) return
|
if (cleanupCalled.value) return
|
||||||
cleanupCalled.value = true
|
cleanupCalled.value = true
|
||||||
|
|
||||||
@ -263,8 +267,8 @@
|
|||||||
}
|
}
|
||||||
releaseRecord()
|
releaseRecord()
|
||||||
if (deviceInfo.value) {
|
if (deviceInfo.value) {
|
||||||
stopSendServiceFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
await stopSendServiceFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
||||||
stopServiceFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
await stopServiceFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`)
|
||||||
}
|
}
|
||||||
// #endif
|
// #endif
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
@ -545,8 +549,15 @@
|
|||||||
// #endif
|
// #endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const callback = audioData => {
|
const callback = async audioData => {
|
||||||
dataSendFunction(`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`, audioData)
|
const result = await dataSendFunction(
|
||||||
|
`${deviceInfo.value.productId}/${deviceInfo.value.deviceName}`,
|
||||||
|
audioData
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
`数据传输结果:${result?.data?.result ? result?.data?.result : result?.data?.dynamicJSONFields?.result}`,
|
||||||
|
audioData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePlaySuccess = () => {
|
const handlePlaySuccess = () => {
|
||||||
|
|||||||
@ -51,7 +51,6 @@
|
|||||||
:defaultIndex="picker"
|
:defaultIndex="picker"
|
||||||
title="选择时间"
|
title="选择时间"
|
||||||
keyName="name"
|
keyName="name"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="show = false"
|
@close="show = false"
|
||||||
@cancel="show = false"
|
@cancel="show = false"
|
||||||
|
|||||||
@ -68,7 +68,6 @@
|
|||||||
:show="showRecordingTime"
|
:show="showRecordingTime"
|
||||||
:columns="recordingTimeList"
|
:columns="recordingTimeList"
|
||||||
keyName="name"
|
keyName="name"
|
||||||
:itemHeight="70"
|
|
||||||
title="有人出现时录像"
|
title="有人出现时录像"
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
:defaultIndex="[recordingTimeIndex]"
|
:defaultIndex="[recordingTimeIndex]"
|
||||||
@ -80,7 +79,6 @@
|
|||||||
:show="showDetectionDistance"
|
:show="showDetectionDistance"
|
||||||
:columns="detectionDistanceList"
|
:columns="detectionDistanceList"
|
||||||
title="人体侦测距离"
|
title="人体侦测距离"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
keyName="name"
|
keyName="name"
|
||||||
:defaultIndex="[detectionDistanceIndex]"
|
:defaultIndex="[detectionDistanceIndex]"
|
||||||
|
|||||||
@ -61,7 +61,6 @@
|
|||||||
:defaultIndex="[distanceIndex]"
|
:defaultIndex="[distanceIndex]"
|
||||||
title="感应距离"
|
title="感应距离"
|
||||||
keyName="name"
|
keyName="name"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="showDistance = false"
|
@close="showDistance = false"
|
||||||
@cancel="showDistance = false"
|
@cancel="showDistance = false"
|
||||||
@ -73,7 +72,6 @@
|
|||||||
:defaultIndex="[enErrUnlock]"
|
:defaultIndex="[enErrUnlock]"
|
||||||
title="防误开"
|
title="防误开"
|
||||||
keyName="name"
|
keyName="name"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="showEnErrUnlock = false"
|
@close="showEnErrUnlock = false"
|
||||||
@cancel="showEnErrUnlock = false"
|
@cancel="showEnErrUnlock = false"
|
||||||
|
|||||||
@ -267,6 +267,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (data[i].openLockType === 6) {
|
||||||
|
for (let j = 0; j < data[i].keys.length; j++) {
|
||||||
|
data[i].keys[j].openLockType = data[i].openLockType
|
||||||
|
data[i].keys[j].name = data[i].keys[j].palmVeinName
|
||||||
|
data[i].keys[j].id = data[i].keys[j].palmVeinId
|
||||||
|
if (data[i].keys[j].palmVeinType === 1) {
|
||||||
|
data[i].keys[j].timeText =
|
||||||
|
timeFormat(data[i].keys[j].createDate, 'yyyy-mm-dd hh:MM') + ' 永久'
|
||||||
|
} else if (data[i].keys[j].palmVeinType === 2) {
|
||||||
|
data[i].keys[j].timeText =
|
||||||
|
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd hh:MM') +
|
||||||
|
' - ' +
|
||||||
|
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd hh:MM') +
|
||||||
|
' 限时'
|
||||||
|
} else {
|
||||||
|
data[i].keys[j].timeText =
|
||||||
|
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd') +
|
||||||
|
' - ' +
|
||||||
|
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd') +
|
||||||
|
' 循环'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data[i].openLockType === 7) {
|
||||||
|
for (let j = 0; j < data[i].keys.length; j++) {
|
||||||
|
data[i].keys[j].openLockType = data[i].openLockType
|
||||||
|
data[i].keys[j].name = data[i].keys[j].remoteName
|
||||||
|
data[i].keys[j].id = data[i].keys[j].remoteId
|
||||||
|
if (data[i].keys[j].remoteType === 1) {
|
||||||
|
data[i].keys[j].timeText =
|
||||||
|
timeFormat(data[i].keys[j].createDate, 'yyyy-mm-dd hh:MM') + ' 永久'
|
||||||
|
} else if (data[i].keys[j].remoteType === 2) {
|
||||||
|
data[i].keys[j].timeText =
|
||||||
|
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd hh:MM') +
|
||||||
|
' - ' +
|
||||||
|
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd hh:MM') +
|
||||||
|
' 限时'
|
||||||
|
} else {
|
||||||
|
data[i].keys[j].timeText =
|
||||||
|
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd') +
|
||||||
|
' - ' +
|
||||||
|
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd') +
|
||||||
|
' 循环'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
result.push(...data[i].keys)
|
result.push(...data[i].keys)
|
||||||
}
|
}
|
||||||
console.log(result)
|
console.log(result)
|
||||||
|
|||||||
@ -61,7 +61,6 @@
|
|||||||
<up-picker
|
<up-picker
|
||||||
:show="show"
|
:show="show"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
:defaultIndex="[value - 1]"
|
:defaultIndex="[value - 1]"
|
||||||
@close="show = false"
|
@close="show = false"
|
||||||
|
|||||||
@ -83,7 +83,6 @@
|
|||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:hasInput="false"
|
:hasInput="false"
|
||||||
:show="showStartTime"
|
:show="showStartTime"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
v-model="defaultStartTime"
|
v-model="defaultStartTime"
|
||||||
mode="time"
|
mode="time"
|
||||||
@ -95,7 +94,6 @@
|
|||||||
<up-datetime-picker
|
<up-datetime-picker
|
||||||
:hasInput="false"
|
:hasInput="false"
|
||||||
:show="showEndTime"
|
:show="showEndTime"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
v-model="defaultEndTime"
|
v-model="defaultEndTime"
|
||||||
mode="time"
|
mode="time"
|
||||||
|
|||||||
@ -446,6 +446,10 @@
|
|||||||
pending.value = false
|
pending.value = false
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
|
if (key === 'appUnlockOnline') {
|
||||||
|
$bluetooth.currentLockInfo.appUnlockOnline =
|
||||||
|
$bluetooth.currentLockSetting.lockSettingInfo[key] === 1 ? 0 : 1
|
||||||
|
}
|
||||||
$bluetooth.updateCurrentLockSetting({
|
$bluetooth.updateCurrentLockSetting({
|
||||||
...$bluetooth.currentLockSetting,
|
...$bluetooth.currentLockSetting,
|
||||||
lockSettingInfo: {
|
lockSettingInfo: {
|
||||||
|
|||||||
@ -51,7 +51,6 @@
|
|||||||
uid: $user.userInfo.uid.toString(),
|
uid: $user.userInfo.uid.toString(),
|
||||||
countReq: 10
|
countReq: 10
|
||||||
})
|
})
|
||||||
console.log('code value :', code)
|
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
if (typeList[progress.value] === 'setting') {
|
if (typeList[progress.value] === 'setting') {
|
||||||
const { code: resultCode } = await lockDataUploadRequest({
|
const { code: resultCode } = await lockDataUploadRequest({
|
||||||
@ -59,13 +58,12 @@
|
|||||||
uploadType: 1,
|
uploadType: 1,
|
||||||
records: data.list
|
records: data.list
|
||||||
})
|
})
|
||||||
uni.hideLoading()
|
|
||||||
pending.value = false
|
|
||||||
$bluetooth.closeBluetoothConnection()
|
$bluetooth.closeBluetoothConnection()
|
||||||
if (resultCode === 0) {
|
if (resultCode === 0) {
|
||||||
const { code, data } = await getLockSettingRequest({
|
const { code, data } = await getLockSettingRequest({
|
||||||
lockId: $bluetooth.currentLockInfo.lockId
|
lockId: $bluetooth.currentLockInfo.lockId
|
||||||
})
|
})
|
||||||
|
pending.value = false
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
if (data.lockSettingInfo.catEyeConfig[0]) {
|
if (data.lockSettingInfo.catEyeConfig[0]) {
|
||||||
data.lockSettingInfo.catEyeConfig[0].catEyeModeConfig.recordTime = Number(
|
data.lockSettingInfo.catEyeConfig[0].catEyeModeConfig.recordTime = Number(
|
||||||
@ -77,19 +75,31 @@
|
|||||||
}
|
}
|
||||||
$bluetooth.updateCurrentLockSetting(data)
|
$bluetooth.updateCurrentLockSetting(data)
|
||||||
}
|
}
|
||||||
|
uni.hideLoading()
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '上传成功',
|
title: '上传成功',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
pending.value = false
|
||||||
|
uni.hideLoading()
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '上传失败,请重试',
|
title: '上传失败,请重试',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
progress.value = 0
|
||||||
|
page.value = 0
|
||||||
|
list.value = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
list.value = list.value.concat(data.list)
|
list.value = list.value.concat(data.list)
|
||||||
|
if (list.value.length === 0) {
|
||||||
|
page.value = 0
|
||||||
|
list.value = []
|
||||||
|
progress.value++
|
||||||
|
return await asyncData(true)
|
||||||
|
}
|
||||||
if (data.size === 10) {
|
if (data.size === 10) {
|
||||||
page.value++
|
page.value++
|
||||||
} else {
|
} else {
|
||||||
@ -107,6 +117,9 @@
|
|||||||
title: '上传失败,请重试',
|
title: '上传失败,请重试',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
|
progress.value = 0
|
||||||
|
page.value = 0
|
||||||
|
list.value = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
page.value = 0
|
page.value = 0
|
||||||
@ -118,6 +131,9 @@
|
|||||||
pending.value = false
|
pending.value = false
|
||||||
$bluetooth.closeBluetoothConnection()
|
$bluetooth.closeBluetoothConnection()
|
||||||
uni.hideLoading()
|
uni.hideLoading()
|
||||||
|
progress.value = 0
|
||||||
|
page.value = 0
|
||||||
|
list.value = []
|
||||||
} else {
|
} else {
|
||||||
pending.value = false
|
pending.value = false
|
||||||
$bluetooth.closeBluetoothConnection()
|
$bluetooth.closeBluetoothConnection()
|
||||||
@ -126,6 +142,9 @@
|
|||||||
title: '上传失败,请保持在锁附近',
|
title: '上传失败,请保持在锁附近',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
})
|
})
|
||||||
|
progress.value = 0
|
||||||
|
page.value = 0
|
||||||
|
list.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -58,7 +58,6 @@
|
|||||||
title="开始时间"
|
title="开始时间"
|
||||||
mode="time"
|
mode="time"
|
||||||
:defaultIndex="startIndex"
|
:defaultIndex="startIndex"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="showStartTime = false"
|
@close="showStartTime = false"
|
||||||
@cancel="showStartTime = false"
|
@cancel="showStartTime = false"
|
||||||
@ -70,7 +69,6 @@
|
|||||||
title="结束时间"
|
title="结束时间"
|
||||||
mode="time"
|
mode="time"
|
||||||
:defaultIndex="endIndex"
|
:defaultIndex="endIndex"
|
||||||
:itemHeight="70"
|
|
||||||
:visibleItemCount="5"
|
:visibleItemCount="5"
|
||||||
@close="showEndTime = false"
|
@close="showEndTime = false"
|
||||||
@cancel="showEndTime = false"
|
@cancel="showEndTime = false"
|
||||||
|
|||||||
@ -94,10 +94,10 @@
|
|||||||
abbreviation: 'CN',
|
abbreviation: 'CN',
|
||||||
group: 'Z'
|
group: 'Z'
|
||||||
})
|
})
|
||||||
const username = ref('')
|
const username = ref('18174429647')
|
||||||
const password = ref('')
|
const password = ref('..022059')
|
||||||
|
|
||||||
const select = ref(false)
|
const select = ref(true)
|
||||||
|
|
||||||
const pending = ref(false)
|
const pending = ref(false)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<view class="tips">找回密码和登录新设备时,可通过绑定的邮箱验证</view>
|
<view class="tips">在APP上找回密码和登录新设备时,可通过绑定的邮箱验证</view>
|
||||||
<input
|
<input
|
||||||
class="input-email"
|
class="input-email"
|
||||||
:value="email"
|
:value="email"
|
||||||
|
|||||||
@ -719,7 +719,6 @@ export const useBluetoothStore = defineStore('ble', {
|
|||||||
list: decrypted.slice(9, length)
|
list: decrypted.slice(9, length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(1234, cmdId, data)
|
|
||||||
characteristicValueCallback({
|
characteristicValueCallback({
|
||||||
code: decrypted[2],
|
code: decrypted[2],
|
||||||
data
|
data
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"minSdkVersion": "21"
|
"minSdkVersion": "21",
|
||||||
|
"dependencies": ["com.tencent.iot.thirdparty.android:media-server:1.0.7"]
|
||||||
}
|
}
|
||||||
@ -5,51 +5,76 @@ import 'android.media.AudioRecord'
|
|||||||
import 'android.media.MediaRecorder'
|
import 'android.media.MediaRecorder'
|
||||||
import 'android.media.AudioFormat'
|
import 'android.media.AudioFormat'
|
||||||
import 'android.media.MediaSyncEvent'
|
import 'android.media.MediaSyncEvent'
|
||||||
|
import 'android.media.MediaCodec'
|
||||||
|
import 'android.media.MediaCodecInfo'
|
||||||
|
import 'android.media.MediaFormat'
|
||||||
import 'java.lang.Thread'
|
import 'java.lang.Thread'
|
||||||
import { UTSAndroid } from 'io.dcloud.uts'
|
import { UTSAndroid } from 'io.dcloud.uts'
|
||||||
import { Result } from '../interface.uts'
|
import { Result } from '../interface.uts'
|
||||||
|
import { FLVPacker, FLVListener } from 'com.tencent.iot.thirdparty.flv'
|
||||||
|
|
||||||
let recorder: AudioRecord | null = null
|
let recorder: AudioRecord | null = null
|
||||||
let recorderState: boolean = false
|
let aacEncoder: MediaCodec | null = null
|
||||||
|
let flvPacker: FLVPacker | null = null
|
||||||
|
let recordThread: Thread | null = null
|
||||||
|
let isRecording: boolean = false
|
||||||
let bufferSizeInBytes: Int = 0
|
let bufferSizeInBytes: Int = 0
|
||||||
|
|
||||||
|
class MyFLVListener extends FLVListener {
|
||||||
|
private callback: (data: Array<number>) => void
|
||||||
|
|
||||||
|
constructor(callback: (data: Array<number>) => void) {
|
||||||
|
super()
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
override onFLV(data: ByteArray) {
|
||||||
|
if (isRecording) {
|
||||||
|
const numberArray: Array<number> = []
|
||||||
|
for (let i: Int = 0; i < data.size; i++) {
|
||||||
|
numberArray.push(data[i].toInt())
|
||||||
|
}
|
||||||
|
UTSAndroid.getUniActivity()?.runOnUiThread(() => {
|
||||||
|
this.callback(numberArray)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addADTStoPacket(packet: ByteArray, packetLen: Int) {
|
||||||
|
const profile = 2
|
||||||
|
const freqIdx = 8
|
||||||
|
const chanCfg = 1
|
||||||
|
|
||||||
|
packet[0] = (0xff).toByte()
|
||||||
|
packet[1] = (0xf9).toByte()
|
||||||
|
packet[2] = (((profile - 1) << 6) | (freqIdx << 2) | (chanCfg >> 2)).toByte()
|
||||||
|
packet[3] = (((chanCfg & 3) << 6) | (packetLen >> 11)).toByte()
|
||||||
|
packet[4] = ((packetLen & 0x7ff) >> 3).toByte()
|
||||||
|
packet[5] = (((packetLen & 7) << 5) | 0x1f).toByte()
|
||||||
|
packet[6] = (0xfc).toByte()
|
||||||
|
}
|
||||||
|
|
||||||
export const requestPermission = async function (): Promise<Result> {
|
export const requestPermission = async function (): Promise<Result> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
try {
|
try {
|
||||||
let permissionNeed = ['android.permission.RECORD_AUDIO']
|
const permissionNeed = ['android.permission.RECORD_AUDIO']
|
||||||
|
|
||||||
UTSAndroid.requestSystemPermission(
|
UTSAndroid.requestSystemPermission(
|
||||||
UTSAndroid.getUniActivity()!,
|
UTSAndroid.getUniActivity()!,
|
||||||
permissionNeed,
|
permissionNeed,
|
||||||
function (allRight: boolean, _: string[]) {
|
(allRight: boolean, _: string[]) => {
|
||||||
if (allRight) {
|
if (allRight) {
|
||||||
resolve({
|
resolve({ code: 0, data: {}, message: '成功' })
|
||||||
code: 0,
|
|
||||||
data: {},
|
|
||||||
message: '成功'
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
resolve({
|
resolve({ code: -1, data: {}, message: '失败' })
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: '失败'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function (_: boolean, _: string[]) {
|
(_: boolean, _: string[]) => {
|
||||||
resolve({
|
resolve({ code: -1, data: {}, message: '失败' })
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: '失败'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
resolve({
|
resolve({ code: -1, data: {}, message: error.toString() })
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: error.toString()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -57,7 +82,7 @@ export const requestPermission = async function (): Promise<Result> {
|
|||||||
export const initAudio = async function (): Promise<Result> {
|
export const initAudio = async function (): Promise<Result> {
|
||||||
try {
|
try {
|
||||||
const audioSource = MediaRecorder.AudioSource.MIC
|
const audioSource = MediaRecorder.AudioSource.MIC
|
||||||
const sampleRateInHz = 44100
|
const sampleRateInHz = 16000
|
||||||
const channelConfig = AudioFormat.CHANNEL_IN_MONO
|
const channelConfig = AudioFormat.CHANNEL_IN_MONO
|
||||||
const audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
const audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
||||||
|
|
||||||
@ -67,8 +92,6 @@ export const initAudio = async function (): Promise<Result> {
|
|||||||
audioFormat
|
audioFormat
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('bufferSizeInBytes', bufferSizeInBytes)
|
|
||||||
|
|
||||||
recorder = new AudioRecord(
|
recorder = new AudioRecord(
|
||||||
audioSource,
|
audioSource,
|
||||||
sampleRateInHz.toInt(),
|
sampleRateInHz.toInt(),
|
||||||
@ -77,133 +100,163 @@ export const initAudio = async function (): Promise<Result> {
|
|||||||
bufferSizeInBytes
|
bufferSizeInBytes
|
||||||
)
|
)
|
||||||
|
|
||||||
const currentRecorder = recorder
|
if (recorder?.getState() == AudioRecord.STATE_INITIALIZED) {
|
||||||
if (currentRecorder !== null) {
|
return { code: 0, data: {}, message: '成功' }
|
||||||
console.log('初始化录音结果:', currentRecorder.getState())
|
} else {
|
||||||
if (currentRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
|
return { code: -1, data: {}, message: '初始化录音失败' }
|
||||||
return {
|
|
||||||
code: 0,
|
|
||||||
data: {},
|
|
||||||
message: '成功'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: '初始化录音失败'
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('初始化录音失败', error)
|
return { code: -1, data: {}, message: error.toString() }
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: error.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function onStartRecord(callback: (data: Array) => void): Promise<Result> {
|
export function onStartRecord(callback: (data: Array<number>) => void) {
|
||||||
try {
|
stopRecord()
|
||||||
const callbackFunction = () => {
|
.then(() => {
|
||||||
const currentRecorder = recorder
|
const currentRecorder = recorder
|
||||||
if (currentRecorder !== null) {
|
if (currentRecorder == null) {
|
||||||
while (recorderState) {
|
return
|
||||||
let audioData = new ByteArray(bufferSizeInBytes)
|
}
|
||||||
const result: Int = currentRecorder.read(audioData, 0, bufferSizeInBytes)
|
|
||||||
if (result > 0) {
|
const listener = new MyFLVListener(callback)
|
||||||
callback(Array.fromNative(audioData))
|
flvPacker = new FLVPacker(listener, true, false)
|
||||||
console.log('录音按帧返回数据', Array.fromNative(audioData))
|
|
||||||
|
const sampleRateInHz = 16000
|
||||||
|
const channelCount = 1
|
||||||
|
const bitRate = 96000
|
||||||
|
aacEncoder = MediaCodec.createEncoderByType('audio/mp4a-latm')
|
||||||
|
const mediaFormat = MediaFormat.createAudioFormat(
|
||||||
|
'audio/mp4a-latm',
|
||||||
|
sampleRateInHz.toInt(),
|
||||||
|
channelCount.toInt()
|
||||||
|
)
|
||||||
|
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate.toInt())
|
||||||
|
mediaFormat.setInteger(
|
||||||
|
MediaFormat.KEY_AAC_PROFILE,
|
||||||
|
MediaCodecInfo.CodecProfileLevel.AACObjectLC
|
||||||
|
)
|
||||||
|
mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1024 * 1024)
|
||||||
|
aacEncoder!!.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
|
||||||
|
|
||||||
|
recordThread = new Thread(() => {
|
||||||
|
try {
|
||||||
|
if (aacEncoder == null || currentRecorder == null || flvPacker == null) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
Thread.sleep(10)
|
|
||||||
|
currentRecorder.startRecording()
|
||||||
|
aacEncoder!!.start()
|
||||||
|
isRecording = true
|
||||||
|
|
||||||
|
const audioInfo = new MediaCodec.BufferInfo()
|
||||||
|
while (isRecording) {
|
||||||
|
const inputBufferId = aacEncoder!!.dequeueInputBuffer(10000)
|
||||||
|
if (inputBufferId >= 0) {
|
||||||
|
const inputBuffer = aacEncoder!!.getInputBuffer(inputBufferId)!!
|
||||||
|
const readSize = currentRecorder.read(inputBuffer, bufferSizeInBytes)
|
||||||
|
if (readSize > 0 && isRecording) {
|
||||||
|
aacEncoder!!.queueInputBuffer(
|
||||||
|
inputBufferId,
|
||||||
|
0,
|
||||||
|
readSize,
|
||||||
|
(Date.now() * 1000).toLong(),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var outputBufferId = aacEncoder!!.dequeueOutputBuffer(audioInfo, 10000)
|
||||||
|
while (outputBufferId >= 0 && isRecording) {
|
||||||
|
const outputBuffer = aacEncoder!!.getOutputBuffer(outputBufferId)!!
|
||||||
|
if (audioInfo.size > 0 && flvPacker != null) {
|
||||||
|
const outDataSize = audioInfo.size
|
||||||
|
const aacPacketWithAdts = ByteArray(outDataSize + 7)
|
||||||
|
|
||||||
|
addADTStoPacket(aacPacketWithAdts, outDataSize + 7)
|
||||||
|
|
||||||
|
outputBuffer.position(audioInfo.offset)
|
||||||
|
outputBuffer.limit(audioInfo.offset + outDataSize)
|
||||||
|
outputBuffer.get(aacPacketWithAdts, 7, outDataSize)
|
||||||
|
|
||||||
|
if (flvPacker != null && isRecording) {
|
||||||
|
flvPacker!!.encodeFlv(
|
||||||
|
aacPacketWithAdts,
|
||||||
|
FLVPacker.TYPE_AUDIO,
|
||||||
|
Date.now().toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
aacEncoder!!.releaseOutputBuffer(outputBufferId, false)
|
||||||
|
outputBufferId = aacEncoder!!.dequeueOutputBuffer(audioInfo, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Record thread error: ' + error.toString())
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
currentRecorder?.stop()
|
||||||
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
aacEncoder?.stop()
|
||||||
|
aacEncoder?.release()
|
||||||
|
} catch (e) {}
|
||||||
|
try {
|
||||||
|
flvPacker?.release()
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
aacEncoder = null
|
||||||
|
flvPacker = null
|
||||||
|
isRecording = false
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
return
|
recordThread!!.start()
|
||||||
}
|
})
|
||||||
|
.catch(error => {
|
||||||
const currentRecorder = recorder
|
console.log('Error in stopRecord(): ' + error.toString())
|
||||||
if (currentRecorder !== null) {
|
})
|
||||||
currentRecorder.startRecording()
|
|
||||||
console.log('开始录音')
|
|
||||||
recorderState = true
|
|
||||||
|
|
||||||
callbackFunction()
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 0,
|
|
||||||
data: {},
|
|
||||||
message: '成功'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: '开始录音失败'
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log('开始录音失败', error)
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: error.toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const stopRecord = async function (): Promise<Result> {
|
export const stopRecord = async function (): Promise<Result> {
|
||||||
try {
|
try {
|
||||||
const currentRecorder = recorder
|
if (isRecording) {
|
||||||
if (currentRecorder !== null) {
|
isRecording = false
|
||||||
currentRecorder.stop()
|
|
||||||
recorderState = false
|
if (recordThread != null) {
|
||||||
console.log('停止录音')
|
try {
|
||||||
return {
|
recordThread!!.join(1000)
|
||||||
code: 0,
|
} catch (e) {
|
||||||
data: {},
|
console.log('recordThread.join error: ' + e.toString())
|
||||||
message: '成功'
|
}
|
||||||
|
recordThread = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { code: 0, data: {}, message: '成功' }
|
||||||
}
|
}
|
||||||
return {
|
return { code: 0, data: {}, message: '录音未开始' }
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: '停止录音失败'
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('停止录音失败', error)
|
isRecording = false
|
||||||
recorderState = false
|
recordThread = null
|
||||||
return {
|
return { code: -1, data: {}, message: error.toString() }
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: error.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const releaseRecord = async function (): Promise<Result> {
|
export const releaseRecord = async function (): Promise<Result> {
|
||||||
try {
|
try {
|
||||||
|
await stopRecord()
|
||||||
|
|
||||||
const currentRecorder = recorder
|
const currentRecorder = recorder
|
||||||
if (currentRecorder !== null) {
|
if (currentRecorder != null) {
|
||||||
currentRecorder.release()
|
try {
|
||||||
console.log('释放录音')
|
if (currentRecorder.getState() == AudioRecord.STATE_INITIALIZED) {
|
||||||
return {
|
currentRecorder.release()
|
||||||
code: 0,
|
}
|
||||||
data: {},
|
} catch (e) {}
|
||||||
message: '成功'
|
recorder = null
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: '释放录音失败'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { code: 0, data: {}, message: '成功' }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('释放录音失败', error)
|
return { code: -1, data: {}, message: error.toString() }
|
||||||
return {
|
|
||||||
code: -1,
|
|
||||||
data: {},
|
|
||||||
message: error.toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore-end
|
// @ts-ignore-end
|
||||||
|
|||||||
@ -1,236 +1,398 @@
|
|||||||
import Foundation
|
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import CoreMedia
|
||||||
|
import Foundation
|
||||||
|
|
||||||
@objc
|
@objc(RecordPermission)
|
||||||
public class RecordPermission: NSObject {
|
public class RecordPermission: NSObject {
|
||||||
@objc
|
@objc
|
||||||
public static func requestRecordPermission(_ completion: @escaping (Bool) -> Void) {
|
public static func requestRecordPermission(_ callback: @escaping (Bool) -> Void) {
|
||||||
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
||||||
completion(granted)
|
callback(granted)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc(AudioRecorderManager)
|
||||||
public class AudioRecorderManager: NSObject {
|
public class AudioRecorderManager: NSObject {
|
||||||
|
@objc public static let shared = AudioRecorderManager()
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
case idle, recording, stopped
|
||||||
|
}
|
||||||
|
|
||||||
|
private var state: State = .idle
|
||||||
|
private let processingQueue = DispatchQueue(label: "com.xhjcn.lock.lite.audio.processing.queue")
|
||||||
|
|
||||||
|
private var recordingCallback: ((Data?, Bool, String) -> Void)?
|
||||||
|
|
||||||
private var audioEngine: AVAudioEngine?
|
private var audioEngine: AVAudioEngine?
|
||||||
private var audioConverter: AVAudioConverter?
|
private var audioConverter: AVAudioConverter?
|
||||||
private var aacBuffer: AVAudioCompressedBuffer?
|
private var outputBuffer: AVAudioCompressedBuffer?
|
||||||
|
private var flvMuxer: FLVMuxer?
|
||||||
|
|
||||||
private let lock = NSLock()
|
@objc
|
||||||
private var _isRecording = false
|
public func initAudio(_ callback: @escaping (Bool, String) -> Void) {
|
||||||
var isRecording: Bool {
|
callback(true, "init success")
|
||||||
get {
|
}
|
||||||
lock.lock()
|
|
||||||
defer { lock.unlock() }
|
@objc
|
||||||
return _isRecording
|
public func startRecord(_ callback: @escaping (Data?, Bool, String) -> Void) {
|
||||||
}
|
processingQueue.async { [weak self] in
|
||||||
set {
|
guard let self = self else { return }
|
||||||
lock.lock()
|
|
||||||
_isRecording = newValue
|
guard self.state == .idle || self.state == .stopped else {
|
||||||
lock.unlock()
|
self.dispatchError(callback: callback, message: "Recording is already in progress.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.recordingCallback = callback
|
||||||
|
|
||||||
|
if AVAudioSession.sharedInstance().recordPermission != .granted {
|
||||||
|
self.dispatchError(callback: callback, message: "Microphone permission not granted.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try self.setupAudioSession()
|
||||||
|
try self.setupAudioEngine()
|
||||||
|
|
||||||
|
guard let converter = self.audioConverter else {
|
||||||
|
self.cleanUp()
|
||||||
|
self.dispatchError(callback: callback, message: "Failed to setup audio engine components.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.flvMuxer = FLVMuxer(sampleRate: converter.outputFormat.sampleRate, channels: Double(converter.outputFormat.channelCount))
|
||||||
|
|
||||||
|
var initialData = Data()
|
||||||
|
|
||||||
|
let header = self.flvMuxer!.getHeader()
|
||||||
|
initialData.append(header)
|
||||||
|
|
||||||
|
let metaTag = self.flvMuxer!.getMetaTag()
|
||||||
|
initialData.append(metaTag)
|
||||||
|
|
||||||
|
let audioConfigTag = self.flvMuxer!.getAudioSpecificConfigTag(config: nil)
|
||||||
|
initialData.append(audioConfigTag)
|
||||||
|
|
||||||
|
let inputNode = self.audioEngine!.inputNode
|
||||||
|
let inputFormat = inputNode.outputFormat(forBus: 0)
|
||||||
|
inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { [weak self] (buffer, _) in
|
||||||
|
self?.processAudioBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.dispatchData(initialData)
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||||
|
self.processingQueue.async { [weak self] in
|
||||||
|
guard let self = self, let engine = self.audioEngine else { return }
|
||||||
|
do {
|
||||||
|
engine.prepare()
|
||||||
|
try engine.start()
|
||||||
|
self.state = .recording
|
||||||
|
self.dispatchSuccess(message: "Recording started successfully.")
|
||||||
|
} catch {
|
||||||
|
self.cleanUp()
|
||||||
|
self.dispatchError(callback: self.recordingCallback ?? {_,_,_ in }, message: "Failed to start engine: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
self.cleanUp()
|
||||||
|
self.dispatchError(callback: callback, message: "Failed to start recording: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public static let shared = AudioRecorderManager()
|
public func stopRecord(_ callback: @escaping (Bool, String, String) -> Void) {
|
||||||
|
processingQueue.async { [weak self] in
|
||||||
private override init() {}
|
guard let self = self else { return }
|
||||||
|
guard self.state == .recording else {
|
||||||
@objc
|
callback(false, "Not recording.", "")
|
||||||
public func initAudio(_ completion: @escaping (Bool, String) -> Void) {
|
return
|
||||||
completion(true, "Module initialized")
|
}
|
||||||
|
self.cleanUp()
|
||||||
|
self.state = .stopped
|
||||||
|
callback(true, "Recording stopped.", "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
@objc
|
||||||
public func startRecord(_ completion: @escaping (Data?, Bool, String) -> Void) {
|
public func releaseRecord(_ callback: @escaping (Bool, String) -> Void) {
|
||||||
if self.isRecording {
|
processingQueue.async { [weak self] in
|
||||||
completion(nil, false, "Recording is already in progress.")
|
guard let self = self else { return }
|
||||||
return
|
self.cleanUp()
|
||||||
|
self.state = .idle
|
||||||
|
callback(true, "Record released.")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let session = AVAudioSession.sharedInstance()
|
private func setupAudioSession() throws {
|
||||||
do {
|
let audioSession = AVAudioSession.sharedInstance()
|
||||||
try session.setCategory(.playAndRecord, mode: .default, options: .defaultToSpeaker)
|
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.defaultToSpeaker, .allowBluetooth])
|
||||||
try session.setPreferredSampleRate(16000.0)
|
try audioSession.setActive(true)
|
||||||
try session.setPreferredInputNumberOfChannels(1)
|
}
|
||||||
try session.setActive(true)
|
|
||||||
} catch {
|
|
||||||
completion(nil, false, "Failed to set up audio session: \(error.localizedDescription)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
private func setupAudioEngine() throws {
|
||||||
audioEngine = AVAudioEngine()
|
audioEngine = AVAudioEngine()
|
||||||
guard let audioEngine = audioEngine else {
|
guard let engine = audioEngine else {
|
||||||
completion(nil, false, "Failed to create audio engine")
|
throw NSError(domain: "AudioRecorderManager", code: -1, userInfo: [NSLocalizedDescriptionKey: "Failed to create AVAudioEngine"])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputNode = audioEngine.inputNode
|
let inputNode = engine.inputNode
|
||||||
let inputFormat = inputNode.outputFormat(forBus: 0)
|
let inputFormat = inputNode.outputFormat(forBus: 0)
|
||||||
|
|
||||||
var outputFormatDescription = AudioStreamBasicDescription(
|
guard inputFormat.sampleRate > 0 else {
|
||||||
mSampleRate: 16000.0,
|
throw NSError(domain: "AudioRecorderManager", code: -2, userInfo: [NSLocalizedDescriptionKey: "Input node has an invalid sample rate."])
|
||||||
mFormatID: kAudioFormatMPEG4AAC,
|
}
|
||||||
mFormatFlags: 2,
|
|
||||||
mBytesPerPacket: 0,
|
|
||||||
mFramesPerPacket: 1024,
|
|
||||||
mBytesPerFrame: 0,
|
|
||||||
mChannelsPerFrame: 1,
|
|
||||||
mBitsPerChannel: 0,
|
|
||||||
mReserved: 0
|
|
||||||
)
|
|
||||||
|
|
||||||
guard let outputFormat = AVAudioFormat(streamDescription: &outputFormatDescription) else {
|
let outputFormatSettings: [String: Any] = [
|
||||||
completion(nil, false, "Failed to create output audio format")
|
AVFormatIDKey: kAudioFormatMPEG4AAC,
|
||||||
return
|
AVSampleRateKey: 16000,
|
||||||
|
AVNumberOfChannelsKey: inputFormat.channelCount,
|
||||||
|
AVEncoderBitRateKey: 48000,
|
||||||
|
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
|
||||||
|
]
|
||||||
|
|
||||||
|
guard let outputFormat = AVAudioFormat(settings: outputFormatSettings) else {
|
||||||
|
throw NSError(domain: "AudioRecorderManager", code: -3, userInfo: [NSLocalizedDescriptionKey: "Failed to create output format."])
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else {
|
guard let converter = AVAudioConverter(from: inputFormat, to: outputFormat) else {
|
||||||
completion(nil, false, "Failed to create audio converter")
|
throw NSError(domain: "AudioRecorderManager", code: -4, userInfo: [NSLocalizedDescriptionKey: "Failed to create audio converter."])
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
converter.bitRate = 48000
|
||||||
|
converter.bitRateStrategy = AVAudioBitRateStrategy_Constant
|
||||||
|
|
||||||
self.audioConverter = converter
|
self.audioConverter = converter
|
||||||
|
|
||||||
self.aacBuffer = AVAudioCompressedBuffer(
|
self.outputBuffer = AVAudioCompressedBuffer(
|
||||||
format: outputFormat,
|
format: converter.outputFormat,
|
||||||
packetCapacity: 1,
|
packetCapacity: 1,
|
||||||
maximumPacketSize: converter.maximumOutputPacketSize
|
maximumPacketSize: converter.maximumOutputPacketSize
|
||||||
)
|
)
|
||||||
|
|
||||||
inputNode.installTap(onBus: 0, bufferSize: 1024, format: inputFormat) { [weak self] (pcmBuffer, when) in
|
|
||||||
guard let self = self, self.isRecording else { return }
|
|
||||||
self.convert(pcmBuffer: pcmBuffer, completion: completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
audioEngine.prepare()
|
|
||||||
try audioEngine.start()
|
|
||||||
self.isRecording = true
|
|
||||||
completion(nil, true, "Recording started")
|
|
||||||
} catch {
|
|
||||||
self.isRecording = false
|
|
||||||
completion(nil, false, "Failed to start audio engine: \(error.localizedDescription)")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func convert(pcmBuffer: AVAudioPCMBuffer, completion: @escaping (Data?, Bool, String) -> Void) {
|
private func processAudioBuffer(_ pcmBuffer: AVAudioPCMBuffer) {
|
||||||
guard let converter = self.audioConverter, let outputBuffer = self.aacBuffer else { return }
|
guard state == .recording,
|
||||||
|
let converter = self.audioConverter,
|
||||||
|
let outputBuffer = self.outputBuffer,
|
||||||
|
let flvMuxer = self.flvMuxer else { return }
|
||||||
|
|
||||||
outputBuffer.byteLength = 0
|
|
||||||
outputBuffer.packetCount = 0
|
outputBuffer.packetCount = 0
|
||||||
|
outputBuffer.byteLength = 0
|
||||||
|
|
||||||
var error: NSError?
|
var error: NSError?
|
||||||
var pcmBufferWasProvided = false
|
|
||||||
let status = converter.convert(to: outputBuffer, error: &error) { _, outStatus in
|
let status = converter.convert(to: outputBuffer, error: &error) { _, outStatus in
|
||||||
if pcmBufferWasProvided {
|
|
||||||
outStatus.pointee = .noDataNow
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
outStatus.pointee = .haveData
|
outStatus.pointee = .haveData
|
||||||
pcmBufferWasProvided = true
|
|
||||||
return pcmBuffer
|
return pcmBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
guard status != .error, error == nil else {
|
if status == .haveData, outputBuffer.byteLength > 0 {
|
||||||
print("AAC conversion error: \(error?.localizedDescription ?? "unknown")")
|
let aacData = Data(bytes: outputBuffer.data, count: Int(outputBuffer.byteLength))
|
||||||
return
|
let aacTag = flvMuxer.getAACTag(data: aacData)
|
||||||
|
dispatchData(aacTag)
|
||||||
|
} else if let error = error {
|
||||||
|
processingQueue.async { [weak self] in
|
||||||
|
guard let self = self, self.state == .recording else { return }
|
||||||
|
|
||||||
|
let errorMessage = "AAC Conversion Error: \(error.localizedDescription)"
|
||||||
|
self.dispatchError(callback: self.recordingCallback ?? { _, _, _ in }, message: errorMessage)
|
||||||
|
|
||||||
|
self.cleanUp()
|
||||||
|
self.state = .stopped
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if outputBuffer.byteLength == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let aacData = Data(bytes: outputBuffer.data, count: Int(outputBuffer.byteLength))
|
|
||||||
|
|
||||||
guard let adtsHeader = self.adtsHeader(for: aacData.count) else {
|
|
||||||
print("Failed to create ADTS header")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var fullPacket = Data()
|
|
||||||
fullPacket.append(Data(adtsHeader))
|
|
||||||
fullPacket.append(aacData)
|
|
||||||
|
|
||||||
completion(fullPacket, true, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func adtsHeader(for aacFrameSize: Int) -> [UInt8]? {
|
private func cleanUp() {
|
||||||
guard let outputFormat = self.audioConverter?.outputFormat else { return nil }
|
|
||||||
|
|
||||||
let adtsLength = aacFrameSize + 7
|
|
||||||
let sampleRate = outputFormat.sampleRate
|
|
||||||
let channels = outputFormat.channelCount
|
|
||||||
|
|
||||||
let sampleRateIndices: [Double: Int] = [
|
|
||||||
96000: 0, 88200: 1, 64000: 2, 48000: 3, 44100: 4, 32000: 5,
|
|
||||||
24000: 6, 22050: 7, 16000: 8, 12000: 9, 11025: 10, 8000: 11, 7350: 12
|
|
||||||
]
|
|
||||||
guard let freqIndex = sampleRateIndices[sampleRate] else {
|
|
||||||
print("Unsupported sample rate for ADTS header: \(sampleRate)")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let profile = 2 // AAC-LC
|
|
||||||
let channelCfg = channels
|
|
||||||
|
|
||||||
var adtsHeader = [UInt8](repeating: 0, count: 7)
|
|
||||||
adtsHeader[0] = 0xFF
|
|
||||||
adtsHeader[1] = 0xF9
|
|
||||||
adtsHeader[2] = UInt8(((profile - 1) << 6) | (freqIndex << 2) | (Int(channelCfg) >> 2))
|
|
||||||
adtsHeader[3] = UInt8((Int(channelCfg) & 3) << 6 | (adtsLength >> 11))
|
|
||||||
adtsHeader[4] = UInt8((adtsLength & 0x7FF) >> 3)
|
|
||||||
adtsHeader[5] = UInt8(((adtsLength & 7) << 5) | 0x1F)
|
|
||||||
adtsHeader[6] = 0xFC
|
|
||||||
|
|
||||||
return adtsHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc
|
|
||||||
public func stopRecord(_ completion: @escaping (Bool, String, String) -> Void) {
|
|
||||||
guard self.isRecording else {
|
|
||||||
completion(false, "Recording is not in progress.", "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.isRecording = false
|
|
||||||
|
|
||||||
audioEngine?.stop()
|
audioEngine?.stop()
|
||||||
audioEngine?.inputNode.removeTap(onBus: 0)
|
audioEngine?.inputNode.removeTap(onBus: 0)
|
||||||
|
try? AVAudioSession.sharedInstance().setActive(false)
|
||||||
|
|
||||||
audioEngine = nil
|
audioEngine = nil
|
||||||
audioConverter = nil
|
audioConverter = nil
|
||||||
aacBuffer = nil
|
outputBuffer = nil
|
||||||
|
flvMuxer = nil
|
||||||
do {
|
recordingCallback = nil
|
||||||
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
|
||||||
} catch {
|
|
||||||
print("Failed to deactivate audio session: \(error)")
|
|
||||||
}
|
|
||||||
|
|
||||||
completion(true, "Recording stopped", "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
private func dispatchData(_ data: Data) {
|
||||||
public func releaseRecord(_ completion: @escaping (Bool, String) -> Void) {
|
DispatchQueue.main.async { [weak self] in
|
||||||
if self.isRecording {
|
self?.recordingCallback?(data, false, "")
|
||||||
self.isRecording = false
|
|
||||||
audioEngine?.stop()
|
|
||||||
audioEngine?.inputNode.removeTap(onBus: 0)
|
|
||||||
}
|
}
|
||||||
audioEngine = nil
|
}
|
||||||
audioConverter = nil
|
|
||||||
aacBuffer = nil
|
|
||||||
|
|
||||||
do {
|
private func dispatchError(callback: @escaping (Data?, Bool, String) -> Void, message: String) {
|
||||||
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
|
DispatchQueue.main.async {
|
||||||
} catch {
|
callback(nil, false, message)
|
||||||
print("Failed to deactivate audio session on release: \(error)")
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
completion(true, "Record released")
|
private func dispatchSuccess(message: String) {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.recordingCallback?(nil, true, message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc
|
private class FLVMuxer {
|
||||||
|
private var startTime: CFTimeInterval
|
||||||
|
private var sampleRate: Double
|
||||||
|
private var channels: Double
|
||||||
|
|
||||||
|
init(sampleRate: Double = 44100.0, channels: Double = 1.0) {
|
||||||
|
self.startTime = CACurrentMediaTime()
|
||||||
|
self.sampleRate = sampleRate
|
||||||
|
self.channels = channels
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHeader() -> Data {
|
||||||
|
return Data([
|
||||||
|
0x46, 0x4C, 0x56,
|
||||||
|
0x01,
|
||||||
|
0x04,
|
||||||
|
0x00, 0x00, 0x00, 0x09,
|
||||||
|
0x00, 0x00, 0x00, 0x00
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMetaTag() -> Data {
|
||||||
|
var scriptData = Data()
|
||||||
|
|
||||||
|
scriptData.append(amfString("onMetaData"))
|
||||||
|
|
||||||
|
let properties: [(String, Any)] = [
|
||||||
|
("audiocodecid", 10.0),
|
||||||
|
("audiosamplerate", self.sampleRate),
|
||||||
|
("audiochannels", self.channels),
|
||||||
|
("stereo", self.channels > 1),
|
||||||
|
("creator", "starlock-record")
|
||||||
|
]
|
||||||
|
scriptData.append(amfECMAArray(properties))
|
||||||
|
|
||||||
|
let tag = createTag(type: .script, timestamp: 0, body: scriptData)
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAudioSpecificConfigTag(config: Data?) -> Data {
|
||||||
|
let audioObjectType: UInt8 = 2
|
||||||
|
guard let frequencyIndex = getSamplingFrequencyIndex(sampleRate: self.sampleRate) else {
|
||||||
|
let defaultConfig = Data([0x12, 0x08])
|
||||||
|
let body = Data([0xAE, 0x00]) + defaultConfig
|
||||||
|
return createTag(type: .audio, timestamp: 0, body: body)
|
||||||
|
}
|
||||||
|
|
||||||
|
let channelConfig = UInt8(self.channels)
|
||||||
|
|
||||||
|
var configByte1: UInt8 = 0
|
||||||
|
configByte1 |= (audioObjectType << 3)
|
||||||
|
configByte1 |= (frequencyIndex >> 1)
|
||||||
|
|
||||||
|
var configByte2: UInt8 = 0
|
||||||
|
configByte2 |= ((frequencyIndex & 0x01) << 7)
|
||||||
|
configByte2 |= (channelConfig << 3)
|
||||||
|
|
||||||
|
let audioSpecificConfig = Data([configByte1, configByte2])
|
||||||
|
|
||||||
|
let body = Data([0xAE, 0x00]) + audioSpecificConfig
|
||||||
|
let tag = createTag(type: .audio, timestamp: 0, body: body)
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAACTag(data: Data) -> Data {
|
||||||
|
let elapsed = CACurrentMediaTime() - self.startTime
|
||||||
|
let timestamp = UInt32(elapsed * 1000)
|
||||||
|
let body = Data([0xAE, 0x01]) + data
|
||||||
|
return createTag(type: .audio, timestamp: timestamp, body: body)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createTag(type: TagType, timestamp: UInt32, body: Data) -> Data {
|
||||||
|
let dataSize = UInt32(body.count)
|
||||||
|
let tagHeaderSize: UInt32 = 11
|
||||||
|
|
||||||
|
var header = Data(capacity: Int(tagHeaderSize))
|
||||||
|
header.append(type.rawValue)
|
||||||
|
header.append(UInt8((dataSize >> 16) & 0xFF))
|
||||||
|
header.append(UInt8((dataSize >> 8) & 0xFF))
|
||||||
|
header.append(UInt8(dataSize & 0xFF))
|
||||||
|
|
||||||
|
header.append(UInt8((timestamp >> 16) & 0xFF))
|
||||||
|
header.append(UInt8((timestamp >> 8) & 0xFF))
|
||||||
|
header.append(UInt8(timestamp & 0xFF))
|
||||||
|
header.append(UInt8((timestamp >> 24) & 0xFF))
|
||||||
|
|
||||||
|
header.append(contentsOf: [0x00, 0x00, 0x00])
|
||||||
|
|
||||||
|
var completeTag = Data()
|
||||||
|
completeTag.append(header)
|
||||||
|
completeTag.append(body)
|
||||||
|
|
||||||
|
let fullTagSize = UInt32(header.count + body.count)
|
||||||
|
completeTag.append(contentsOf: fullTagSize.bytes)
|
||||||
|
|
||||||
|
return completeTag
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getSamplingFrequencyIndex(sampleRate: Double) -> UInt8? {
|
||||||
|
let rates: [Double: UInt8] = [
|
||||||
|
96000: 0, 88200: 1, 64000: 2, 48000: 3, 44100: 4, 32000: 5,
|
||||||
|
24000: 6, 22050: 7, 16000: 8, 12000: 9, 11025: 10, 8000: 11, 7350: 12
|
||||||
|
]
|
||||||
|
return rates[sampleRate]
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum TagType: UInt8 { case audio = 0x08, video = 0x09, script = 0x12 }
|
||||||
|
|
||||||
|
private func amfString(_ str: String) -> Data {
|
||||||
|
var data = Data([0x02])
|
||||||
|
let bytes = [UInt8](str.utf8)
|
||||||
|
data.append(UInt8((bytes.count >> 8) & 0xFF))
|
||||||
|
data.append(UInt8(bytes.count & 0xFF))
|
||||||
|
data.append(contentsOf: bytes)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
private func amfNumber(_ val: Double) -> Data {
|
||||||
|
var data = Data([0x00])
|
||||||
|
data.append(contentsOf: val.bitPattern.bytes)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
private func amfBool(_ val: Bool) -> Data {
|
||||||
|
return Data([0x01, val ? 0x01 : 0x00])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func amfECMAArray(_ properties: [(String, Any)]) -> Data {
|
||||||
|
var data = Data([0x08])
|
||||||
|
let countBytes = UInt32(properties.count).bytes
|
||||||
|
data.append(contentsOf: countBytes)
|
||||||
|
|
||||||
|
for (key, value) in properties {
|
||||||
|
data.append(amfString(key).dropFirst(1))
|
||||||
|
if let numValue = value as? Double {
|
||||||
|
data.append(amfNumber(numValue))
|
||||||
|
} else if let boolValue = value as? Bool {
|
||||||
|
data.append(amfBool(boolValue))
|
||||||
|
} else if let stringValue = value as? String {
|
||||||
|
data.append(amfString(stringValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.append(contentsOf: [0x00, 0x00, 0x09])
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate extension FixedWidthInteger {
|
||||||
|
var bytes: [UInt8] { withUnsafeBytes(of: self.bigEndian) { Array($0) } }
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc(UTSConversionHelper)
|
||||||
public class UTSConversionHelper: NSObject {
|
public class UTSConversionHelper: NSObject {
|
||||||
@objc
|
@objc
|
||||||
public static func dataToNSArray(_ data: Data) -> NSArray {
|
public static func dataToNSArray(_ data: Data) -> NSArray {
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export const stopRecord = async function (): Promise<Result> {
|
|||||||
AudioRecorderManager.shared.stopRecord(
|
AudioRecorderManager.shared.stopRecord(
|
||||||
(success: boolean, message: string, filePath: string) => {
|
(success: boolean, message: string, filePath: string) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
resolve({ code: 0, data: { filePath: filePath }, message: message })
|
resolve({ code: 0, data: {}, message: message })
|
||||||
} else {
|
} else {
|
||||||
resolve({ code: -1, data: {}, message: message })
|
resolve({ code: -1, data: {}, message: message })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,28 +15,28 @@ class XP2PCallbackImpl extends XP2PCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fail(msg: string | null, errorCode: Int): void {
|
override fail(msg: string | null, errorCode: Int): void {
|
||||||
console.log('XP2P callback fail:', msg, errorCode)
|
// console.log('XP2P callback fail:', msg, errorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override commandRequest(id: string | null, msg: string | null): void {
|
override commandRequest(id: string | null, msg: string | null): void {
|
||||||
console.log('XP2P callback commandRequest:', id, msg)
|
// console.log('XP2P callback commandRequest:', id, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
override xp2pEventNotify(id: string | null, msg: string | null, event: Int): void {
|
override xp2pEventNotify(id: string | null, msg: string | null, event: Int): void {
|
||||||
console.log('XP2P callback xp2pEventNotify:', id, msg, event)
|
// console.log('XP2P callback xp2pEventNotify:', id, msg, event)
|
||||||
this.eventNotifyCallback?.invoke(id, msg, event)
|
this.eventNotifyCallback?.invoke(id, msg, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
override avDataRecvHandle(id: string | null, data: ByteArray | null, len: Int): void {
|
override avDataRecvHandle(id: string | null, data: ByteArray | null, len: Int): void {
|
||||||
console.log('XP2P callback avDataRecvHandle:', id, len)
|
// console.log('XP2P callback avDataRecvHandle:', id, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
override avDataCloseHandle(id: string | null, msg: string | null, errorCode: Int): void {
|
override avDataCloseHandle(id: string | null, msg: string | null, errorCode: Int): void {
|
||||||
console.log('XP2P callback avDataCloseHandle:', id, msg, errorCode)
|
// console.log('XP2P callback avDataCloseHandle:', id, msg, errorCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
override onDeviceMsgArrived(id: string | null, data: ByteArray | null, len: Int): string {
|
override onDeviceMsgArrived(id: string | null, data: ByteArray | null, len: Int): string {
|
||||||
console.log('XP2P callback onDeviceMsgArrived:', id, len)
|
// console.log('XP2P callback onDeviceMsgArrived:', id, len)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +103,8 @@ export const startServiceFunction = async function (
|
|||||||
|
|
||||||
export const stopServiceFunction = async function (id: string): Promise<Result> {
|
export const stopServiceFunction = async function (id: string): Promise<Result> {
|
||||||
try {
|
try {
|
||||||
await XP2P.stopService(id)
|
const result = await XP2P.stopService(id)
|
||||||
|
console.log('停止服务', result)
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
data: {},
|
data: {},
|
||||||
@ -125,7 +126,7 @@ export const runSendServiceFunction = async function (
|
|||||||
): Promise<Result> {
|
): Promise<Result> {
|
||||||
try {
|
try {
|
||||||
const result = await XP2P.runSendService(id, cmd, crypto)
|
const result = await XP2P.runSendService(id, cmd, crypto)
|
||||||
console.log('开始发送服务', result)
|
console.log('发送服务', result)
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
data: {},
|
data: {},
|
||||||
@ -168,11 +169,11 @@ export const dataSendFunction = async function (id: string, data: Array<number>)
|
|||||||
|
|
||||||
const result = await XP2P.dataSend(id, byteArray, data.length.toInt())
|
const result = await XP2P.dataSend(id, byteArray, data.length.toInt())
|
||||||
|
|
||||||
console.log('发送数据结果', result, byteArray)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
data: {},
|
data: {
|
||||||
|
result: result
|
||||||
|
},
|
||||||
message: '成功'
|
message: '成功'
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -132,7 +132,7 @@ export const runSendServiceFunction = async function (
|
|||||||
crypto: boolean
|
crypto: boolean
|
||||||
): Promise<Result> {
|
): Promise<Result> {
|
||||||
try {
|
try {
|
||||||
const result = runSendService(id, cmd, crypto)
|
const result = await runSendService(id, cmd, crypto)
|
||||||
console.log('开始发送服务', result)
|
console.log('开始发送服务', result)
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
@ -178,14 +178,15 @@ export const dataSendFunction = async function (id: string, data: Array<number>)
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = dataSend(id, buffer, data.length)
|
const result = await dataSend(id, buffer, data.length)
|
||||||
// console.log('发送数据结果', result)
|
|
||||||
|
|
||||||
XP2PDataHelper.deallocateBytes(buffer)
|
XP2PDataHelper.deallocateBytes(buffer)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: 0,
|
code: 0,
|
||||||
data: {},
|
data: {
|
||||||
|
result: result
|
||||||
|
},
|
||||||
message: '成功'
|
message: '成功'
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user