package me.barrasso.android.volume.media;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
import android.util.Pair;
import android.view.KeyEvent;
import me.barrasso.android.volume.LogUtils;
import me.barrasso.android.volume.popup.VolumePanel;
import me.barrasso.android.volume.utils.Constants;
import me.barrasso.android.volume.utils.Utils;
import static me.barrasso.android.volume.LogUtils.LOGI;
/**
* {@link android.content.BroadcastReceiver} to handle events related to audio/ media.
* Requires a {@link android.os.Handler} to respond to the messages, and to have it's
* screen state updated to determine whether to send {@link android.os.Message}s
* synchronously or not.
*/
public final class VolumeMediaReceiver extends BroadcastReceiver {
public static final String TAG = LogUtils.makeLogTag(VolumeMediaReceiver.class);
public static final int MSG_VOLUME_CHANGED = 0;
public static final int MSG_RINGER_MODE_CHANGED = 1;
public static final int MSG_MUTE_CHANGED = 2;
public static final int MSG_SPEAKERPHONE_CHANGED = 3;
public static final int MSG_VIBRATE_MODE_CHANGED = 4;
public static final int MSG_MEDIA_BUTTON_EVENT = 5;
public static final int MSG_VOLUME_LONG_PRESS = 6;
public static final int MSG_PLAY_STATE_CHANGED = 7;
public static final int MSG_DISPATCH_KEYEVENT = 8;
public static final int MSG_CALL_STATE_CHANGED = 9;
public static final int MSG_AUDIO_ROUTES_CHANGED = 10;
public static final int MSG_HEADSET_PLUG = 11;
public static final int MSG_USER_PRESENT = 12;
public static final int MSG_ALARM_CHANGED = 13;
public static String getMsgName(final int msg) {
switch (msg) {
case MSG_VOLUME_CHANGED:
return "MSG_VOLUME_CHANGED";
case MSG_RINGER_MODE_CHANGED:
return "MSG_RINGER_MODE_CHANGED";
case MSG_MUTE_CHANGED:
return "MSG_MUTE_CHANGED";
case MSG_SPEAKERPHONE_CHANGED:
return "MSG_SPEAKERPHONE_CHANGED";
case MSG_VIBRATE_MODE_CHANGED:
return "MSG_VIBRATE_MODE_CHANGED";
case MSG_MEDIA_BUTTON_EVENT:
return "MSG_MEDIA_BUTTON_EVENT";
case MSG_VOLUME_LONG_PRESS:
return "MSG_VOLUME_LONG_PRESS";
case MSG_PLAY_STATE_CHANGED:
return "MSG_PLAY_STATE_CHANGED";
case MSG_DISPATCH_KEYEVENT:
return "MSG_DISPATCH_KEYEVENT";
case MSG_CALL_STATE_CHANGED:
return "MSG_CALL_STATE_CHANGED";
case MSG_AUDIO_ROUTES_CHANGED:
return "MSG_AUDIO_ROUTES_CHANGED";
case MSG_HEADSET_PLUG:
return "MSG_HEADSET_PLUG";
case MSG_USER_PRESENT:
return "MSG_USER_PRESENT";
case MSG_ALARM_CHANGED:
return "MSG_ALARM_CHANGED";
default:
return "MSG_UNKNOWN";
}
}
private boolean mScreen = true;
private Handler mHandler;
public VolumeMediaReceiver(Handler handler) {
mHandler = handler;
}
public void setScreen(final boolean screen) { mScreen = screen; }
public void setHandler(Handler handler) { mHandler = handler; }
@Override
public void onReceive(Context context, Intent intent) {
if (null == intent || TextUtils.isEmpty(intent.getAction())) return;
final String mAction = intent.getAction();
if (Intent.ACTION_MEDIA_BUTTON.equals(mAction))
{
KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (null == event) return;
LOGI(TAG, Intent.ACTION_MEDIA_BUTTON + ':' + String.valueOf(event.getKeyCode()));
sendMessage(Message.obtain(mHandler, MSG_MEDIA_BUTTON_EVENT, event));
}
else if (Intent.ACTION_USER_PRESENT.equals(mAction))
{
LOGI(TAG, Intent.ACTION_USER_PRESENT);
sendMessage(Message.obtain(mHandler, MSG_USER_PRESENT));
}
else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(mAction))
{
int ringerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, AudioManager.RINGER_MODE_NORMAL);
LOGI(TAG, AudioManager.RINGER_MODE_CHANGED_ACTION + ':' + String.valueOf(ringerMode));
sendMessage(Message.obtain(mHandler, MSG_RINGER_MODE_CHANGED, ringerMode, 0));
}
else if (Constants.VOLUME_CHANGED_ACTION.equals(mAction))
{
int streamType = intent.getIntExtra(Constants.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM);
int streamValue = intent.getIntExtra(Constants.EXTRA_VOLUME_STREAM_VALUE, 0);
int prevValue = intent.getIntExtra(Constants.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
LOGI(TAG, Constants.VOLUME_CHANGED_ACTION + "[type=" + String.valueOf(streamType) +
", vol=" + String.valueOf(streamValue) + ", prev=" + String.valueOf(prevValue) + ']');
int[] vals = new int[] { streamType, streamValue, prevValue };
sendMessage(Message.obtain(mHandler, MSG_VOLUME_CHANGED, vals));
}
else if (Constants.MASTER_MUTE_CHANGED_ACTION.equals(mAction))
{
int streamType = VolumePanel.STREAM_MASTER;
boolean muted = intent.getBooleanExtra(Constants.EXTRA_MASTER_VOLUME_MUTED, false);
LOGI(TAG, Constants.MASTER_MUTE_CHANGED_ACTION + "[stream=" + String.valueOf(streamType)
+ ", muted=" + String.valueOf(muted) + ']');
sendMessage(Message.obtain(mHandler, MSG_MUTE_CHANGED, streamType, ((muted) ? 1 : 0)));
}
else if (Constants.MASTER_VOLUME_CHANGED_ACTION.equals(mAction))
{
int streamType = VolumePanel.STREAM_MASTER;
int prevValue = intent.getIntExtra(Constants.EXTRA_PREV_MASTER_VOLUME_VALUE, 0);
int streamValue = intent.getIntExtra(Constants.EXTRA_MASTER_VOLUME_VALUE, 0);
LOGI(TAG, Constants.MASTER_VOLUME_CHANGED_ACTION + "[type=" + String.valueOf(streamType) +
", vol=" + String.valueOf(streamValue) + ", prev=" + String.valueOf(prevValue) + ']');
int[] vals = new int[] { streamType, streamValue, prevValue };
sendMessage(Message.obtain(mHandler, MSG_VOLUME_CHANGED, vals));
}
else if (Intent.ACTION_HEADSET_PLUG.equals(mAction))
{
int state = intent.getIntExtra("state", 0);
LOGI(TAG, Intent.ACTION_HEADSET_PLUG + "[state=" + String.valueOf(state) + ']');
sendMessage(Message.obtain(mHandler, MSG_HEADSET_PLUG, state, 0));
}
else if (Constants.ACTION_ALARM_CHANGED.endsWith(mAction))
{
boolean alarmSet = intent.getBooleanExtra(Constants.ALARM_SET_EXTRA, false);
LOGI(TAG, Constants.ALARM_SET_EXTRA + "[alarmSet=" + String.valueOf(alarmSet) + ']');
sendMessage(Message.obtain(mHandler, MSG_ALARM_CHANGED, (alarmSet) ? 1 : 0));
}
else
{
processMediaIntent(context, intent);
}
}
// Respond to an unknown media Intent (try to extract metadata from its extras)
private void processMediaIntent(Context context, Intent intent) {
// Only handle media intents if we don't have a NotificationListenerService active.
boolean notifRunning = Utils.isMediaControllerRunning(context);
LOGI(TAG, "processMediaIntent(" + notifRunning + ')');
if (!notifRunning) {
Pair<MediaMetadataCompat, PlaybackStateCompat> data = MediaEventResponder.respond(context, intent);
if (null != data) {
sendMessage(Message.obtain(mHandler, MSG_PLAY_STATE_CHANGED, data));
}
}
}
// Handles a Message, even when the screen is off.
private void sendMessage(Message msg) {
if (null == msg) return;
if (mScreen) msg.sendToTarget();
else mHandler.handleMessage(msg);
}
}