Android 12 or newer - Crash when changing device connection type to bluetooth

61 views Asked by At

I'm new to kotlin/android programming. I am trying to fix program crashing when selecting a device connection type to bluetooth. This only happens in android 12 or newer.

This is a code fragment in kotlin related to bluetooth device connection:

private fun onItemSelected(view: View, printer: PosPrinter) {
        var connectionType = printer.connectionType
        when (view.id) {
            R.id.connectionLayout -> {
                val selectedConnection = connectionAdapter.selected?.first ?: ConnectionType.IP
                if (selectedConnection != connectionType) {
                    connectionType = selectedConnection
                    if (model.triggerConnectivityChange(connectionType))
                        if (connectionType == ConnectionType.BT) showBtSelectDialog()
                        else if (connectionType == ConnectionType.USB) showUSBSelectDialog()
                }
            }
            R.id.protocolLayout -> {
                val protocol = protocolAdapter.selected?.first ?: PrintProtocol.ELZAB_STX
                model.selectedProtocolChange(protocol)
            }
        }
        handleVisibilityElements(connectionType)
    }

    private fun showBtSelectDialogInternal() {
        ListSelectDialog.Builder<IODevice, LayoutSelectItemListBinding>(appActivity)
            .apply {
                title(R.string.select_bt_device)
                applyItemLayout(R.layout.layout_select_item_list)
                applyFunction { binding, dialog ->
                    IODeviceHolder(dialog, binding) {
                        model.selectedConnectionChange(ConnectionType.BT, it)
                        handleVisibilityElements(ConnectionType.BT)
                    }
                }
                applyList(appActivity.resolveBTDevices().map { IODevice.parse(it) })
                headerIcon(R.drawable.ic_bluetooth)
                titleColor(Helper.uiModeDialogColors(isDarkMode))
                headerIconColor(Helper.uiModeDialogColors(isDarkMode))
                dialogBackgroundColor(Helper.uiModeDialogColors(isDarkMode))
                tintAll(R.color.iconBlue)
            }.create().show(childFragmentManager, null)
    }

    private fun showBtSelectDialog() {
        lifecycleScope.launch {
            lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED){
                showProgress()
                bluetoothEnabler.launch(BluetoothUtils.turnBluetoothOn())
            }
        }
    }

    private val bluetoothEnabler: ActivityResultLauncher<Intent> = forResult {
        hideProgress()
        if (it.resultCode == Activity.RESULT_OK) showBtSelectDialogInternal()
    }

And this is a permission related code in my android manifest file:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.BLUETOOTH"
        android:maxSdkVersion="30" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"
        android:usesPermissionFlags="neverForLocation"
        android:minSdkVersion="31"
        tools:targetApi="s" />
    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"
        android:minSdkVersion="31"/>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="32"
        tools:ignore="ScopedStorage" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

I've tried some other solutions I found on the internet but they didn't worked or I didn't know how to adjust them into this code

1

There are 1 answers

5
Risto On BEST ANSWER

It is not enough to declare Bluetooth permissions in the manifest file. Bluetooth permissions are runtime permissions and the application must obtain user consent to use the corresponding Bluetooth feature.

Here is a Stack Overflow answer on how to get these runtime permissions.

And here is the corresponding source code example from the link:

//check android12+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                            requestMultiplePermissions.launch(arrayOf(
                                Manifest.permission.BLUETOOTH_SCAN,
                                Manifest.permission.BLUETOOTH_CONNECT))
                        }
                        else{
                            val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
                            requestBluetooth.launch(enableBtIntent)
                        }
....................................................

private var requestBluetooth = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                if (result.resultCode == RESULT_OK) {
                    //granted
                }else{
                    //deny
                }
 }

private val requestMultiplePermissions =
                registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
                    permissions.entries.forEach {
                        Log.d("test006", "${it.key} = ${it.value}")
                    }
}