Search code examples
androidaudiomediarouter

Android MediaRouter Volume events


I want to implement volume change as seen in Youtube app while casting, like if app is in background or on lock screen

Like this

private void createSession() {
    ComponentName receiver = new ComponentName(getPackageName(), RemoteReceiver.class.getName());
    mediaSession = new MediaSessionCompat(this, "PlayerService", receiver, null);
    mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
            MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
            .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
            .build());
    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    audioManager.requestAudioFocus(new AudioManager.OnAudioFocusChangeListener() {
        @Override
        public void onAudioFocusChange(int focusChange) {
            // Ignore
        }
    }, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    mediaSession.setActive(true);

    mediaRouter = MediaRouter.getInstance(this);
    mediaRouter.setMediaSessionCompat(mediaSession);
}

Now I get the slider just like Image above and it responds to volume buttons, but I dont receive change in my broadcast receiver.


Solution

  • All you have to do is create a MediaSessionCompat with a VolumeProviderCompat.

    // Here is a volume provider which sets the volume of a remote route.
    // Extend VolumeProviderCompat with your own implementation.
    public static class RemoteVolume extends VolumeProviderCompat {
        public RemoteVolume(MediaRouter.RouteInfo routeInfo) {
            super(VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, STEPS, 0);
            this.stepSize = routeInfo.getVolumeMax() / STEPS;
            this.routeInfo = routeInfo;
        }
    
        @Override
        public void onSetVolumeTo(int volume) {
            routeInfo.requestSetVolume(volume * stepSize);
            setCurrentVolume(volume);
        }
    
        @Override
        public void onAdjustVolume(int delta) {
            int newVolume = getCurrentVolume() + delta;
            routeInfo.requestSetVolume(newVolume * stepSize);
            setCurrentVolume(newVolume);
        }
    }
    

    Then, connect the VolumeProviderCompat to your MediaSessionCompat:

    MediaSessionCompat session = new MediaSessionCompat(context, TAG);
    
    // might need some of these flags
    session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS |
                MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS);
    
    // the volume buttons are routed to this session when it is
    // active and currently playing
    session.setPlaybackState(new PlaybackStateCompat.Builder()
            .setState(PlaybackStateCompat.STATE_PLAYING, 0, 1.0f)
            .build());
    
    session.setActive(true);
    
    // The media router tries to bind its own VolumeProvider which kinda
    // works. We need to unbind the one provided and put ours in.
    router.setMediaSessionCompat(session);
    session.setPlaybackToRemote(new RemoteVolume(myRemoteRoute));