日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android 蓝牙使用

發布時間:2024/1/11 Android 57 coder
生活随笔 收集整理的這篇文章主要介紹了 Android 蓝牙使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文地址: Android 藍牙使用 - Stars-One的雜貨小窩

公司項目需求需要實現監聽藍牙耳機連接,且要獲取藍牙耳機電量功能,翻了不少官方文檔,記錄下技術調研代碼

注:本文沒有研究藍牙配對功能

關于藍牙權限適配

Android12以后,申請藍牙權限需要申請一組,如新增的幾個權限,需要一起申請

參考: 藍牙權限 ?|? Connectivity ?|? Android Developers

val permissionList =  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
   //android12及以上版本,這2個權限申請只會彈出一個對話框
   listOf(Permission.BLUETOOTH_CONNECT, Permission.BLUETOOTH_SCAN)
} else {
	//android12以下版本申請,默認是同意的,不會有權限彈窗
   listOf(Permission.BLUETOOTH_CONNECT)
}

打開藍牙開關

注意,如果是Android12及以上版本,藍牙開關打開操作需要有Bluetooth_Connect權限才能執行操作

效果就是直接打開藍牙開關

val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter

//需要權限android.permission.BLUETOOTH_CONNECT才能執行操作
bluetoothAdapter.enable()

不過Android還是有提供另外的一個方法供我們使用,就是下面的方法

此方法是API 5 就有的方法,和上面一樣,Android12及以上版本,就是需要有Bluetooth_Connect權限才能執行成功,否則會拋出異常

兼容低版本和高版本,此方法兼容,調用此方法,系統會彈出一個是否允許打開藍牙的對話提示框

val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
//這里寫你對應的Activity,我這里只是個例子
Activity.startActivityForResult(intent,7777)

至于接收回調,則是在對應的Activity中的onActivityResult()方法中處理返回結果:

  • 返回結果RESULT_OK,藍牙模塊打開成功
  • 返回結果RESULT_CANCELED,藍牙模塊打開失敗

PS: 測試的時候,用的華為手機,系統為鴻蒙4,Android Studio顯示為Android12,但是使用bluetoothAdapter.enable()卻是能夠正常彈出申請藍牙是否打開的對話框

獲取已配對的藍牙設備列表

val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter
val bluetoothDevices = bluetoothAdapter.bondedDevices


//獲取已配對的藍牙設備列表
bluetoothDevices.forEach { device->
	val text = when(device.type){
		BluetoothDevice.DEVICE_TYPE_UNKNOWN -> "傳統藍牙"
		BluetoothDevice.DEVICE_TYPE_CLASSIC -> "傳統藍牙"
		BluetoothDevice.DEVICE_TYPE_LE -> "低功耗藍牙"
		BluetoothDevice.DEVICE_TYPE_DUAL -> "傳統/低功耗雙模式藍牙"
		else->"未知類型"
	}
	LogUtils.d("藍牙設備名稱: ${device.name} 藍牙設備地址: ${device.address} 設備類型: $text")

}

獲取藍牙耳機設備列表

fun getEarPhoneDevices(context: Context): List<BluetoothDevice> {
	val bluetoothAdapter = context.getSystemService(BluetoothManager::class.java).adapter

	val bluetoothDevices = bluetoothAdapter.bondedDevices
	val types = listOf(
		BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES,
		BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET,
		BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO,
		BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE
	)
	return bluetoothDevices.filter { device ->
		types.any { it == device.bluetoothClass.deviceClass }
	}
}

PS: 測試過程中,發現漫步者耳機的類型識別不了為上述的四個類型...

獲取當前已連接藍牙耳機

一般只能連接一個藍牙耳機

val bluetoothAdapter = getSystemService(BluetoothManager::class.java).adapter
//如果在連接了藍牙耳機的情況,這里會進入到里面獲取到數據
bluetoothAdapter.getProfileProxy(this@EarphoneActivity, object : ServiceListener {
	override fun onServiceConnected(p0: Int, p1: BluetoothProfile?) {
		p1?.apply {
			//獲取藍牙耳機的設備列表
			val devices = this.connectedDevices
			devices.forEach { device ->
				val text = when (device.type) {
					BluetoothDevice.DEVICE_TYPE_UNKNOWN -> "傳統藍牙"
					BluetoothDevice.DEVICE_TYPE_CLASSIC -> "傳統藍牙"
					BluetoothDevice.DEVICE_TYPE_LE -> "低功耗藍牙"
					BluetoothDevice.DEVICE_TYPE_DUAL -> "傳統/低功耗雙模式藍牙"
					else -> "未知類型"
				}
				LogUtils.d("藍牙設備名稱: ${device.name} 藍牙設備地址: ${device.address} 設備類型: $text")
			}
		}
		LogUtils.d("設備連接")
	}

	override fun onServiceDisconnected(p0: Int) {

	}
}, BluetoothProfile.HEADSET)
}

獲取藍牙耳機電量

此方法適應市面上大多數藍牙耳機,但如果是AirPods,則無效果,下一章節會講到獲取AirPods電量方法

(雖然參考的文章說這個是AirPods的擴展AT命令,但實際對于正版AirPods無效果,反倒是我同事的華強北AirPods支持...)

