Metachanged broadcast intent for Rdio? [Android]

1,788 views
Skip to first unread message

mjmeyer

unread,
May 13, 2011, 11:08:29 PM5/13/11
to Rdio API
Many of the media players on Android use the convention of
broadcasting intents for various events such as track started,
completed, etc. so that other apps can react to these events.
Scrobbling is a common use case.

For example, lastfm, Amazon CloudPlayer, WinAmp, and the built in
Android music player declare the following intent actions that they
broadcast when a new track starts:

<action android:name="fm.last.android.metachanged" />
<action android:name="com.sec.android.app.music.metachanged" />
<action android:name="com.nullsoft.winamp.metachanged" />
<action android:name="com.amazon.mp3.metachanged" />
<action android:name="com.android.music.metachanged" />

Does Rdio broadcast a similar intent action? I've looked around and
cant seem to find any mention of it.

Stephen Lau

unread,
May 14, 2011, 12:43:06 PM5/14/11
to rdio...@googlegroups.com
Hi,
Are you asking about the Rdio player app itself, or the API?  In both cases, nothing is fired.

For the API, since the MediaPlayer object you get back implements the standard Android MediaPlayer interface, you can add completion listeners to know when the track is completed playing so you can fetch the MediaPlayer for the next track (or get() the metdata for it via the webservice API).

For the player, I'm not sure what the value is for firing an intent?  We take care of scrobbling on the server side for you.  We can look into firing one, just trying to get a sense of what extras/data would be desired to bundle with the Intent, so it would help if I knew the use case for what would consume these.

cheers,
steve
--
stephen lau | st...@grommit.com | http://whacked.net | @stevel

mjmeyer

unread,
May 14, 2011, 4:06:54 PM5/14/11
to Rdio API
Hi,

Thanks for responding. I'm asking about the player itself.

The very general use case I have in mind is simply the openness of
letting other apps know what track the user is currently listening
to. Specific things that could be done with that might include stuff
like:

mjmeyer

unread,
May 14, 2011, 5:09:16 PM5/14/11
to Rdio API
Hi, thanks for responding.

I'm asking about the app itself. The general use case I have in mind
is simply the openness of letting other apps on the device know what
track the user is currently listening to.

Specific things that might be done with that include:
- Implementing a visualizer that uses EchoNest or something to get
BPM, mood or other info about the song to drive the visualization
- Use current playing track as default search for LyricWiki app or
something using the musiXmatch APIs for a lyric browsing app,
preventing the user from having to search for the track that's already
playing.

The typical extras that are included with this action for the other
music players are: artist name, track name, album name. A few add
things like duration and release year, but it's pretty rare.

All the previously mentioned players also fire a "playstatechaged"
action which adds a boolean extra called "isplaying", so other apps
can pick up on play/pause/stop.

Rhapsody supports these actions on their android player. Pandora
doesnt seem to have any interest in implementing this as they seem to
want to keep all the eyeballs on their app (and the ads). It doesnt
seem to me that Rdio's model would be threatened by this openness.

Hopefully you'll consider it further.



Stephen Lau

unread,
May 16, 2011, 1:41:13 PM5/16/11
to rdio...@googlegroups.com
Hi,
Cool - makes sense.  So I'm planning on adding two actions:

com.rdio.android.playstatechanged
com.rdio.android.playbackcomplete

they will contain the following extras:
rdioSourceKey: the source key (e.g. 'a739678') mapping to the actively playing source
rdioTrackKey: the track key (e.g. 't8743227') mapping to the currently playing track
isPaused: boolean true or false
isPlaying: boolean true or false
album: string containing the album name
track: string containing the track name
artist: string containing the artist name

sound reasonable?  comments? thoughts?

cheers,
steve

Stephen Lau

unread,
May 16, 2011, 2:18:46 PM5/16/11
to rdio...@googlegroups.com
So one follow-up question I have is...
I can piggyback the global Android namespace and use "com.android.music.playstatechanged", but the stock Android player includes an "id" extra in their bundle (which looks like it maps to the ID you can reference in the local MediaManager).  On the plus side, if I use this name, then any existing apps will just work UNLESS they expect that "id" extra to be there in which case they'll crash and burn badly.  If I use "com.rdio.android.playstatechanged", then any apps will have to update to use our namespace, but they will at least not crash and burn.

Thoughts?

cheers,
steve

Stephen Lau wrote:
--
You received this message because you are subscribed to the Google Groups "Rdio API" group.
To post to this group, send email to rdio...@googlegroups.com.
To unsubscribe from this group, send email to rdio-api+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/rdio-api?hl=en.

