开发者

Android: How to detect Bluetooth connection status

I want to detect the connection status of a paired Bluetooth headset to the phone. In Android 3.0 (API level 11) "BluetoothHeadset" class has "isAudio开发者_如何学JAVAConnected()" method.

  • I don't know how to create (initialize) a "BluetoothHeadset" object. It seems that I need to use "getProfileProxy ()" but I need a sample code to find out how I need to create and pass the parameters.

Thanks, Hos


You need to implement BluetoothProfile.ServiceListener :

BluetoothProfile.ServiceListener b = new BlueToothListener();
            boolean profileProxy = BluetoothAdapter.getDefaultAdapter()
                    .getProfileProxy(Handler.bot, b, BluetoothProfile.HEADSET);


public class BlueToothListener implements ServiceListener {
        public static BluetoothHeadset headset;
        public static BluetoothDevice bluetoothDevice;
    @Override
    public void onServiceDisconnected(int profile) {// dont care
        headset = null;
    }

    @Override
    public void onServiceConnected(int profile,
            BluetoothProfile proxy) {// dont care
        try {
            Debugger.test("BluetoothProfile onServiceConnected "+proxy);
            if (proxy instanceof BluetoothHeadset)
                headset = ((BluetoothHeadset) proxy);
            else// getProfileProxy(Handler.bot, b, BluetoothProfile.HEADSET); 
                return;// ^^ => NEVER

            List<BluetoothDevice> connectedDevices = proxy
                    .getConnectedDevices();
            for (BluetoothDevice device : connectedDevices) {
                Debugger.log("BluetoothDevice found :" + device);
                bluetoothDevice = device;
                int connectionState = headset.getConnectionState(bluetoothDevice);
                Debugger.log("BluetoothHeadset connectionState "+connectionState);//2 == OK
                boolean startVoiceRecognition = headset
                        .startVoiceRecognition(device);
                if (startVoiceRecognition) {
                    Debugger
                            .log("BluetoothHeadset init Listener OK");
                    return;
                }
                else 
                    Notify.popup("Bluetooth headset can't start speech recognition");

            }
        } catch (Exception e) {
            // }
        }
    }
}

