初始化一些参数
const BluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter') as any
const Intent = plus.android.importClass('android.content.Intent') as any
const IntentFilter = plus.android.importClass('android.content.IntentFilter') as any
const BluetoothDevice = plus.android.importClass('android.bluetooth.BluetoothDevice') as any
const UUID = plus.android.importClass('java.util.UUID') as any
const Toast = plus.android.importClass('android.widget.Toast') as any
//连接串口设备的 UUID
const MY_UUID = UUID.fromString('00001101-0000-1000-8000-00805F9B34FB')
const invoke = plus.android.invoke
const btAdapter = BluetoothAdapter.getDefaultAdapter()
const activity = plus.android.runtimeMainActivity() as any
let btSocket: any = null
let btInStream: any = null
let btOutStream: any = null
let setIntervalId = 0
let btFindReceiver: any = null //蓝牙搜索广播接收器
let btStatusReceiver: any = null //蓝牙状态监听广播
interface BlueToothToolStateType {
bluetoothEnable: boolean
bluetoothState: string
discoveryDeviceState: boolean
readThreadState: boolean
}
interface BlueToothTooloptionsType {
listenBTStatusCallback: (arg?: any) => void
discoveryDeviceCallback: (arg?: any) => void
discoveryFinishedCallback: () => void
readDataCallback: (arg?: any) => void
connExceptionCallback: (arg?: any) => void
}
权限文件
/**
* 本模块封装了Android、iOS的应用权限判断、打开应用权限设置界面、以及位置系统服务是否开启
*/
// #ifdef APP-PLUS
const isIos = plus.os.name == 'iOS'
// #endif
// 判断推送权限是否开启
function judgeIosPermissionPush() {
let result = false
const UIApplication = (plus.ios as any).import('UIApplication')
const app = UIApplication.sharedApplication()
let enabledTypes = 0
if (app.currentUserNotificationSettings) {
const settings = app.currentUserNotificationSettings()
enabledTypes = settings.plusGetAttribute('types')
console.log('enabledTypes1:' + enabledTypes)
if (enabledTypes == 0) {
console.log('推送权限没有开启')
} else {
result = true
console.log('已经开启推送功能!')
}
plus.ios.deleteObject(settings)
} else {
enabledTypes = app.enabledRemoteNotificationTypes()
if (enabledTypes == 0) {
console.log('推送权限没有开启!')
} else {
result = true
console.log('已经开启推送功能!')
}
console.log('enabledTypes2:' + enabledTypes)
}
plus.ios.deleteObject(app)
plus.ios.deleteObject(UIApplication)
return result
}
// 判断定位权限是否开启
function judgeIosPermissionLocation() {
let result = false
const cllocationManger = (plus.ios as any).import('CLLocationManager')
const status = cllocationManger.authorizationStatus()
result = status != 2
console.log('定位权限开启:' + result)
// 以下代码判断了手机设备的定位是否关闭,推荐另行使用方法 checkSystemEnableLocation
/* const enable = cllocationManger.locationServicesEnabled();
const status = cllocationManger.authorizationStatus();
console.log("enable:" + enable);
console.log("status:" + status);
if (enable && status != 2) {
result = true;
console.log("手机定位服务已开启且已授予定位权限");
} else {
console.log("手机系统的定位没有打开或未给予定位权限");
} */
plus.ios.deleteObject(cllocationManger)
return result
}
// 判断麦克风权限是否开启
function judgeIosPermissionRecord() {
let result = false
const avaudiosession = (plus.ios as any).import('AVAudioSession')
const avaudio = avaudiosession.sharedInstance()
const permissionStatus = avaudio.recordPermission()
console.log('permissionStatus:' + permissionStatus)
if (permissionStatus == 1684369017 || permissionStatus == 1970168948) {
console.log('麦克风权限没有开启')
} else {
result = true
console.log('麦克风权限已经开启')
}
plus.ios.deleteObject(avaudiosession)
return result
}
// 判断相机权限是否开启
function judgeIosPermissionCamera() {
let result = false
const AVCaptureDevice = (plus.ios as any).import('AVCaptureDevice')
const authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide')
console.log('authStatus:' + authStatus)
if (authStatus == 3) {
result = true
console.log('相机权限已经开启')
} else {
console.log('相机权限没有开启')
}
plus.ios.deleteObject(AVCaptureDevice)
return result
}
// 判断相册权限是否开启
function judgeIosPermissionPhotoLibrary() {
let result = false
const PHPhotoLibrary = (plus.ios as any).import('PHPhotoLibrary')
const authStatus = PHPhotoLibrary.authorizationStatus()
console.log('authStatus:' + authStatus)
if (authStatus == 3) {
result = true
console.log('相册权限已经开启')
} else {
console.log('相册权限没有开启')
}
plus.ios.deleteObject(PHPhotoLibrary)
return result
}
// 判断通讯录权限是否开启
function judgeIosPermissionContact() {
let result = false
const CNContactStore = (plus.ios as any).import('CNContactStore')
const cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0)
if (cnAuthStatus == 3) {
result = true
console.log('通讯录权限已经开启')
} else {
console.log('通讯录权限没有开启')
}
plus.ios.deleteObject(CNContactStore)
return result
}
// 判断日历权限是否开启
function judgeIosPermissionCalendar() {
let result = false
const EKEventStore = (plus.ios as any).import('EKEventStore')
const ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0)
if (ekAuthStatus == 3) {
result = true
console.log('日历权限已经开启')
} else {
console.log('日历权限没有开启')
}
plus.ios.deleteObject(EKEventStore)
return result
}
// 判断备忘录权限是否开启
function judgeIosPermissionMemo() {
let result = false
const EKEventStore = (plus.ios as any).import('EKEventStore')
const ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1)
if (ekAuthStatus == 3) {
result = true
console.log('备忘录权限已经开启')
} else {
console.log('备忘录权限没有开启')
}
plus.ios.deleteObject(EKEventStore)
return result
}
type AndroidPermissionIDType =
| 'android.permission.ACCESS_FINE_LOCATION' //位置权限
| 'android.permission.ACCESS_COARSE_LOCATION' //模糊位置权限(蓝牙\ble依赖)
| 'android.permission.CAMERA' //摄像头权限
| 'android.permission.READ_EXTERNAL_STORAGE' //外部存储(含相册)读取权限
| 'android.permission.WRITE_EXTERNAL_STORAGE' //外部存储(含相册)写入权限
| 'android.permission.RECORD_AUDIO ' //麦克风权限
| 'android.permission.READ_CONTACTS' //通讯录读取权限
| 'android.permission.WRITE_CONTACTS' //通讯录写入权限
| 'android.permission.READ_CALENDAR' //日历读取权限
| 'android.permission.WRITE_CALENDAR' //日历写入权限
| 'android.permission.READ_SMS' //短信读取权限
| 'android.permission.SEND_SMS' //短信发送权限
| 'android.permission.RECEIVE_SMS' //接收新短信权限
| 'android.permission.READ_PHONE_STATE' //获取手机识别码等信息的权限
| 'android.permission.CALL_PHONE' //拨打电话权限
| 'android.permission.READ_CALL_LOG' //获取通话记录权限
// Android权限查询
function requestAndroidPermission(permissionID: AndroidPermissionIDType) {
return new Promise((resolve) => {
plus.android.requestPermissions(
[permissionID], // 理论上支持多个权限同时查询,但实际上本函数封装只处理了一个权限的情况。有需要的可自行扩展封装
function (resultObj) {
let result = 0
for (let i = 0; i < resultObj.granted.length; i++) {
const grantedPermission = resultObj.granted[i]
console.log('已获取的权限:' + grantedPermission)
result = 1
}
for (let i = 0; i < resultObj.deniedPresent.length; i++) {
const deniedPresentPermission = resultObj.deniedPresent[i]
console.log('拒绝本次申请的权限:' + deniedPresentPermission)
result = 0
}
for (let i = 0; i < resultObj.deniedAlways.length; i++) {
const deniedAlwaysPermission = resultObj.deniedAlways[i]
console.log('永久拒绝申请的权限:' + deniedAlwaysPermission)
result = -1
}
resolve(result)
// 若所需权限被拒绝,则打开APP设置界面,可以在APP设置界面打开相应权限
// if (result != 1) {
// gotoAppPermissionSetting()
// }
},
function (error) {
console.log('申请权限错误:' + error.code + ' = ' + error.message)
resolve({
code: error.code,
message: error.message,
})
},
)
})
}
type IosPermissionIDType = 'location' | 'push' | 'camera' | 'photoLibrary' | 'record' | 'contact' | 'calendar' | 'memo'
// 使用一个方法,根据参数判断权限
function judgeIosPermission(permissionID: IosPermissionIDType) {
if (permissionID == 'location') {
return judgeIosPermissionLocation()
} else if (permissionID == 'camera') {
return judgeIosPermissionCamera()
} else if (permissionID == 'photoLibrary') {
return judgeIosPermissionPhotoLibrary()
} else if (permissionID == 'record') {
return judgeIosPermissionRecord()
} else if (permissionID == 'push') {
return judgeIosPermissionPush()
} else if (permissionID == 'contact') {
return judgeIosPermissionContact()
} else if (permissionID == 'calendar') {
return judgeIosPermissionCalendar()
} else if (permissionID == 'memo') {
return judgeIosPermissionMemo()
}
return false
}
// 跳转到**应用**的权限页面
function gotoAppPermissionSetting() {
if (isIos) {
const UIApplication = (plus.ios as any).import('UIApplication')
const application2 = UIApplication.sharedApplication()
const NSURL2 = (plus.ios as any).import('NSURL')
// const setting2 = NSURL2.URLWithString("prefs:root=LOCATION_SERVICES");
const setting2 = NSURL2.URLWithString('app-settings:')
application2.openURL(setting2)
plus.ios.deleteObject(setting2)
plus.ios.deleteObject(NSURL2)
plus.ios.deleteObject(application2)
} else {
// console.log(plus.device.vendor);
const Intent = plus.android.importClass('android.content.Intent') as any
const Settings = plus.android.importClass('android.provider.Settings') as any
const Uri = plus.android.importClass('android.net.Uri') as any
const mainActivity = plus.android.runtimeMainActivity() as any
const intent = new Intent()
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
const uri = Uri.fromParts('package', mainActivity.getPackageName(), null)
intent.setData(uri)
mainActivity.startActivity(intent)
}
}
// 检查系统的设备服务是否开启
// const checkSystemEnableLocation = async function () {
function checkSystemEnableLocation() {
if (isIos) {
let result = false
const cllocationManger = (plus.ios as any).import('CLLocationManager')
result = cllocationManger.locationServicesEnabled() as unknown as boolean
console.log('系统定位开启:' + result)
plus.ios.deleteObject(cllocationManger)
return result
} else {
const context = plus.android.importClass('android.content.Context') as any
const locationManager = plus.android.importClass('android.location.LocationManager') as any
const main = plus.android.runtimeMainActivity() as any
const mainSvr = main.getSystemService(context.LOCATION_SERVICE)
const result = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER)
console.log('系统定位开启:' + result)
return result
}
}
export default {
judgeIosPermission: judgeIosPermission,
requestAndroidPermission: requestAndroidPermission,
checkSystemEnableLocation: checkSystemEnableLocation,
gotoAppPermissionSetting: gotoAppPermissionSetting,
}
构建构造函数
import Perssimmon from './permission'
export class BlueToothTool {
private state: BlueToothToolStateType = {
bluetoothEnable: false, //蓝牙是否开启
bluetoothState: '', //当前蓝牙状态
discoveryDeviceState: false, //是否正在搜索蓝牙设备
readThreadState: false, //蓝牙设备名称
}
private options: BlueToothTooloptionsType = {
/**
* 监听蓝牙状态回调
* @param {String} state
*/
listenBTStatusCallback: function () {},
/**
* 搜索到新的蓝牙设备回调
* @param {Device} newDevice
*/
discoveryDeviceCallback: function () {},
/**
* 蓝牙搜索完成回调
*/
discoveryFinishedCallback: function () {},
/**
* 接收到数据回调
* @param {Array} dataByteArr
*/
readDataCallback: function () {},
/**
* 蓝牙连接中断回调
* @param {Exception} e
*/
connExceptionCallback: function () {},
}
constructor() {}
Init(setOptions: any) {
Object.assign(this.options, setOptions)
this.state.bluetoothEnable = this.GetBluetoothStatus()
this.ListenBluetoothStatus()
}
ShortToast(msg: string) {
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show()
}
/**
* 是否支持蓝牙
* @return {boolean}
*/
IsSupportBluetooth() {
if (btAdapter != null) {
return true
}
return false
}
/**
* 获取蓝牙的状态
* @return {boolean} 是否已开启
*/
GetBluetoothStatus() {
if (btAdapter != null) {
return btAdapter.isEnabled()
}
return false
}
/**
* 断开连接设备
* @param {Object} address
* @return {Boolean}
*/
CloseBtSocket() {
this.state.readThreadState = false
if (!btSocket) {
return
}
try {
btSocket.close()
} catch (e) {
console.error(e)
btSocket = null
}
}
/**
* 取消发现
*/
CancelDiscovery() {
if (btAdapter.isDiscovering()) {
btAdapter.cancelDiscovery()
}
if (btFindReceiver != null) {
activity.unregisterReceiver(btFindReceiver)
btFindReceiver = null
}
this.state.discoveryDeviceState = false
}
/**
* 打开蓝牙
* @param activity
* @param requestCode
*/
TurnOnBluetooth() {
if (btAdapter == null) {
this.ShortToast('没有蓝牙')
return
}
if (!btAdapter.isEnabled()) {
if (activity == null) {
this.ShortToast('未获取到activity')
return
} else {
const intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
const requestCode = 1
activity.startActivityForResult(intent, requestCode)
return
}
} else {
this.ShortToast('蓝牙已经打开')
}
}
/**
* 关闭蓝牙
*/
TurnOffBluetooth() {
if (btAdapter != null && btAdapter.isEnabled()) {
btAdapter.disable()
}
if (btFindReceiver != null) {
try {
activity.unregisterReceiver(btFindReceiver)
} catch (e) {
console.log('TurnOffBluetooth - Err', e)
}
btFindReceiver = null
}
this.state.bluetoothEnable = false
this.CancelDiscovery()
this.CloseBtSocket()
if (btAdapter != null && btAdapter.isEnabled()) {
btAdapter.disable()
this.ShortToast('蓝牙关闭成功')
} else {
this.ShortToast('蓝牙已经关闭')
}
}
/**
* 获取已经配对的设备
* @return {Array} connetedDevices
*/
GetPairedDevices() {
const pairedDevices: any[] = []
//蓝牙连接android原生对象,是一个set集合
let pairedDevicesAndroid = null
if (btAdapter != null && btAdapter.isEnabled()) {
pairedDevicesAndroid = btAdapter.getBondedDevices()
} else {
this.ShortToast('蓝牙未开启')
}
if (!pairedDevicesAndroid) {
return pairedDevices
}
//遍历连接设备的set集合,转换为js数组
const it = invoke(pairedDevicesAndroid, 'iterator')
while (invoke(it, 'hasNext')) {
const device = invoke(it, 'next')
pairedDevices.push({
name: invoke(device, 'getName'),
address: invoke(device, 'getAddress'),
})
}
return pairedDevices
}
/**
* 发现设备
*/
DiscoveryNewDevice() {
if (btFindReceiver != null) {
try {
activity.unregisterReceiver(btFindReceiver)
} catch (e) {
console.error(e)
}
btFindReceiver = null
this.CancelDiscovery()
}
const Build = plus.android.importClass('android.os.Build') as any
//6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限
if (Build.VERSION.SDK_INT >= 6.0) {
await Perssimmon.requestAndroidPermission('android.permission.ACCESS_FINE_LOCATION')
await Perssimmon.requestAndroidPermission('android.permission.ACCESS_COARSE_LOCATION')
console.log('6.0以后的如果需要利用本机查找周围的wifi和蓝牙设备, 申请权限')
}
const options = this.options
btFindReceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: function (context: any, intent: any) {
plus.android.importClass(context)
plus.android.importClass(intent)
const action = intent.getAction()
if (BluetoothDevice.ACTION_FOUND == action) {
// 找到设备
const device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
const newDevice = {
name: plus.android.invoke(device, 'getName'),
address: plus.android.invoke(device, 'getAddress'),
}
options.discoveryDeviceCallback && options.discoveryDeviceCallback(newDevice)
}
if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED == action) {
// 搜索完成
this.CancelDiscovery()
options.discoveryFinishedCallback && options.discoveryFinishedCallback()
}
},
})
const filter = new IntentFilter()
filter.addAction(BluetoothDevice.ACTION_FOUND)
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
activity.registerReceiver(btFindReceiver, filter)
btAdapter.startDiscovery() //开启搜索
this.state.discoveryDeviceState = true
}
/**
* 蓝牙状态监听
* @param {Activity} activity
*/
ListenBluetoothStatus() {
if (btStatusReceiver != null) {
try {
activity.unregisterReceiver(btStatusReceiver)
} catch (e) {
console.error(e)
}
btStatusReceiver = null
}
btStatusReceiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: (context: any, intent: any) => {
plus.android.importClass(context)
plus.android.importClass(intent)
const action = intent.getAction()
const blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 0)
let stateStr = ''
switch (action) {
case BluetoothAdapter.ACTION_STATE_CHANGED:
switch (blueState) {
case BluetoothAdapter.STATE_TURNING_ON:
stateStr = 'STATE_TURNING_ON'
break
case BluetoothAdapter.STATE_ON:
this.state.bluetoothEnable = true
stateStr = 'STATE_ON'
break
case BluetoothAdapter.STATE_TURNING_OFF:
stateStr = 'STATE_TURNING_OFF'
break
case BluetoothAdapter.STATE_OFF:
stateStr = 'STATE_OFF'
this.state.bluetoothEnable = false
break
}
this.state.bluetoothState = stateStr
this.options.listenBTStatusCallback && this.options.listenBTStatusCallback(stateStr)
break
}
},
})
const filter = new IntentFilter()
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
activity.registerReceiver(btStatusReceiver, filter)
// 首次连接 状态回调
if (this.state.bluetoothEnable) {
this.options.listenBTStatusCallback && this.options.listenBTStatusCallback('STATE_ON')
}
}
/**
* 根据蓝牙地址,连接设备
* @param {Stirng} address
* @return {Boolean}
*/
ConnDevice(address: any, callback: (arg: boolean) => void) {
plus.android.importClass('java.io.InputStream')
plus.android.importClass('java.io.OutputStream')
plus.android.importClass('android.bluetooth.BluetoothSocket')
this.CancelDiscovery()
if (btSocket != null) {
this.CloseBtSocket()
}
this.state.readThreadState = false
try {
const device = invoke(btAdapter, 'getRemoteDevice', address)
btSocket = invoke(device, 'createRfcommSocketToServiceRecord', MY_UUID)
} catch (e) {
console.error(e)
this.ShortToast('连接失败,获取Socket失败!')
callback(false)
return false
}
try {
invoke(btSocket, 'connect')
this.ReadData() //读数据
this.ShortToast('连接成功')
callback(true)
} catch (e) {
console.error(e)
this.ShortToast('连接失败')
callback(false)
try {
btSocket.close()
btSocket = null
} catch (e1) {
console.error(e1)
}
return false
}
return true
}
/**
* 读取数据
* @param {Object} activity
* @param {Function} callback
* @return {Boolean}
*/
ReadData() {
if (!btSocket) {
this.ShortToast('请先连接蓝牙设备!')
return false
}
try {
btInStream = invoke(btSocket, 'getInputStream')
btOutStream = invoke(btSocket, 'getOutputStream')
console.log('ReadData - invoke', btSocket)
} catch (e) {
console.error(e)
this.ShortToast('创建输入输出流失败!')
this.CloseBtSocket()
return false
}
this.Read()
this.state.readThreadState = true
return true
}
/**
* 模拟java多线程读取数据
*/
Read() {
// console.log('Read - 模拟java多线程读取数据', btOutStream)
let setTimeCount = 0
clearInterval(setIntervalId)
setIntervalId = setInterval(() => {
setTimeCount++
if (this.state.readThreadState) {
const t = new Date().getTime()
//心跳检测
if (setTimeCount % 20 == 0) {
try {
btOutStream.write([0b00]) //这里报错
} catch (e) {
this.state.readThreadState = false
this.options.connExceptionCallback && this.options.connExceptionCallback(e)
}
}
const dataArr = []
while (invoke(btInStream, 'available') !== 0) {
const data = invoke(btInStream, 'read')
dataArr.push(data)
const ct = new Date().getTime()
if (ct - t > 20) {
break
}
}
if (dataArr.length > 0) {
this.options.readDataCallback && this.options.readDataCallback(dataArr)
}
}
}, 40)
}
/**
* 断开连接设备
* @param {Object} address
* @return {Boolean}
*/
DisConnDevice() {
if (btSocket != null) {
this.CloseBtSocket()
}
this.state.readThreadState = false
this.ShortToast('断开连接成功')
}
/**
* 发送数据
* @param {String} dataStr
* @return {Boolean}
*/
SendData(dataStr: string): boolean {
if (!btOutStream) {
this.ShortToast('创建输出流失败!')
return false
}
const bytes = invoke(dataStr as any, 'getBytes', 'gbk')
try {
btOutStream.write(bytes)
} catch (e) {
return false
}
return true
}
SendByteData(byteData: any) {
if (!btOutStream) {
this.ShortToast('创建输出流失败!')
return
}
try {
btOutStream.write(byteData)
} catch (e) {
return false
}
return true
}
}
使用嘛,就直接引入实例化就可以啦,不过由于蓝牙一直开着会很耗电,影响性能,如果连接上了,记得关闭蓝牙搜索功能