themobilecoder / headset_connection_event

Flutter library to capture connect and disconnect events from headset
MIT License
7 stars 21 forks source link

[Android] Does not work when targetSdkVersion is set to 31+ #8

Open Colton127 opened 2 years ago

Colton127 commented 2 years ago

To replicate: Open example, change targetSdkVersion to 31 (or 32) in build.gradle, make the necessary changes in AndroidManifest, and build.

You will have to make these minor adjustments in AndroidManifest Under "<activity android:exported="true"

And make this the intent-filter

     <receiver
            android:name="flutter.moum.headset_event.HeadsetBroadcastReceiver"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.HEADSET_PLUG" />
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

This will allow you to successfully build the release onto your device. However, the listener does not work at all. It simply does not respond to bluetooth connect/disconnect events. Setting TargetSdkVersion to 30, even while keeping the above manifest changes, fixes the problem entirely. This is on an Android 12 device.

Colton127 commented 2 years ago

The problem is that, after setting TargetSdk to 31+, you have to request permissions for bluetoothconnect. This requires the user agreeing to a prompt. See my fork here: https://github.com/Colton127/headset_connection_event

I discovered a different way of detecting headset status, including listening to events, by using https://github.com/ryanheise/audio_session. session.getDevices returns currently connected devices, and session.devicesChangedEventStream will listen to changes. You have to then create a function that determines if the device is a headset or not.

I'll leave this open while I continue my research. I should be pulling my fork soon.

anleac commented 2 years ago

Wow awesome catch @Colton127 - I was wondering why a few months ago my app stopped working when I did seemingly minor version bumps, thanks for the fork, will use it 👍

Colton127 commented 2 years ago

Wow awesome catch @Colton127 - I was wondering why a few months ago my app stopped working when I did seemingly minor version bumps, thanks for the fork, will use it 👍

Thank you, and you're welcome. I actually used your fork earlier in attempt to fix the problem but had no luck.

I have a HeadPhone manager class that utilizes audio_session and doesn't require any annoying permissions. I may publish it once I verify it works 100%.

Until then, Google is out to annoy us!

themobilecoder commented 2 years ago

The problem is that, after setting TargetSdk to 31+, you have to request permissions for bluetoothconnect. This requires the user agreeing to a prompt. See my fork here: https://github.com/Colton127/headset_connection_event

I discovered a different way of detecting headset status, including listening to events, by using https://github.com/ryanheise/audio_session. session.getDevices returns currently connected devices, and session.devicesChangedEventStream will listen to changes. You have to then create a function that determines if the device is a headset or not.

I'll leave this open while I continue my research. I should be pulling my fork soon.

@Colton127 awesome fork mate!

Apologies for not updating this project anymore. Feel free to publish your fork/library in pub.dev and I'll link yours from here so users can start using that. Or you can raise a PR here for that fork and ill merge it.

Cheers

anleac commented 2 years ago

The problem is that, after setting TargetSdk to 31+, you have to request permissions for bluetoothconnect. This requires the user agreeing to a prompt. See my fork here: https://github.com/Colton127/headset_connection_event I discovered a different way of detecting headset status, including listening to events, by using https://github.com/ryanheise/audio_session. session.getDevices returns currently connected devices, and session.devicesChangedEventStream will listen to changes. You have to then create a function that determines if the device is a headset or not. I'll leave this open while I continue my research. I should be pulling my fork soon.

@Colton127 awesome fork mate!

Apologies for not updating this project anymore. Feel free to publish your fork/library in pub.dev and I'll link yours from here so users can start using that. Or you can raise a PR here for that fork and ill merge it.

Cheers

Haha apologies about my fork not working - I had it working locally but when I tried to integrate my plugin with my actual app it was crashing (and didn't revisit the plugin) - would be absolutely curious with your HeadPhone manager class, though I might play around with also implementing my own today based on your earlier info about the device stream with https://github.com/ryanheise/audio_session

Update - nice, the devices changed stream is perfect, I just did something really hacky and quick:

  final session = await AudioSession.instance;
    session.devicesChangedEventStream.listen((AudioDevicesChangedEvent payload) {
      // The user unplugged the headphones, so we should pause or lower the volume.
      if (payload.devicesRemoved.isNotEmpty &&
          _audioPlayer?.state == PlayerState.playing) {
        // pause logic
      }
    });

but depending if you wanted bluetooth specific you could easily do payload.devicesRemoved.any((device) => isBlueTooth(device)) with checks on the bluetooth type.

I imagine @Colton127 that is what you ended up with doing

themobilecoder commented 1 year ago

I have merged your PR #11 and published it as part of the latest release @Colton127 🙏

Heech commented 1 year ago

@Colton127 @themobilecoder Thank you for working on this fix. 🙇‍♂️

I have a HeadPhone manager class that utilizes audio_session and doesn't require any annoying permissions.

So looking at the PR we still have to request permission for Android 12+? Or is there a way to detect a bluetooth device disconnect without showing the permission popup?

themobilecoder commented 1 year ago

Hi @Heech!

It seems like this specific Bluetooth functionality that we use (BLUETOOTH_CONNECT) requires user intervention to allow this permission starting Android 12 from google.

The BLUETOOTH_ADVERTISE, BLUETOOTH_CONNECT, and BLUETOOTH_SCAN permissions are runtime permissions. Therefore, you must explicitly request user approval in your app before you can look for Bluetooth devices, make a device discoverable to other devices, or communicate with already-paired Bluetooth devices. When your app requests at least one of these permissions, the system prompts the user to allow your app to access Nearby devices, as shown in figure 1.

So I'm not sure if there's a way to circumvent this

Heech commented 1 year ago

@themobilecoder Ah ok thanks for the clarification. It seems the youtube app can currently pause playback on disconnect while not requiring the bluetooth permissions even on android 12. So I wondered if there was maybe some alternative way to detect it or perhaps it's just some special privilege of the youtube app.

themobilecoder commented 1 year ago

@Heech mate that's a really good point.

So the permission that we use in this repo is BLUETOOTH_CONNECT, which would appear as "Nearby Devices" in the permission settings.

Looking at Youtube, like you said, no such permission.

I got curious and found this stackoverflow thread, and looks like there's a way to capture this event without ever needing bluetooth permission. (Use AudioManager)

Only thing here is that it does not use BroadcastReceiver, so there's a need to wrap this up within a Service or something that runs on the background.