Я реализовал BLE, и он работает. Поток: DrawerActivity запускается, он устанавливает фрагментА , который имеет реализацию BLE, потому что я хочу активный BLE только в фрагментА . Таким образом, если вы переключитесь на фрагмент B , это должно прервать соединение BLE и выполнить сброс устройства.
Что происходит, так это то, что он полностью отключается только при закрытии приложения или выключении Bluetooth. Если вы закроете фрагмент A и откроете его снова, он будет работать из pocketActivity . Если вы сделаете это снова, так что теперь это уже третий раз, он не будет подключен к устройству BLE. Когда я продолжу исследовать, он даже не найдет правильное устройство BLE. . Это означает, что если вы запустите фрагмент в 4-й, 5-й раз, это будет тот же результат.
Чего я хочу добиться, так это когда onDestroy в Fragment вызывается, он должен отключиться от BLE и уничтожить все ссылки. И затем, если вы снова войдете во фрагмент A , он должен снова все воссоздать, независимо от того, сколько раз вы открываете фрагмент A . Но теперь устройство больше не найдено, возможно, потому что оно не отключился должным образом, и устройство BLE имеет старые ссылки или что-то.
Вот как я отключаюсь.
Это onDestroy метод:
override fun onDestroy() {
super.onDestroy()
activity?.unregisterReceiver(bluetoothReceiver)
bluetoothManager?.disconnectBluetoothService()
bluetoothManager = null
}
А в bluetoothManager
fun disconnectBluetoothService() {
bluetoothService?.disconnectGattServer()
}
А на bluetoothService :
fun disconnectGattServer() {
mConnected = false
mBluetoothGatt?.disconnect()
mBluetoothGatt?.close()
mBluetoothGatt = null
}
Вот все 3 файла, которые используются для BLE.
Fragmenta
private var bluetoothManager: MyBluetoothManager? = null
private val bluetoothReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (action == BluetoothAdapter.ACTION_STATE_CHANGED) {
when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
BluetoothAdapter.STATE_OFF -> {}
BluetoothAdapter.STATE_ON -> {
initBluetoothIfPossible()
bluetoothManager?.scanForBluetoothDevicesIfPossible(true)
}
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
listenToBluetoothChanges()
}
override fun onDestroy() {
super.onDestroy()
activity?.unregisterReceiver(bluetoothReceiver)
bluetoothManager?.disconnectBluetoothService()
bluetoothManager = null
}
private fun listenToBluetoothChanges() {
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
carSharingActivity?.registerReceiver(bluetoothReceiver, filter)
}
private fun initBluetoothIfPossible() {
bluetoothToken ?: return
if (bluetoothManager != null) {
bluetoothManager!!.pairDevice()
} else {
bluetoothManager = MyBluetoothManager(activity as Activity,
this,
bluetoothToken!!.token,
bluetoothToken!!.sessionKey,
bluetoothToken!!.uuid)
}
setImageForBluetoothStatus()
}
MyBluetoothManager
class ACCarBluetoothManager(var activity: Activity,
var listener: MyBluetoothListener,
private var token: String,
private var sessionKey: String,
private var accessDeviceUID: String) {
// Bluetooth adapter
private var bluetoothAdapter: BluetoothAdapter?
// Bluetooth service
private var bluetoothService: MyBluetoothService? = null
private var isBluetoothAvailable: Boolean = false
val isBluetoothEnabled: Boolean
get() = bluetoothAdapter?.isEnabled == true
var connectionStatus: Boolean = false
set(value) {
if (field == value) return
field = value
if (value) stopScanning()
else startScanning()
}
private var savedDevice: BluetoothDevice? = null
/**
* Service lifecyle management.
*/
private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, service: IBinder) {
bluetoothService = (service as MyBluetoothService.LocalBinder).service
bluetoothService?.isConnectedListener = { isConnected ->
listener.isConnected(isConnected)
connectionStatus = isConnected
}
isBluetoothAvailable = bluetoothService?.initialize() == true
}
override fun onServiceDisconnected(componentName: ComponentName) {
bluetoothService = null
connectionStatus = false
}
}
/**
* Broadcast receiver.
*/
private val gattUpdateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.action) {
BluetoothConstants.ACTION_GATT_SERVICES_DISCOVERED -> bluetoothService?.initializeIndications()
BluetoothConstants.ACTION_INDICATIONS_INITIALIZED -> bluetoothService?.startAuthentication(token)
}
} catch (e: Exception) {
Log.e("GattUpdateReciever", e.message)
}
}
}
/**
* Bluetooth device scanning callback. The scanned device is added to the list of available
* devices.
*/
private val bluetoothScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
val btDevice = result.device
if (btDevice.name.isNullOrEmpty()) return
if (deviceMatchesUID(btDevice)) {
savedDevice = btDevice
pairDevice()
}
}
}
init {
val gattServiceIntent = Intent(activity, MyBluetoothService::class.java)
activity.bindService(gattServiceIntent, this.serviceConnection, Context.BIND_AUTO_CREATE)
// Setup bluetooth adapter
val bluetoothManager = activity.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
// If bluetooth is not enabled, request permission, otherwise start scanning process, Not IMPLEMENTED, because it is not needed.
scanForBluetoothDevicesIfPossible()
activity.registerReceiver(gattUpdateReceiver, BluetoothConstants.makeGattUpdateIntentFilter())
}
fun scanForBluetoothDevicesIfPossible(enable: Boolean = isBluetoothEnabled) {
val hasLocationPermission = ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
if (enable) {
if (hasLocationPermission) {
startScanning()
}
//You can request for location permission if he doesn't have permission
} else {
stopScanning()
}
}
fun pairDevice() {
if (isBluetoothAvailable && savedDevice != null) {
bluetoothService?.connect(savedDevice!!)
}
}
fun startScanning() {
bluetoothAdapter?.bluetoothLeScanner?.startScan(bluetoothScanCallback)
}
fun stopScanning() {
bluetoothAdapter?.bluetoothLeScanner?.stopScan(bluetoothScanCallback)
}
fun deviceMatchesUID(device: BluetoothDevice): Boolean {
return device.name.equals(accessDeviceUID, ignoreCase = true)
}
}
MyBluetoothService
class ACCarBluetoothService : Service() {
var isConnectedListener: ((Boolean) -> Unit)? = null
var mConnected = false
set(value) {
field = value
isConnectedListener?.invoke(value)
}
private val mBinder = LocalBinder()
private var mBluetoothManager: BluetoothManager? = null
private var mBluetoothAdapter: BluetoothAdapter? = null
private var mBluetoothGatt: BluetoothGatt? = null
private var mDividedTokenList: MutableList<ByteArray>? = null
// Various callback methods defined by the BLE API.
private val mGattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (status == BluetoothGatt.GATT_FAILURE
|| status != BluetoothGatt.GATT_SUCCESS
|| newState == BluetoothProfile.STATE_DISCONNECTED) {
disconnectGattServer()
return
}
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) onServiceDiscoveryReady()
}
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
when {
descriptor.characteristic.uuid == BluetoothConstants.UUID_COMMAND_CHALLENGE -> setCharacteristicNotification(
BluetoothConstants.UUID_DEBUG,
true)
descriptor.characteristic.uuid == BluetoothConstants.UUID_DEBUG -> setCharacteristicNotification(
BluetoothConstants.UUID_STATUS_1,
true)
descriptor.characteristic.uuid == BluetoothConstants.UUID_STATUS_1 -> setCharacteristicNotification(
BluetoothConstants.UUID_STATUS_2,
true)
descriptor.characteristic.uuid == BluetoothConstants.UUID_STATUS_2-> setCharacteristicNotification(
BluetoothConstants.UUID_STATUS_3,
true)
else -> onIndicationsInitialized()
}
}
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
if (status == BluetoothGatt.GATT_SUCCESS) broadcastUpdate(characteristic)
}
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
if (characteristic.uuid == BluetoothConstants.UUID_COMMAND_CHALLENGE) {
commandChallenge = characteristic.value
} else {
broadcastUpdate(characteristic)
}
}
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
if (BluetoothConstants.UUID_AUTHORIZE_PHONE == characteristic.uuid) writeNextPartToken()
}
}
override fun onBind(intent: Intent): IBinder? {
return mBinder
}
/**
* Initializes a reference to the local Bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
fun initialize(): Boolean {
if (mBluetoothManager == null) {
mBluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
if (mBluetoothManager == null) return false
}
mBluetoothAdapter = mBluetoothManager!!.adapter
if (mBluetoothAdapter == null) return false
return true
}
fun initializeIndications() {
setCharacteristicNotification(BluetoothConstants.UUID_COMMAND_CHALLENGE, true)
}
fun startAuthentication(token: String) {
mDividedTokenList = Tools.divideArray(Tools.decodeBase64(token))
writeNextPartToken()
}
fun writeCommand(sessionKey: String, command: ByteArray) {
val safeCommand = Tools.generateSafeCommand(command, commandChallenge, Tools.decodeBase64(sessionKey))
val commandCharacteristic = mBluetoothGatt!!.getService(BluetoothConstants.UUID_CAR_CONTROL_SERVICE)
.getCharacteristic(BluetoothConstants.UUID_COMMAND_PHONE)
commandCharacteristic.value = safeCommand
mBluetoothGatt!!.writeCharacteristic(commandCharacteristic)
}
fun connect(device: BluetoothDevice) {
mBluetoothGatt = device.connectGatt(this, false, this.mGattCallback)
}
fun disconnectGattServer() {
mConnected = false
mBluetoothGatt?.disconnect()
mBluetoothGatt?.close()
mBluetoothGatt = null
}
private fun onIndicationsInitialized() {
val intent = Intent()
intent.action = BluetoothConstants.ACTION_INDICATIONS_INITIALIZED
sendBroadcast(intent)
}
private fun onServiceDiscoveryReady() {
val intent = Intent()
intent.action = BluetoothConstants.ACTION_GATT_SERVICES_DISCOVERED
sendBroadcast(intent)
}
private fun writeNextPartToken() {
if (mDividedTokenList!!.isEmpty()) {
broadcastUpdate(BluetoothConstants.ACTION_INIT_READY)
return
}
writeValue(BluetoothConstants.UUID_AUTHORIZE_PHONE, mDividedTokenList!!.removeAt(0))
}
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
private fun writeValue(characteristicUUID: UUID, valueBytes: ByteArray) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) return
val service = mBluetoothGatt!!.getService(BluetoothConstants.UUID_CAR_CONTROL_SERVICE)
val characteristic = service.getCharacteristic(characteristicUUID)
characteristic.value = valueBytes
mBluetoothGatt!!.writeCharacteristic(characteristic)
}
private fun setCharacteristicNotification(characteristicUUID: UUID, enabled: Boolean) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) return
val characteristic = mBluetoothGatt!!
.getService(BluetoothConstants.UUID_CAR_INFORMATION_SERVICE)
.getCharacteristic(characteristicUUID)
mBluetoothGatt!!.setCharacteristicNotification(characteristic, enabled)
characteristic.getDescriptor(CONFIG_DESCRIPTOR)?.let {
it.value = if (enabled) BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
mBluetoothGatt!!.writeDescriptor(it)
}
}
private fun broadcastUpdate(characteristic: BluetoothGattCharacteristic) {
val intent = Intent()
if (BluetoothConstants.UUID_STATUS_1 == characteristic.uuid) {
if (!hasDataInBluetooth(characteristic.value)) {
mConnected = true
statusListener?.invoke()
}
intent.action = BluetoothConstants.ACTION_STATUS_1_AVAILABLE
intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
}
if (BluetoothConstants.UUID_DEBUG == characteristic.uuid) {
intent.action = BluetoothConstants.ACTION_DEBUG_AVAILABLE
intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
}
if (BluetoothConstants.UUID_STATUS_2 == characteristic.uuid) {
intent.action = BluetoothConstants.ACTION_STATUS_1_AVAILABLE
intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
}
if (BluetoothConstants.UUID_STATUS_3 == characteristic.uuid) {
intent.action = BluetoothConstants.ACTION_STATUS_1_AVAILABLE
intent.putExtra(BluetoothConstants.EXTRA_DATA, characteristic.value)
}
sendBroadcast(intent)
}
private fun hasDataInBluetooth(byteArray: ByteArray): Boolean {
for (b in byteArray) {
if (b.toInt() != 0) {
return false
}
}
return true
}
inner class LocalBinder : Binder() {
val service: MyBluetoothService
get() = this@MyBluetoothService
}
}