4

I want to use the new MediaController in Android 5 instead of the old RemoteController for getting the currently playing track and changing the track.

I couldn't find any examples so I tried it by myself.

To get the current MediaController, I have implemented a Class which extends MediaController.Callback and implements MediaSessionManager.OnActiveSessionsChangedListener.

With this method I try to get the current mediaController:

@Override
public void onActiveSessionsChanged(List<MediaController> mediaControllers) {
    Log.i(Util.LOG_TAG, "sessions changed");
    if (mediaControllers != null && !mediaControllers.isEmpty()) {
        this.mediaController = mediaControllers.get(0);
        mediaController.registerCallback(this);
    }
}

But I think I have to register my class. So I have done

MediaSessionManager mgr =  (MediaSessionManager)context.getSystemService(Context.MEDIA_SESSION_SERVICE);
mgr.addOnActiveSessionsChangedListener(this, null);

But when I do this I get an Error that I lack the permission to control media. When I tried to add this permission I noticed that this permission can't be used by third party apps.

What am I doing wrong?

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Martin
  • 155
  • 1
  • 8

2 Answers2

16

UPDATE: A comment clarified that the question was actually about viewing/controlling other apps' MediaSessions, not your own.

Although there is no way to do it directly, due to privacy reasons, you have two options with different levels of available controls/information and different requirements for user interaction:

  1. If you just want to skip musics or play/pause, you can send media button events (via AudioManager) and/or request/release audio focus.

  2. If you need to also know the metadata of what is currently playing, there is a more intrusive way that requires explicit user interaction:

First, create and register a NotificationListenerService:

public class NotificationListener extends NotificationListenerService {
    public NotificationListener() {
    }
}

In AndroidManifest.xml:

<service android:name=".NotificationListener"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
    android:enabled="true" android:exported="true">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>
</service>

Then you will be able to fetch MediaSessions by specifying your NotificationListenerService componentName in the getActiveSessions call:

MediaSessionManager mm = (MediaSessionManager) this.getSystemService(
    Context.MEDIA_SESSION_SERVICE);
List<MediaController> controllers = mm.getActiveSessions(
    new ComponentName(this, NotificationListener.class));
Log.i(TAG, "found " + controllers.size() + " controllers");

One caveat is that the user will need to explicitly give your app Notification access permission, by going to Settings -> Sound & Notification -> Notification access

Original response:

The object that describes your MediaSession and that can be passed along to allow other components/apps to control an existing MediaSesion is the MediaSession.Token. With a token, you can create a MediaController directly, without resorting to the MediaSessionManager. The code for that would be something like:

MediaController mediaController = new MediaController(getActivity(),
    sessionToken);

This doesn't require any special permission. If you are also using the MediaBrowser or the MediaBrowserService, you should get the token associated with the MediaBrowser, by using it's getSessionToken() method.

We just released an example that uses a MediaBrowserService to handle music browsing, playback and media style notification and provides a simple Activity to control playback.

Community
  • 1
  • 1
mangini
  • 4,179
  • 2
  • 19
  • 23
  • 1
    Thanks for your answer (and sorry for the late response) but I think you missunderstood me. I don't have an own media player. I just want to see the currently playing track (e.g. from Poweramp or Google Music) and switch the tracks (next or previous). – Martin Nov 26 '14 at 18:49
  • Oh, ok, sorry for the misunderstanding. You cannot directly connect to MediaSession's that you don't own (or for which you don't know the session tokens), for privacy reasons. There is a way using a NotificationListener, though. I will update the answer with that solution. – mangini Nov 26 '14 at 19:32
  • Thanks, that helps, but I get the error "java.lang.SecurityException: Missing permission to control media" when I try to add the listener with "mm.addOnActiveSessionsChangedListener(this, null);" I do have the permission declared in my Manifest – Martin Nov 27 '14 at 19:43
  • 1
    Ah I found out, i have to do mm.addOnActiveSessionsChangedListener(this, new ComponentName(getApplicationContext().getPackageName(), MusicReceiverLollipop.class.getName())); No it works! Thanks a lot! – Martin Nov 27 '14 at 21:52
  • Can anyone please publish a working sample for this? – android developer Sep 07 '20 at 20:21
  • Hello @mangini the official BOSE app, for example, can read the played track without using NotificationListenerService or other specific public broadcasts. How can they do this? Thanks – Fabio Fracassi Aug 22 '21 at 20:11
2

Check for following permission

private void notificationAccessPermission() {
    if (Settings.Secure.getString(this.getContentResolver(), "enabled_notification_listeners").contains(getApplicationContext().getPackageName())) {
        //service is enabled do something
    } else {
        Intent intent = new
                Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
        startActivity(intent);
    }
}
Sagar
  • 5,273
  • 4
  • 37
  • 50