package me.barrasso.android.volume.media;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.text.TextUtils;
import android.util.Pair;
import me.barrasso.android.volume.media.compat.RemoteControlCompat;
import me.barrasso.android.volume.utils.Utils;
import static me.barrasso.android.volume.LogUtils.LOGI;
/**
* Used for "metachanged" broadcasts to get {@link Metadata}
* information from them. Different apps have different extras they send, but this class
* doesn't necessarily know state so it's best to be as agnostic as possible.
*/
public final class MediaEventResponder {
public static enum PlayState {
START("START"), RESUME("RESUME"), PAUSE("PAUSE"), COMPLETE("COMPLETE");
private String title;
public String getTitle() { return title; }
PlayState(String title) {
this.title = title;
}
}
public static Pair<MediaMetadataCompat, PlaybackStateCompat> respond(Context context, Intent intent) {
if (null == context || null == intent) return null;
String mAction = intent.getAction();
Bundle extras = intent.getExtras();
if (null == extras) extras = Bundle.EMPTY; // In case we've got nothing.
MediaMetadataCompat.Builder mBuilder = null;
PlaybackStateCompat.Builder pBuilder = null;
int state = PlaybackStateCompat.STATE_NONE;
long position = 0;
LOGI("MediaEventResponder", mAction + ", extras=" + Utils.bundle2string(intent.getExtras()));
if (mAction.startsWith("com.amazon.mp3")) {
mBuilder = new MediaMetadataCompat.Builder();
pBuilder = new PlaybackStateCompat.Builder();
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, extras.getString("com.amazon.mp3.artist"));
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, extras.getString("com.amazon.mp3.track"));
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, extras.getString("com.amazon.mp3.album"));
state = (isPlaying(extras.getInt("com.amazon.mp3.playstate")) ?
PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_STOPPED);
} else if (mAction.startsWith("com.sonyericsson")) {
mBuilder = new MediaMetadataCompat.Builder();
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, extras.getString("ARTIST_NAME"));
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, extras.getString("TRACK_NAME"));
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, extras.getString("ALBUM_NAME"));
} else {
// This is the default case, standard API check.
mBuilder = new MediaMetadataCompat.Builder();
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, extras.getString("artist"));
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, extras.getString("album"));
if (extras.containsKey("title"))
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, extras.getString("title"));
else if (extras.containsKey("track"))
mBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, extras.getString("track"));
}
// Try the many ways to interpret the play state.
if (null == pBuilder) {
pBuilder = new PlaybackStateCompat.Builder();
String extraKey = null;
if (extras.containsKey("playstate"))
extraKey = "playstate";
else if (extras.containsKey("isPlaying"))
extraKey = "isPlaying";
else if (extras.containsKey("playing"))
extraKey = "playing";
else if (extras.containsKey("state"))
extraKey = "state";
// We still haven't given up, check the action.
if (TextUtils.isEmpty(extraKey)) {
boolean bState = false;
if (mAction.endsWith("endofplayback"))
bState = false;
else if (mAction.endsWith("playbackcomplete"))
bState = false;
else if (mAction.endsWith("ACTION_PLAYBACK_PAUSE")) // SEMC Legacy
bState = false;
else if (mAction.endsWith("ACTION_PAUSED")) // SEMC
bState = false;
else if (mAction.endsWith("ACTION_TRACK_STARTED")) // SEMC Legacy
bState = true;
else if (mAction.endsWith("ACTION_PLAYBACK_PLAY")) // SEMC
bState = true;
state = (bState ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_STOPPED);
} else {
state = (extras.getBoolean(extraKey) ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_STOPPED);
}
}
// Some extras we might want to use... might.
if (extras.containsKey("duration"))
mBuilder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, extras.getLong("duration"));
if (extras.containsKey("position"))
position = extras.getLong("position");
// Attempt to figure out what app is playing music.
pBuilder.setState(state, position, 1.0f);
mBuilder.putString(RemoteControlCompat.METADATA_KEY_PACKAGE, packageForAction(mAction));
// Workaround for Google Play Music... not the best :(
if (extras.containsKey("previewPlayType") && extras.containsKey("supportsRating") &&
extras.containsKey("currentContainerId"))
mBuilder.putString(RemoteControlCompat.METADATA_KEY_PACKAGE, "com.google.android.music");
// Workaround for Poweramp... should be pretty specific.
if (extras.containsKey("com.maxmpz.audioplayer.source"))
mBuilder.putString(RemoteControlCompat.METADATA_KEY_PACKAGE, "com.maxmpz.audioplayer");
return Pair.create(mBuilder.build(), pBuilder.build());
}
private static boolean isPlaying(final int state) {
if (state < 0) return false;
final PlayState[] states = PlayState.values();
if (state >= states.length) return false;
switch (states[state]) {
case START:
case RESUME:
return true;
}
return false;
}
public static String packageForAction(String action) {
if (TextUtils.isEmpty(action)) return null;
if (action.startsWith("com.android.music")) // AOSP
return "com.android.music";
if (action.startsWith("com.htc.music")) // HTC
return "com.htc.music";
if (action.startsWith("com.amazon.mp3")) // Amazon MP3
return "com.amazon.mp3";
if (action.startsWith("com.sonyericsson")) // WALKMAN
return "com.sonyericsson.music";
if (action.startsWith("app.odesanmi.and.wpmusic")) // ZPlayer
return "app.odesanmi.and.wpmusic";
if (action.startsWith("fm.last.android"))
return "fm.last.android";
if (action.startsWith("com.sec.android.app.music")) // Samsung
return "com.sec.android.app.music";
if (action.startsWith("com.miui.player")) // MIUI
return "com.miui.player";
if (action.startsWith("com.real.RealPlayer")) // RealPlayer
return "com.real.RealPlayer";
if (action.startsWith("com.rdio.android")) // Rdio
return "com.rdio.android";
if (action.startsWith("com.andrew.apollo")) // Apollo (CyanogenMod)
return "com.andrew.apollo";
if (action.startsWith("com.mog.android")) // MOG Mobile Music
return "com.mog.android";
if (action.startsWith("com.musixmatch.android")) // musiXmatch
return "com.musixmatch.android";
if (action.startsWith("com.doubleTwist.androidPlayer")) // DoubleTwist
return "com.doubleTwist.androidPlayer";
if (action.startsWith("com.samsung.sec.android.MusicPlayer")) // More Samsung
return "com.samsung.sec.android.MusicPlayer";
if (action.startsWith("com.samsung.music")) // Still more Samsung
return "com.samsung.music";
if (action.startsWith("com.spotify")) // Spotify
return "com.spotify.music";
if (action.startsWith("com.tbig.playerprotrial")) // PlayerPro Trial
return "com.tbig.playerprotrial";
if (action.startsWith("com.tbig.playerpro")) // PlayerPro
return "com.tbig.playerpro"; // MUST GO AFTER TRIAL!!!
if (action.startsWith("com.jrtstudio.AnotherMusicPlayer")) // Rocket Music Player
return "com.jrtstudio.AnotherMusicPlayer";
if (action.startsWith("com.lge.music")) // LG Music (Optimus 4X)
return "com.lge.music";
if (action.startsWith("com.jrtstudio.music")) // JRT Studio AOSP Music
return "com.jrtstudio.music";
if (action.startsWith("com.rhapsody")) // Rhapsody
return "com.rhapsody";
if (action.startsWith("org.iii.romulus.meridian")) // Meridian Player Transcend
return "org.iii.romulus.meridian";
if (action.startsWith("org.abrantix.rockon.rockonnggl")) // DoubleTwist Cubed
return "org.abrantix.rockon.rockonnggl";
return null; // Default
}
}