mjmeyer

unread,
May 16, 2011, 4:13:14 PM5/16/11
to Rdio API
Sounds good though I think you may also want to implement the
metachanged action. Not sure why we have all three but the players
I've tested with so far tend to fire a metachanged and then a
playstatechanged when a new song starts. The difference seems to be
that metachanged doesnt include the isPlayed/isPaused extras and only
really tells you that a new song is selected.

If I get a few minutes today I'll dump some debug info with the extras
that the various players include in the respective actions so maybe
that will help you decide.

I'd love it if the playstatechanged action included track duration and
maybe track elapsed time, if possible.

Seems like the convention is to use your own namespace. Amazon, HTC,
Last.FM all do. Given that you can add a few additional extras that
are meaningful to your service (love the idea of having the
rdioTrackKey and rdioSourceKey) it makes even more sense to use your
own namespace.

FWIW, adding an additional action to listen for is trivially done via
a string value in manifest or in a call to register a broadcast
receiver in android, so I doubt it'd be a hassle and we already have
to do so for all of the other players.

Thanks much!

Stephen Lau

unread,
May 16, 2011, 4:44:40 PM5/16/11
to rdio...@googlegroups.com
Yeah - I guess I don't totally get the point of the metachanged Intent being fired if it's always going to be followed by a playstatechanged broadcast with the relevant info.  It makes sense for a stream (e.g. Shoutcast) where a single stream is always played and the "track" never changes, but metadata does - but doesn't really make sense for our track-based player.

Yeah if you have logs/debug info on the existing extras dispatched by other players, that'd be helpful.  I can add duration, but would like to see what other players are calling it in order to try and match as much as possible what the existing players are doing.

thanks for the suggestion :)

cheers,
steve

mjmeyer

unread,
May 17, 2011, 1:19:54 AM5/17/11
to Rdio API
Here are my findings on the intent actions fired and extras data sent
by various music players on android.

As might be expected with the fragmentation fest that is Android,
everyone does things differently and there doesnt seem to be any
clearly common approach.
That said, digging out the relevant track info and monitoring state of
the player does seem mostly possibly with these players.

FWIW, I think the last.fm model is the cleanest in terms of state
transitions and lack of multiple actions being sent for one event.

I also like the inclusion of the additional extras such as: duration,
position, station.

The following players were tested:
- Winamp
- Amazon MP3 player
- HTC Music Player (default/OEM player on HTC phones)
- last.fm
- Rhapsody

*- I wanted to test the OEM Music Player on Samsung Galaxy S but cant
find any good mention of the action/namespace to use. I found some
references to stuff like
com.samsung.sec.android.app.music.metachanged and countless variations
but none worked. I did find that musicPlayer.service.updateMediaInfo
and musicPlayer.service.updatePlayInfo worked on my Samsung phone but
these didnt include ANY extras in the action. Damn you Samsung!

The following scenarios were tested:
- A) starting the app and playing a track
- B) pausing a playing track
- C) resuming a playing track
- D) letting the current track play to completion and letting the next
track in playlist/station start
- E) playing a playlist thru to completion

*** WINAMP

A) WINAMP - Open & Play track

1) sends action: com.nullsoft.winamp.playstatechanged with extras:
id = 15
track = Pocket Full of Shells
artist = Rage Against The Machine
album = Evil Empire

2) immediately follows with action: com.nullsoft.winamp.metachanged
with extras (same as above):
id = 15
track = Pocket Full of Shells
artist = Rage Against The Machine
album = Evil Empire

B) WINAMP - pause playing track

1) sends action: com.nullsoft.winamp.playstatechanged with extras:
id = 15
track = Pocket Full of Shells
artist = Rage Against The Machine
album = Evil Empire

C) WINAMP - resume playing track

1) sends action: com.nullsoft.winamp.playstatechanged with extras:
id = 15
track = Pocket Full of Shells
artist = Rage Against The Machine
album = Evil Empire

D) WINAMP - play track to completion, let next track in list/station
start

1) sends action: com.nullsoft.winamp.metachanged with extras:
id = 17
track = Root Down
artist = Beastie Boys
album = The Sounds of Science (Disc 1)

E) WINAMP - playing a playlist thru to completion

1) sends action: com.nullsoft.winamp.playbackcomplete with extras:
id = 17
track = Root Down
artist = Beastie Boys
album = The Sounds of Science (Disc 1)

WINAMP - observations
- Pause & Resume are not really observable. You get playstatechange,
but you dont know to what state.
- there is never any variation of what extras are sent for the various
actions from Winamp.

