Search code examples
androidkotlinpermissionsbluetoothandroid-12

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


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


Solution

  • 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}")
                        }
    }