通過注冊廣播,來獲取到對應的AT命令,在參數可以取值

val bluetoothIntentFilter = IntentFilter().apply {
	addAction(BluetoothDevice.ACTION_ACL_CONNECTED)
	addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED)

+	addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY+"."+BluetoothAssignedNumbers.APPLE)
+	addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)
}
registerReceiver(BlueToothReceiver(), bluetoothIntentFilter)

廣播詳情說明可看此鏈接藍牙耳機 | 安卓開發者

之后在Receiver可以獲取對應的AT命令參數,如下代碼:

//藍牙耳機的廣播監聽
if (BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT == action) {
	Log.d(TAG, "onReceive: 藍牙設備AT命令")

	//藍牙設備
	val blueDevice = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
		intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, BluetoothDevice::class.java)
	} else {
		intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
	}
	blueDevice?.apply {
		val device = this
		val text = when (device.type) {
			BluetoothDevice.DEVICE_TYPE_UNKNOWN -> "傳統藍牙"
			BluetoothDevice.DEVICE_TYPE_CLASSIC -> "傳統藍牙"
			BluetoothDevice.DEVICE_TYPE_LE -> "低功耗藍牙"
			BluetoothDevice.DEVICE_TYPE_DUAL -> "傳統/低功耗雙模式藍牙"
			else -> "未知類型"
		}
		LogUtils.d("藍牙設備名稱: ${device.name} 藍牙設備地址: ${device.address} 設備類型: $text")
	}

	intent.extras?.apply {
		val cmd = getString(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD,"")
		val cmdType = getInt(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,0)
		//根據命令行類型,會有不同的參數
		val cmdTypeStr = when (cmdType) {
			BluetoothHeadset.AT_CMD_TYPE_ACTION -> {"AT_CMD_TYPE_ACTION"}
			BluetoothHeadset.AT_CMD_TYPE_BASIC -> {"AT_CMD_TYPE_BASIC"}
			BluetoothHeadset.AT_CMD_TYPE_READ -> {"AT_CMD_TYPE_READ"}
			BluetoothHeadset.AT_CMD_TYPE_SET -> {"AT_CMD_TYPE_SET"}
			BluetoothHeadset.AT_CMD_TYPE_TEST -> {"AT_CMD_TYPE_TEST"}
			else -> {""}
		}

		val args = get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS) as Array<Any>

		LogUtils.d("""
	   接收到的AT命令:  AT $cmd $cmdTypeStr ${args.joinToString(",") { it.toString() }}
	""".trimIndent())
	
		if (cmd == "+IPHONEACCEV") {
				//電量等級說明 0:10% 9:100%
				val param = args.map { it.toString().toInt() }
				val level = param.last()
				
				   //電量
			val battery = (level + 1) * 10
		}
	}
	
}

AT+IPHONEACCEV命令
該命令是用來提示藍牙配件的電池狀態,可以提示兩方面:一方面是電池的電量百分比,一當面是藍牙配件的當前的充電狀態。該命令的說明見下方:

格式:AT+IPHONEACCEV=Number of key/value pairs,key1,val1,key2,val2,…

附帶的參數的含義分別是:①鍵值對的數目:接下來的參數文本的數量;②接下來就是鍵值對分別是:鍵值為1表示的是電量,該鍵所對應的值就是電量百分比,使用字串”0“到”9“表示;鍵值為2表示的是充電狀態,0表示不在充電,1表示正在充電。

舉例:AT+IPHONEACCEV=1,1,3 該AT指令就說明附帶了一個鍵值對(第一個參數是1);鍵是1,那么表示的是電量,且電量是40%(因為使用的是0~9,這里3就對應的百分比是40%)。

有個疑問,AirPods在電量變化后,會主動發送AT命令嗎?還是說是在連接后才會發一次,之后便不再發送了?

AT +XAPL AT_CMD_TYPE_SET AB-12-0100,18

AirPods耳機電量

起初一致沒找到方案,最終在github上輸入了AirPods關鍵字,發現了有幾個對于對應的開源庫,測試發現下面這個能夠符合要求(不過測試的時候,電量有些誤差,充電倉在iphone手機上顯示為8%,而android這邊則顯示為5%)

  • adolfintel/OpenPods: The Free and Open Source app for monitoring your AirPods on Android

app原理則是通過藍牙掃描,獲取到藍牙設備對應的設備廠商數據,并區分型號,然后做對應的處理從而獲取到電量(比如說左耳機,右耳機,耳機倉)

通過藍牙的adapter獲取scanner,調用掃描方法,之后在掃描的回調里處理返回結果, 從而得到對應的電量數據

藍牙掃描還需要一個獲取定位的權限(在Android12版本之下需要),不然無法掃描

參考

  • Android狀態欄顯示藍牙耳機電量 - CodeAntenna
  • 蘋果公司對藍牙免提AT指令的擴充 - WestMountain - 博客園
  • Bluetooth (Android) 之自定義 AT 指令 – xmamiga
  • 如何獲得藍牙耳機的電池電量?
  • adolfintel/OpenPods: The Free and Open Source app for monitoring your AirPods on Android

總結

以上是生活随笔為你收集整理的Android 蓝牙使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。