*** AMAZON MP3

A) Amazon MP3 player - Open & play track
1) sends action: com.amazon.mp3.playstatechanged with extras:
track_count_int_key = 2
com.amazon.mp3.playstate = 0
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = 4
track_position_int_key = 0

2) immediately follows with action: com.amazon.mp3.metachanged with
extras:
com.amazon.mp3.track = Shake Your Rump
com.amazon.mp3.id = 4
com.amazon.mp3.artist = Beastie Boys
com.amazon.mp3.album = Paul's Boutique

3) immediately follows with action: com.amazon.mp3.playstatechanged
with extras:
track_count_int_key = 2
com.amazon.mp3.playstate = 3
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = 4
track_position_int_key = 0

*- note playstate has changed from the 1st action in this sequence.


B) Amazon MP3 player - pause current track

1) sends action: com.amazon.mp3.playstatechanged with extras:
track_count_int_key = 2
com.amazon.mp3.playstate = 1
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = 4
track_position_int_key = 0

C) Amazon MP3 player - resume paused track

1) sends action: com.amazon.mp3.playstatechanged with extras
track_count_int_key = 2
com.amazon.mp3.playstate = 3
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = 4
track_position_int_key = 0

D) Amazon MP3 player - play track to completion, let next track in
list/station start

1) sends action: com.amazon.mp3.metachanged with extras:
com.amazon.mp3.track = Johnny Ryall
com.amazon.mp3.id = 5
com.amazon.mp3.artist = Beastie Boys
com.amazon.mp3.album = Paul's Boutique

2) immediately follows with action: com.amazon.mp3.playstatechanged
with extras:
track_count_int_key = 2
com.amazon.mp3.playstate = 3
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = 5
track_position_int_key = 1

E) Amazon MP3 player - play thru to end of playlist

1) sends action: com.amazon.mp3.playstatechanged with extras:
track_count_int_key = 2
com.amazon.mp3.playstate = 0
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = 5
track_position_int_key = 1

2) immediately follows with action: com.amazon.mp3.playstatechanged
with extras:
track_count_int_key = 0
com.amazon.mp3.playstate = 0
shuffle_enabled_key = false
repeat_state_key = 0
com.amazon.mp3.id = -1
track_position_int_key = -1

3) immediately follows with action: com.amazon.mp3.metachanged with
extras:
com.amazon.mp3.track = <null>
com.amazon.mp3.id = -1
com.amazon.mp3.artist = <null>
com.amazon.mp3.album = <null>

Amazon MP3 player - Observations:
- provides discrete playstates to aid in tracking paused (1), playing
(3), stopped (0)
- uses "special" extra key names where everyone else uses "track",
"artist", "album"

*** HTC Music Player

A) HTC - Open & start track

1) sends action: com.htc.music.playstatechanged with extras:
id = 6
track = She Don't Use Jelly (Flaming Lips cover)
artist = Ben Folds Five
albumid = 6
album = Unknown
isplaying = true


B) HTC - pause

1) sends action: com.htc.music.playstatechanged with extras:
id = 6
track = She Don't Use Jelly (Flaming Lips cover)
artist = Ben Folds Five
albumid = 6
album = Unknown
isplaying = false

B) HTC - resume

1) sends action: com.htc.music.playstatechanged with extras:
id = 6
track = She Don't Use Jelly (Flaming Lips cover)
artist = Ben Folds Five
albumid = 6
album = Unknown
isplaying = true

C) HTC - play thru to next track

1) sends action: com.htc.music.metachanged with extras:
id = 9
track = Get Up, Stand Up
artist = Bob Marley
albumid = 8
album = Live!
isplaying = true

D) HTC - play to end of playlist

1) sends action: om.htc.music.playbackcomplete with extras:
id = 9
track = Get Up, Stand Up
artist = Bob Marley
albumid = 8
album = Live!
isplaying = true

2) immediately follows with action: com.htc.music.metachanged with
extras:
id = 6
track = She Don't Use Jelly (Flaming Lips cover)
artist = Ben Folds Five
albumid = 6
album = Unknown
isplaying = false

*- note that the playlist in this scenario had 2 tracks in it and
this 2nd action send seems to signal that it's looped back to the
beginning of the list, but hasnt started playing it

HTC player - Observations:
- provides isPlaying key in each action
- always a single action, except for the play to end of playlist case.

*** last.fm Player

A) last.fm - open & play station

1) sends action: fm.last.android.metachanged with extras:
track = Killing In The Name
duration = 314000
artist = Rage Against the Machine
trackAuth = 8260c
loved = false
station = fm.last.api.Station@47cac7f0
album = Rage Against The Machine

