Volume change Listener: Is registerMediaButtonEven

2019-02-07 01:58发布

问题:

Looking for a "most comprehensive & compatible (i.e. all Android versions...)" way to listen to volume changes, I found 2 different approaches to handle this:

  1. registerMediaButtonEventReceiver
  2. onKeyDown + SettingsContentObserver

Which method is preferable?

And why?

UPDATE 1: Thanks to the comment below, I discovered that onKeyDown() actually takes over the volume key, which may not be a complete solution as one of the posts mentioned that volume could be changed via interfaces other than the hardware buttons (not to mention that Google seems to be gradually taking away those "take over" capabilities).

OTOH, android.media.VOLUME_CHANGED_ACTION is a hack and isn't even documented. Which probably means it will cease to work in Android 5 or so...

UPDATE 2: registerMediaButtonEventReceiver doesn't work at all! (for the volume hardware buttons that is, I just tried it).

Additional insights?

回答1:

It would be great to have in future APIs a BroadcastReceiver for volume streams, but today maybe the best solution is to register a ContentObserver for the settings (that includes VOLUME_NOTIFICATION):

mSettingsContentObserver = new SettingsContentObserver( new Handler() ); 
this.getApplicationContext().getContentResolver().registerContentObserver( 
    android.provider.Settings.System.CONTENT_URI, true,
    mSettingsContentObserver );

see this answer for more info: https://stackoverflow.com/a/7017516/117382

Edit: Corrected with working code. Maybe this solution is better: https://stackoverflow.com/a/17398781/117382



回答2:

To be able to listen all volume streams the best way for me is using ContentObserver. Here is my code:

public class AudioVolumeObserver
{
    public interface OnAudioStreamVolumeChangedListener
    {
        void onAudioStreamVolumeChanged(ArrayList<Integer> audioStreamType);
    }

    public interface OnAudioModeVolumeChangedListener
    {
        void onAudioModeVolumeChanged(int volumeMode);
    }

    private static class AudioVolumeContentObserver
            extends ContentObserver
    {
        private final AudioManager mAudioManager;

        private final OnAudioStreamVolumeChangedListener mVolumeStreamListener;
        private final OnAudioModeVolumeChangedListener mVolumeModeListener;

        private int[] currentVolumeStreams = new int[6];
        private int[] lastVolumeStreams = new int[6];

        private int currentVolumeMode;
        private int lastVolumeMode;

        ArrayList<Integer> changedVolumeStreams = new ArrayList<>();


        public AudioVolumeContentObserver(
                @NonNull
                        Handler handler,
                @NonNull
                AudioManager audioManager,
                @NonNull
                        OnAudioModeVolumeChangedListener volumeModeListener,
                @NonNull
                OnAudioStreamVolumeChangedListener volumeStreamListener)
        {
            super(handler);

            mAudioManager = audioManager;
            mVolumeModeListener = volumeModeListener;
            mVolumeStreamListener = volumeStreamListener;

            lastVolumeMode = mAudioManager.getRingerMode();

            lastVolumeStreams[0] = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
            lastVolumeStreams[1] = mAudioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
            lastVolumeStreams[2] = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
            lastVolumeStreams[3] = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            lastVolumeStreams[4] = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
            lastVolumeStreams[5] = mAudioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION);

        }

        @Override
        public void onChange(boolean selfChange)
        {
            currentVolumeMode = mAudioManager.getRingerMode();

            currentVolumeStreams[0] = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
            currentVolumeStreams[1] = mAudioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
            currentVolumeStreams[2] = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
            currentVolumeStreams[3] = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
            currentVolumeStreams[4] = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
            currentVolumeStreams[5] = mAudioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION);

            if (currentVolumeMode != lastVolumeMode){
                lastVolumeMode = currentVolumeMode;
                mVolumeModeListener.onAudioModeVolumeChanged(currentVolumeMode);

            }

            changedVolumeStreams.clear();

            for (int i=0; i<6; i++)
                if (currentVolumeStreams[i] != lastVolumeStreams[i])
                {
                    lastVolumeStreams[i] = currentVolumeStreams[i];
                    changedVolumeStreams.add(i);
                }


            if (!changedVolumeStreams.isEmpty()){
                mVolumeStreamListener.onAudioStreamVolumeChanged(changedVolumeStreams);
            }

        }
    }

    private final Context mContext;

    private AudioVolumeContentObserver mAudioVolumeContentObserver;

    public AudioVolumeObserver(
            @NonNull
            Context context)
    {
        mContext = context;
    }

    public void start(@NonNull
                      OnAudioModeVolumeChangedListener volumeModeListener,
                      @NonNull
                      OnAudioStreamVolumeChangedListener volumeStreamListener)
    {
        stop();

        Handler handler = new Handler();
        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);

        mAudioVolumeContentObserver = new AudioVolumeContentObserver(handler, audioManager, volumeModeListener, volumeStreamListener);

        mContext.getContentResolver()
                .registerContentObserver(android.provider.Settings.System.CONTENT_URI, true, mAudioVolumeContentObserver);
    }

    public void stop()
    {
        if (mAudioVolumeContentObserver == null)
        {
            return;
        }

        mContext.getContentResolver()
                .unregisterContentObserver(mAudioVolumeContentObserver);
        mAudioVolumeContentObserver = null;
    }
}