Search code examples
androidvolumegoogle-castlockscreen

Handling volume change on android lockscreen?


What I am trying to do is, being able to catch volume up/down button actions on lockscreen on android 4.4.

Google Cast Design Checklist document describes lock screen requirement "Provide access to the volume control via hardware buttons". I tried various ways to handle hardware volume buttons on lock screen but none of them worked.

  1. onKeyDown/dispatchKeyEvent - I tried to override onKeyDown as well as dispatchKeyEvent methods on Activity, but none of these are executed on lockscreen, these only works when my app is focused.

  2. Settings.System.CONTENT_URI/ContentObserver - Registering content observer on main activity's content resolver does catch the system settings change, but that also does not occur on lockscreen.

  3. android.intent.action.MEDIA_BUTTON - Having this filter in manifest, I am able to receive play/pause actions from lockscreen however no volume change event.

  4. android.media.VOLUME_CHANGED_ACTION - Having this filter in manifest, this event is received in my BroadcastReceiver, unfortunately its extra values never get changed on lockscreen. When I keep hitting the volume button, the returned android.media.EXTRA_VOLUME_STREAM_VALUE remains the same (i.e. always 1), even though the CHANGED action is received by my broadcast receiver.

  5. CastVideos-android - The reference android sender app seems to be able to control volume on receiver even when controling from senders lock screen, however even after putting breakpoitns all over the place around Cast.CastApi.setVolume(), these would not get picked. So it seems that the command is being send to a receiver from somewhere I can not locate.

I can also see some other apps being able to catch HW volume keys i.e. Play Music app. So my device surely is capable...

Can anyone suggest any working solution?


Solution

  • At the end it appeared I was missing one line of code:

    MediaRouter.getInstance(context).addRemoteControlClient(remoteControlClient);
    

    or

    MediaRouter.getInstance(activity).setMediaSession(session.getMediaSession());
    

    here is some more code from my implementation for 4.0+:

    import android.media.AudioManager;
    import android.media.RemoteControlClient;
    import android.support.v7.media.MediaRouter;
    
    remoteControlClient = new RemoteControlClient(pendingIntent);
    remoteControlClient.setTransportControlFlags(RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE);
    
    audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    audioManager.registerMediaButtonEventReceiver(receiver);
    audioManager.registerRemoteControlClient(remoteControlClient);
    
    MediaRouter.getInstance(context).addRemoteControlClient(remoteControlClient);
    

    and for 5.0+:

    import android.media.AudioManager;
    import android.support.v4.media.session.MediaSessionCompat;
    import android.support.v7.media.MediaRouter;
    
    session = new MediaSessionCompat(activity, TAG);
    session.setPlaybackToLocal(AudioManager.STREAM_MUSIC);
    
    MediaRouter.getInstance(activity).setMediaSession(session.getMediaSession());
    

    The interesting thing is, there is some black magic that controls the receiver volume internally and your own Cast.CastApi.setVolume() call is not involved at all