B) last.fm - pause current track

1) sends action: fm.last.android.playstatechanged with extras:
track = Killing In The Name
position = 203722
duration = 314000
artist = Rage Against the Machine
trackAuth = 8260c
loved = false
station = fm.last.api.Station@47e71d60
album = Rage Against The Machine

C) last.fm - resume paused track

1) sends action: fm.last.android.metachanged with extras:
track = Killing In The Name
position = 203722
duration = 314000
artist = Rage Against the Machine
trackAuth = 8260c
loved = false
station = fm.last.api.Station@47e71d60
album = Rage Against The Machine

D) last.fm - play thru to next track on station

1) sends action: fm.last.android.metachanged with extras:
track = Be Yourself
duration = 279000
artist = Audioslave
trackAuth = 0031c
loved = false
station = fm.last.api.Station@47d24718
album = Out of Exile

E) last.fm - play to end of playlist
N/A - there is no notion of end with a station

last.fm player Observations:
- duration and position seem to be in ms.
- position is only sent when pausing or resuming
- metachanged is used for new track as well as resuming a paused track
- always a single action sent

*** Rhapsody

A) rhapsody - open & start station

1) sends action: com.rhapsody.playstatechanged with extras:
track = KRS-1 Radio
state = 1
artist = <null>
album = <null>

*- note that "KRS-1 Radio" is the name of the "station" being played

2) immediately follows with action: com.rhapsody.metachanged with
extras:
track = 20 G's
state = 1
artist = KRS-1
album = Official Joints

3) immediately follows with action: com.rhapsody.playstatechanged with
extras:
track = 20 G's
state = 4
artist = KRS-1
album = Official Joints

B) rhapsody - pause current track
Almost N/A there is no pause in Rhapsody, only stop which...

1) sends action: com.rhapsody.playstatechanged with extras:
track = 20 G's
state = 3
artist = KRS-1
album = Official Joints

C) rhapsody - resume
Almost N/A. there is no pause, so after hitting stop, the button
changes to play, which when pressed....

1) sends action: com.rhapsody.playstatechanged with extras:
track = Dead And Gone [Feat. Justin Timberlake]
state = 4
artist = T.I.
album = Paper Trail

*- stop then play basically picks a new song

D) rhapsody - play thru to next track

1) sends action: com.rhapsody.playstatechanged with extras:
track = KRS-1 Radio
state = 1
artist = <null>
album = <null>

2) immediately follows with action: com.rhapsody.metachanged with
extras:
track = The Gangsta, The Killa And The Dope Dealer
state = 1
artist = Westside Connection
album = The N.W.A. Legacy Vol. 1: 1988-1998

E) rhapsody - play to end of playlist
N/A - there is no notion of end with a station

Rhapsody Observations:
- the only player that sends some extras with actual NULLs in some
cases
- sends a playstatechanged action with station info and a state of 1
when there is a delay in selecting next track.
When next track is immediately available, such as stop & start we get
a metachanged alone with no playstatechanged before it.






Stephen Lau

unread,
May 17, 2011, 5:51:50 PM5/17/11
to rdio...@googlegroups.com
Hey,
Thanks for the exhaustive and comprehensive testing :)  Okay, so I'll update our player to fire metachanged & playstatechanged.  In practice, metachanged will always be followed by a playstatechanged Intent - but oh well... it will include the ones I mentioned previously, plus duration and position if they are available (since playstatechanged can fire on pause as you noted, position might be useful).

