Starting from Musixmatch 4.0 for Android, we launched an extremely powerful and exciting feature called FloatingLyrics™. With FloatingLyrics™, we offer a seamless integration to every music player in the Android ecosystem by popping up a floating window that shows the lyrics to the user.
FloatingLyrics™ has the following capabilities, with many more to come:
Enabling FloatingLyrics™ for your application is fairly straightforward, as it is entirely controlled via broadcast intents. Basically, FloatingLyrics™ reacts to these two events:
com.android.music.metachanged
the metadata has changed, which typically means that a new song is now playing
com.android.music.playstatechanged
the playstate has changed, indicating that a playback event has occurred (play/pause, seek, stop, next/prev, ..)
The following list contains all the extra fields that the Intent must contain:
track | Title of the current track (String) |
artist | Artist of the current track (String) |
album | Album of the current track (String) |
duration | Duration of the current track (Long, milliseconds) |
position | The current playback position of the track (Long, milliseconds) |
playing | Playback status of the current track (Boolean, whether is playing or paused) |
scrobbling_source | Package name of the application that sends the Intent (String) |
Let's see how to create a sample intent for these events. This is how the intent for a "new" song should look like:
Intent intent = new Intent();
intent.setAction("com.android.music.metachanged");
Bundle bundle = new Bundle();
// put the song's metadata
bundle.putString("track", "Torn");
bundle.putString("artist", "Natalie Imbruglia");
bundle.putString("album", "Left of the Middle");
// put the song's total duration (in ms)
bundle.putLong("duration", 245000L); // 4:05
// put the song's current position
bundle.putLong("position", 1L); // beginning of the song
// put the playback status
bundle.putBoolean("playing", true); // currently playing
// put your application's package
bundle.putString("scrobbling_source", "com.yourcompany.yourapp");
intent.putExtras(bundle);
context.sendBroadcast(intent);
And this is a sample intent for the same song, which has been put to pause at 30 seconds:
Intent intent = new Intent();
intent.setAction("com.android.music.playstatechanged");
Bundle bundle = new Bundle();
// put the song's metadata
bundle.putString("track", "Torn");
bundle.putString("artist", "Natalie Imbruglia");
bundle.putString("album", "Left of the Middle");
// put the song's total duration (in ms)
bundle.putLong("duration", 245000L); // 4:05
// put the song's current position
bundle.putLong("position", 30000L); // 0:30
// put the playback status
bundle.putBoolean("playing", false); // currently playing
// put your application's package
bundle.putString("scrobbling_source", "com.yourcompany.yourapp");
intent.putExtras(bundle);
context.sendBroadcast(intent);
The snippets are pretty self-explanatory. However, there are a few points worth noting:
You can also use the following command line instructions, which match the previous snippets:
adb shell am broadcast -a com.android.music.metachanged -e track "Torn" -e artist "Natalie Imbruglia" -e album "Left of the Middle" -e scrobbling_source "com.yourcompany.yourapp" --el position 1 --ez playing true
adb shell am broadcast -a com.android.music.playstatechanged -e track "Torn" -e artist "Natalie Imbruglia" -e album "Left of the Middle" --el position 30000 --ez playing false -e scrobbling_source "com.yourcompany.yourapp"
Please note that, depending on your ADB version, whitespaces might need to be escaped, as you can see here:
adb shell am broadcast -a com.android.music.metachanged -e track "Torn" -e artist "Natalie\ Imbruglia" -e album "Left\ of\ the\ Middle" --el position 1 --ez playing true -e scrobbling_source "com.yourcompany.yourapp"
For devices that run on API 19 or above, Musixmatch receives notifications every time a media session is opened/changed/closed. For Musixmatch to correctly display synced lyrics, your application should provide the following information to the media session:
track | Title of the current track (String) |
artist | Artist of the current track (String) |
album | Album of the current track (String) |
duration | Duration of the current track (Long, milliseconds) |
position | The current playback position of the track (Long, milliseconds) |
playing | Playback status of the current track (Boolean, whether is playing or paused) |
scrobbling_source | Package name of the application that controls the media session (String) |
For a full overview on the MediaSessionCompat APIs, please refer to the documentation.
Testing your integration is super simple. Just install Musixmatch on your Android device and play a song from your application: since FloatingLyrics™ is enabled by default, you should see it popping up on your screen.
Alternatively, you can send the command line test instructions to check that FloatingLyrics™ is working as expected.
There are a few ways of troubleshooting you integration.
LOGCAT
Keep an eye on on the log and look for warnings/errors. For instance, this error appears if you pass a String value for the position, rather than a Long value:
W/Bundle﹕ Key playing expected Boolean but value was a java.lang.String. The default value false was returned.
W/Bundle﹕ Attempt to cast generated internal exception:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
at android.os.Bundle.getBoolean(Bundle.java:864)
at com.designfuture.music.scrobbler.BuiltInMusicAppReceiver.parseIntent(BuiltInMusicAppReceiver.java:65)
at com.designfuture.music.scrobbler.AbstractPlayStatusReceiver.onReceive(AbstractPlayStatusReceiver.java:135)
[...]
LYRICS ARE NEVER SYNCED
The fact that lyrics do not appear as synced (they don't scroll automatically) may indicate an issue on the "position" extra in your intent. Double check that it is a Long value and try hardcoding it to 1L.
Please note, however, that not all the lyrics we have are synced: be sure to test with the top hits of the moment, which are typically synced.