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:
- registerMediaButtonEventReceiver
- 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?
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
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;
}
}