Here's some sample debug logging:
I/RDIO    ( 3435): [INFO][PlayerService] Broadcast intent for: com.rdio.android.metachanged => Intent { act=com.rdio.android.metachanged (has extras) }
I/RDIO    ( 3435): [INFO][PlayerService]     duration => 120
I/RDIO    ( 3435): [INFO][PlayerService]     rdioSourceKey => a739678
I/RDIO    ( 3435): [INFO][PlayerService]     isPaused => false
I/RDIO    ( 3435): [INFO][PlayerService]     rdioTrackKey => t8743224
I/RDIO    ( 3435): [INFO][PlayerService]     album => Turtleneck & Chain
I/RDIO    ( 3435): [INFO][PlayerService]     track => Mama
I/RDIO    ( 3435): [INFO][PlayerService]     artist => The Lonely Island
I/RDIO    ( 3435): [INFO][PlayerService]     isPlaying => false
I/RDIO    ( 3435): [INFO][PlayerService] clearing paused track state
I/RDIO    ( 3435): [INFO][PlayerService] Registered remote control receiver
I/RDIO    ( 3435): [INFO][PlayerService] Requested audio focus
W/RDIO    ( 3435): [WARNING] [PlayerService] Set foreground
I/RDIO    ( 3435): [INFO][PlayerService] Broadcast intent for: com.rdio.android.playstatechanged => Intent { act=com.rdio.android.playstatechanged (has extras) }
I/RDIO    ( 3435): [INFO][PlayerService]     position => 104000
I/RDIO    ( 3435): [INFO][PlayerService]     duration => 120
I/RDIO    ( 3435): [INFO][PlayerService]     rdioSourceKey => a739678
I/RDIO    ( 3435): [INFO][PlayerService]     isPaused => false
I/RDIO    ( 3435): [INFO][PlayerService]     rdioTrackKey => t8743224
I/RDIO    ( 3435): [INFO][PlayerService]     album => Turtleneck & Chain
I/RDIO    ( 3435): [INFO][PlayerService]     track => Mama
I/RDIO    ( 3435): [INFO][PlayerService]     artist => The Lonely Island
I/RDIO    ( 3435): [INFO][PlayerService]     isPlaying => true

hopefully that should fulfill most developers' needs?  Any additional feedback/comments before I push a new release with this?
cheers,
steve

mjmeyer

unread,
May 18, 2011, 12:45:35 PM5/18/11
to Rdio API
Yep. That's looks awesome.

Thanks!

stfn

unread,
Oct 23, 2012, 9:34:49 AM10/23/12
to rdio...@googlegroups.com
Hi folks,

this is pretty cool that you stick to the "standard" way of broadcasting extends. However, I am looking for an Intent to basically control Rdio with the 3 standard commands as Play/Pause, Next, and Previous track. Is there any way to do this?

Thanks,
stfn

Devin Sevilla

unread,
Oct 31, 2012, 4:29:46 PM10/31/12
to rdio...@googlegroups.com
On Tue, Oct 23, 2012 at 6:34 AM, stfn <stefan....@gmail.com> wrote:
> this is pretty cool that you stick to the "standard" way of broadcasting
> extends. However, I am looking for an Intent to basically control Rdio with
> the 3 standard commands as Play/Pause, Next, and Previous track. Is there
> any way to do this?

Unfortunately we don't have those intents. I've suggested to the
mobile team that we add them.

-- Devin

Kevin Nelson

unread,
Aug 16, 2013, 4:55:05 PM8/16/13
to rdio...@googlegroups.com
Hi all,

Just to tie up loose ends, it looks like we've implemented most of this.


Best,
Kevin


-- -- --
R. Kevin Nelson
API Engineer @ Rdio, Inc.

Guillaume Hachez

unread,
Aug 17, 2013, 12:07:30 PM8/17/13
to rdio...@googlegroups.com
Hi

When Receiving the "com.rdio.android.metachanged" intent, I only get 3 fields (isPaused, IsPlaying, RdioSourceKey). I'm pretty sure there used to be artist, track, etc. in there.
I don't want my app to update everytime the user pauses or resumes the song. Only when the song changes.. So I would prefer to use metachanged over playstatechanged.
Is it something you've changed recently ?

Guillaume Hachez

unread,
Aug 17, 2013, 12:23:16 PM8/17/13
to rdio...@googlegroups.com
This is seems inconsistent with the doc
http://developer.rdio.com/docs/read/Android

Devin Sevilla

unread,
Aug 20, 2013, 12:35:22 PM8/20/13
to rdio...@googlegroups.com
The `com.rdio.android.metachanged` will sometimes be sent when a track
isn't loaded. In you're testing, are you always missing fields or is
it only occasionally?

-- Devin

Guillaume Hachez

unread,
Aug 20, 2013, 6:45:40 PM8/20/13
to rdio...@googlegroups.com
Thank you for replying Devin.
I'm afraid it's systematically missing the fields...
Here's a snippet of my code just in case
http://pastebin.com/kNY5x1T4

Devin Sevilla

unread,
Aug 22, 2013, 6:14:29 PM8/22/13
to rdio...@googlegroups.com
Thanks for the additional information. I'm working with the mobile
team to identify the cause of this issue.

-- Devin
> --
> You received this message because you are subscribed to the Google Groups
> "Rdio API" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to rdio-api+u...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/rdio-api/416599f9-a8a5-447b-87e6-8c6c305f1030%40googlegroups.com.
>
> For more options, visit https://groups.google.com/groups/opt_out.
Reply all
Reply to author
Forward
0 new messages