`


Monitoring of the BT status can be done indeed by polling. Here's how I did it (full sample here) . Note that it's just a sample and you should manage the polling better:

manifest

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

gradle

implementation 'androidx.core:core-ktx:1.9.0'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.work:work-runtime-ktx:2.7.1"

MainActivityViewModel.kt

@UiThread
class MainActivityViewModel(application: Application) : BaseViewModel(application) {
    private val bluetoothAdapter: BluetoothAdapter =
        context.getSystemService<BluetoothManager>()!!.adapter
    private var bluetoothHeadsetProfile: BluetoothProfile? = null
    val connectedDevicesLiveData =
        DistinctLiveDataWrapper(MutableLiveData<ConnectedDevicesState>(ConnectedDevicesState.Idle))
    val bluetoothTurnedOnLiveData = DistinctLiveDataWrapper(MutableLiveData<Boolean?>(null))
    val isConnectedToBtHeadsetLiveData = DistinctLiveDataWrapper(MutableLiveData<Boolean?>(null))
    private val pollingBtStateRunnable: Runnable

    init {
        updateBtStates()
        pollingBtStateRunnable = object : Runnable {
            override fun run() {
                updateBtStates()
                handler.postDelayed(this, POLLING_TIME_IN_MS)
            }
        }
        // Establish connection to the proxy.
        val serviceListener = object : BluetoothProfile.ServiceListener {
            override fun onServiceConnected(profile: Int, bluetoothProfile: BluetoothProfile) {
                this@MainActivityViewModel.bluetoothHeadsetProfile = bluetoothProfile
                handler.removeCallbacks(pollingBtStateRunnable)
                pollingBtStateRunnable.run()
            }

            override fun onServiceDisconnected(profile: Int) {
                handler.removeCallbacks(pollingBtStateRunnable)
                updateBtStates()
            }
        }
        bluetoothAdapter.getProfileProxy(context, serviceListener, BluetoothProfile.HEADSET)
        onClearedListeners.add {
            this.bluetoothHeadsetProfile?.let { bluetoothProfile ->
                bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothProfile)
            }
            handler.removeCallbacks(pollingBtStateRunnable)
        }
    }

    fun initWithLifecycle(lifecycle: Lifecycle) {
        lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onResume(owner: LifecycleOwner) {
                super.onResume(owner)
                pollingBtStateRunnable.run()
            }

            override fun onPause(owner: LifecycleOwner) {
                super.onPause(owner)
                handler.removeCallbacks(pollingBtStateRunnable)
            }
        })
    }

    @UiThread
    private fun updateBtStates() {
        //        Log.d("AppLog", "updateBtStates")
        val isBlueToothTurnedOn = bluetoothAdapter.state == BluetoothAdapter.STATE_ON
        bluetoothTurnedOnLiveData.value = isBlueToothTurnedOn
        if (!isBlueToothTurnedOn) {
            connectedDevicesLiveData.value = ConnectedDevicesState.BluetoothIsTurnedOff
            isConnectedToBtHeadsetLiveData.value = false
            return
        }
        val isConnectedToBtHeadset = try {
            bluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) == BluetoothAdapter.STATE_CONNECTED
        } catch (e: SecurityException) {
            null
        }
        isConnectedToBtHeadsetLiveData.value = isConnectedToBtHeadset
        val bluetoothProfile = bluetoothHeadsetProfile
        if (bluetoothProfile != null) {
            if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
                val connectedDevicesSet = bluetoothProfile.connectedDevices.toHashSet()
                val previousConnectedDevices =
                    (connectedDevicesLiveData.value as? ConnectedDevicesState.GotResult)?.connectedDevices
                if (previousConnectedDevices == null || previousConnectedDevices != connectedDevicesSet)
                    connectedDevicesLiveData.value =
                        ConnectedDevicesState.GotResult(connectedDevicesSet)
            } else {
                connectedDevicesLiveData.value =
                    ConnectedDevicesState.NeedBlueToothConnectPermission
            }
        } else {
            connectedDevicesLiveData.value = ConnectedDevicesState.Idle
        }
    }

    companion object {
        private const val POLLING_TIME_IN_MS = 500L
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MainActivityViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
        viewModel.initWithLifecycle(lifecycle)
        viewModel.bluetoothTurnedOnLiveData.observe(this) {
            Log.d("AppLog", "MainActivity bluetoothTurnedOnLiveData BT turned on? $it")
        }
        viewModel.isConnectedToBtHeadsetLiveData.observe(this) {
            Log.d("AppLog", "MainActivity isConnectedToBtHeadsetLiveData BT headset connected? $it")
        }
        viewModel.connectedDevicesLiveData.observe(this) {
            Log.d("AppLog", "MainActivity connectedDevicesLiveData devices: $it")
        }
        findViewById<View>(R.id.grantBtPermission).setOnClickListener {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                requestPermissions(arrayOf(Manifest.permission.BLUETOOTH_CONNECT), 1)
            }
        }
    }
}

ConnectedDevicesState.kt

sealed class ConnectedDevicesState {
    object Idle : ConnectedDevicesState() {
        override fun toString(): String {
            if (BuildConfig.DEBUG)
                return "Idle"
            return super.toString()
        }
    }

    class GotResult(@Suppress("MemberVisibilityCanBePrivate") val connectedDevices: Set<BluetoothDevice>) : ConnectedDevicesState() {
        override fun toString(): String {
            if (BuildConfig.DEBUG) {
                return "GotResult: connectedDevices:${
                    connectedDevices.map {
                        try {
                            it.name
                        } catch (e: SecurityException) {
                            it.address
                        }
                    }
                }"
            }
            return super.toString()
        }
    }

    object BluetoothIsTurnedOff : ConnectedDevicesState() {
        override fun toString(): String {
            if (BuildConfig.DEBUG)
                return "BluetoothIsTurnedOff"
            return super.toString()
        }
    }

    object NeedBlueToothConnectPermission : ConnectedDevicesState() {
        override fun toString(): String {
            if (BuildConfig.DEBUG)
                return "NeedBlueToothConnectPermission"
            return super.toString()
        }
    }
}

DistinctLiveDataWrapper.kt

class DistinctLiveDataWrapper<T>(@Suppress("MemberVisibilityCanBePrivate") val mutableLiveData: MutableLiveData<T>) {
    @Suppress("MemberVisibilityCanBePrivate")
    val distinctLiveData = Transformations.distinctUntilChanged(mutableLiveData)
    var value: T?
        @UiThread
        set(value) {
            mutableLiveData.value = value
        }
        get() {
            return mutableLiveData.value
        }

    @AnyThread
    fun postValue(value: T) {
        mutableLiveData.postValue(value)
    }

    fun observe(lifecycleOwner: LifecycleOwner, observer: Observer<in T>) {
        distinctLiveData.observe(lifecycleOwner, observer)
    }
}

BaseViewModel.kt

/**usage: class MyViewModel(application: Application) : BaseViewModel(application)
 * getting instance:    private lateinit var viewModel: MyViewModel
 * viewModel=ViewModelProvider(this).get(MyViewModel::class.java)*/
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
    @Suppress("MemberVisibilityCanBePrivate")
    var isCleared = false
    @Suppress("MemberVisibilityCanBePrivate")
    val onClearedListeners = ArrayList<Runnable>()

    @Suppress("unused")
    @SuppressLint("StaticFieldLeak")
    val context: Context = application.applicationContext
    @Suppress("unused")
    val handler = Handler(Looper.getMainLooper())

    override fun onCleared() {
        super.onCleared()
        isCleared = true
        onClearedListeners.forEach { it.run() }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