Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to gate playback on Bluetooth Headset connection #15

Closed
yschimke opened this issue Dec 3, 2021 · 5 comments
Closed

How to gate playback on Bluetooth Headset connection #15

yschimke opened this issue Dec 3, 2021 · 5 comments
Assignees
Labels

Comments

@yschimke
Copy link
Contributor

yschimke commented Dec 3, 2021

I'd like to ensure playback is only active when a bluetooth headset is connected.

For stopping when the audio output changes it seems like .setHandleAudioBecomingNoisy(true) is the right approach.

Is LoadControl, the right API when the user clicks play to trigger a headset selection before continuing?

I've seen the references in google/ExoPlayer#9694, so I understand I'll need to implement this myself, but am curious which APIs are the right ones to plug into, to effect availableCommands for example? Or maybe allow the command, but launch the Bluetooth settings automatically and wait for a connection before playing?

@ojw28
Copy link
Contributor

ojw28 commented Dec 3, 2021

I think this should be done outside of the player. In other words, you should query whether there's a bluetooth headset connected before you prepare your ExoPlayer instance. ExoPlayer's audio-becoming-noisy handling might be sufficient for stopping the audio, but it's probably cleaner and more symmetric if you also handle this outside of the player. In other words, listen for the headset disconnecting, and stop the player when this occurs.

How to query bluetooth connected devices is outside the scope of this issue tracker, but you should be able to find what you need online. For example https://stackoverflow.com/questions/4715865/how-can-i-programmatically-tell-if-a-bluetooth-device-is-connected has some sample isBluetoothHeadsetConnected methods that might be useful to you.

@ojw28 ojw28 closed this as completed Dec 3, 2021
@ojw28 ojw28 self-assigned this Dec 3, 2021
@ojw28 ojw28 removed the needs triage label Dec 3, 2021
@yschimke
Copy link
Contributor Author

yschimke commented Dec 3, 2021

@ojw28 Thanks. That makes sense, and I've got docs on how to check and connect to the bluetooth headset.

It's not perfect, If the app laucnhes and then bluetooth disconnects before playback is started, I'll be in the same place. I can guard any play button presses, but it doesn't help for commands via the MediaSession, so I was hoping there was a nice approach to do this in ExoPlayer/Media3. Maybe show up via affecting availableCommands? Maybe interject during the LoadControl before it continues? I understand if not, and if there is some clean way to add, I'll suggest putting up a PR.

Would there be a downside to using LoadControl?

@ojw28
Copy link
Contributor

ojw28 commented Dec 6, 2021

I don't think LoadControl is the right place to be doing this. It feels like it's too deep in the player.

If you'd like to push your logic for starting playback just inside of the player, so that it works for commands via the MediaSession (and any other UI component), then you could use ForwardingPlayer. This is completely untested, but I'd imagine it would look something like:

class BtPlayer extends ForwardingPlayer {

    public BtPlayer(ExoPlayer player) {
      super(player);
      player.setHandleAudioBecomingNoisy(true);
    }

    @Override
    public void prepare() {
      if (blockPlayback()) {
        return;
      }
      super.prepare();
    }

    @Override
    public void play() {
      if (blockPlayback()) {
        return;
      }
      super.play();
    }

    @Override
    public void setPlayWhenReady(boolean playWhenReady) {
      if (playWhenReady && blockPlayback()) {
        return;
      }
      super.setPlayWhenReady(playWhenReady);
    }

    private boolean blockPlayback() {
      // Return whether playback should be blocked from starting
    }
}

You can then wrap your ExoPlayer inside an instance of this class, and pass the BtPlayer to the media session and any other UI components.

@yschimke
Copy link
Contributor Author

yschimke commented Dec 7, 2021

Thanks I'll try that or fallback to external logic at all points before playing.

Thanks.

@yschimke
Copy link
Contributor Author

yschimke commented Oct 3, 2022

The above solution has worked reasonably well, but it looks like to avoid errors like #167.

We will need to implement google/horologist#562 and use playback suppression for this.

marcbaechinger pushed a commit that referenced this issue Oct 17, 2022
A follow up to stopping speaker playback with a Player decorator from
#15.

It looks like we will need to change to using playback suppression to avoid
errors like #167, when we don't start
a foreground service.

We may not have this implemented by 1.0, but would like it in the API and it seems to be appropriate.

PiperOrigin-RevId: 478835686
marcbaechinger pushed a commit to google/ExoPlayer that referenced this issue Oct 20, 2022
A follow up to stopping speaker playback with a Player decorator from
androidx/media#15.

It looks like we will need to change to using playback suppression to avoid
errors like androidx/media#167, when we don't start
a foreground service.

We may not have this implemented by 1.0, but would like it in the API and it seems to be appropriate.

PiperOrigin-RevId: 478835686
@androidx androidx locked and limited conversation to collaborators Mar 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants