13

I have an Android Auto app. I would like to take advantage of pagination for browsing within the app. It seems that you can set EXTRA_PAGE and EXTRA_PAGE_SIZE by getting a reference to the MediaBrowserCompat and passing those constants in .subscribe(). However, I can't figure out how to get a reference to the MediaBrowserCompat that Android Auto Audio uses in order to call .subscribe().

This seems way too complicated for something that should be simple, am I just overthinking things?

I'm_With_Stupid
  • 1,112
  • 4
  • 16
  • 35

2 Answers2

1

How to get the reference to the Android Auto MediaBrowser? For it, you suppose to know the package name and the class name (if you are trying to bind it outside the app). If you don't know the these details, you can just get it all from the package manager.

final Intent providerIntent = 
new Intent(MediaBrowserService.SERVICE_INTERFACE);
List<ResolveInfo> mediaApps = 
    mPackageManager.queryIntentServices(providerIntent, 0);
for (ResolveInfo info : mediaApps) {
    new MediaBrowserCompat(context, 
        new ComponentName(info.serviceInfo.packageName, 
        info.serviceInfo.name), mConnectionCallbacks, null);
}

How to set EXTRA_PAGE and EXTRA_PAGE_SIZE?

Bundle bundle = new Bundle();
bundle.putInt(MediaBrowserCompat.EXTRA_PAGE, 1);
bundle.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, 1);
mBrowser.subscribe("__SOME_ID__", bundle, callback);

If you are overriding the onLoadChildren() with the bundle on your service side than you have to handle the paging logic too. You can bypass it by just overriding onLoadChildren without bundle.

Shailendra Yadav
  • 1,822
  • 1
  • 12
  • 16
0

Please note: Usually in Android when you see compat suffix at the end, it is new (enhanced) version of one without compat.

MediaActivity is not special Activity, it's a kind of Activity, which is designed to play the musics. And, as you asked MediaBrowserCompat and MediaBrowserServiceCompat, I changed my default architecture (Architecture 2 presented below), to Architecture 1 (Architecture 1 presented below which is new introduced in version 22), just to give the exact answer that you asked.

Two Architectures are:

Architecture1)

1) MediaActivity <--uses----> MediaBrowserCompat <---uses--> MediaServiceBrowserCompat <----> MediaSessionCompat <---> MediaSession <--pass session token --> MediaControllerCompat <-- it also passes token to create --> MediaController /* latest API introduced in 22 */

2)

 <service android:name=".MyMediaBrowserServiceCompat"
              android:label="@string/service_name" >
         <intent-filter>
             <action android:name="android.media.browse.MediaBrowserService" />
         </intent-filter>
     </service>

3) Uses MediaSessionCompat to control music playing.

4) Once a session is created the owner of the session may pass its session token to other processes to allow them to create a MediaControllerCompat to interact with the session.

5) A MediaController can be created if you have a MediaSessionCompat.Token from the session owner.

Now, You created MediaController

From here, both Architecture do the same thing.

Architecture2)

1)MediaActivity <--uses----> MediaBrowser <---uses--> MediaServiceBrowser /* old one introduced in 21. This is default */

2)

 <service android:name=".MyMediaBrowserService"
          android:label="@string/service_name" >
     <intent-filter>
         <action android:name="android.media.browse.MediaBrowserService" />
     </intent-filter>
 </service>

3) Uses MediaSession to control music playing

4) Once a session is created the owner of the session may pass its session token to other processes to allow them to create a MediaController to interact with the session.

5) A MediaController can be created through MediaSessionManager if you hold the "android.permission.MEDIA_CONTENT_CONTROL" permission or are an enabled notification listener or by getting a MediaSession.Token directly from the session owner.

Now, You created MediaController

From here, both Architecture do the same thing.

Note: By default, when you create Android Auto project, it still uses Architecture 2, But, I'm using Architecture 1 because you asked to do via MediaBrowserCompat. So, you can be a little confused here.

Since exact implementation is kind of long, so I'm providing exact link where you kind find the implementation via MediaBrowserCompat way (Architecture 1) https://github.com/googlesamples/android-MediaBrowserService

I am posting basic code here based on the Architecture -- because it's a new one introduced in version 22 -- just to enough to show how you can get MediaBrowserCompat reference.

mConnectionCallbacks is the main thing that connectes MediaServiceBrowserCompat with MediaBrowserCompat. MediaBrowserCompat controls the media provided by the MediaServiceBrowserCompat. Activity, which is suitablely designed to control the media is called MediaActivity. MediaActivity uses MediaBrowserCompat to control media (eg, volume, play change etc). MediaBrowserCompat setups mConnectionCallbacks which further has onConnected() etc methods where you can put your own logic there.

public class MyMediaActivity /* can be any activity */ extends AppCompatActivity {

private MediaBrowserCompat mMediaBrowserCompat; /* your reference here */

MediaControllerCompat mediaController = MediaControllerCompat.getMediaController(MyMediaActivity.this);
MediaControllerCompat.Callback controllerCallback =
        new MediaControllerCompat.Callback() {
            @Override
            public void onMetadataChanged(MediaMetadataCompat metadata) {
            }

            @Override
            public void onPlaybackStateChanged(PlaybackStateCompat state) {
            }
        };


private MediaBrowserCompat.ConnectionCallback mConnectionCallbacks =
        new MediaBrowserCompat.ConnectionCallback() {
            @Override
            public void onConnected() {

                // Get the token for the MediaSession
                MediaSessionCompat.Token token = mMediaBrowserCompat.getSessionToken();

                // Create a MediaControllerCompat
                MediaControllerCompat mediaController =
                        null;
                try {
                    mediaController = new MediaControllerCompat(MyMediaActivity.this, // Context
                            token);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                // Save the controller
                MediaControllerCompat.setMediaController(MyMediaActivity.this, mediaController);

                // Finish building the UI
                buildTransportControls();
            }

            @Override
            public void onConnectionSuspended() {
                // The Service has crashed. Disable transport controls until it automatically reconnects
            }

            @Override
            public void onConnectionFailed() {
                // The Service has refused our connection
            }
        };

void buildTransportControls() {
    /* you can define your view to control music here */
    /* your stuffs here */
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Display the initial state
    MediaMetadataCompat metadata = mediaController.getMetadata();
    PlaybackStateCompat pbState = mediaController.getPlaybackState();

    // Register a Callback to stay in sync
    mediaController.registerCallback(controllerCallback);


    mConnectionCallbacks = new MediaBrowserCompat.ConnectionCallback();

    /* your MediaBrowserCompat instance reference here*/
    mMediaBrowserCompat = new MediaBrowserCompat(this,
            new ComponentName(this, MyMediaBrowserServiceCompat.class),
            mConnectionCallbacks,
            null); // optional Bundle



/* now you can call subscribe() callbacks via mMediaBrowserCompat.subscribe(.....) anywhere inside this Activity's 
            lifecycle callbacks
             */


}

@Override
public void onStart() {
    super.onStart();
    mMediaBrowserCompat.connect();
}

@Override
public void onStop() {
    super.onStop();
    // (see "stay in sync with the MediaSession")
    if (MediaControllerCompat.getMediaController(MyMediaActivity.this) != null) {
        MediaControllerCompat.getMediaController(MyMediaActivity.this).unregisterCallback(controllerCallback);
    }


      mMediaBrowserCompat.disconnect();

    }
}

And, now, you can create MediaBrowserServiceCompat /* note MediaBrowserServiceCompat is service */ as below.

public class MyMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
/* various calbacks here */
}

For more research, you can read this link, which exactly explains the logic I presented above. https://developer.android.com/guide/topics/media-apps/audio-app/building-a-mediabrowser-client.html#connect-ui-and-mediacontroller

Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64
  • Yes, this is normally the correct way to do this. But this does not work for an app that implements a MediaServiceBrowserCompat through Android Auto. In Android Auto the MediaActivity already exists but it's part of the Android Auto app, which is closed source. If I were able to use my own Media Activity it would be easy to get a reference to the MediaBrowserCompat because I would be the one creating the instance of MediaBrowserCompat. Please read up here: https://developer.android.com/training/auto/audio/index.html – I'm_With_Stupid Jul 29 '17 at 20:15
  • @I'm_With_Stupid, oh yeah man, you are right. Here is the exact practical application. I completely understood. This seems very easy. Have a look at it. https://github.com/googlesamples/android-MediaBrowserService , I will modify my answer soon. – Uddhav P. Gautam Jul 29 '17 at 21:44
  • Here is the exact code. https://github.com/googlesamples/android-MediaBrowserService/blob/master/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java – Uddhav P. Gautam Jul 29 '17 at 21:45
  • @I'm_With_Stupid, MediaActivity is not special Activity, it's a kind of Activity, which is designed to play the musics. And, as you asked MediaBrowserCompat and MediaBrowserServiceCompat, I changed my default architecture (Architecture 2 presented above), to Architecture 1 (new Architecture), just to give the exact answer that you asked. – Uddhav P. Gautam Jul 30 '17 at 13:35
  • I'm still confused. In Android Auto the activity where the Media is being controlled, the "Media Activity" has been created already by the Android Auto app. _As per the [documentation](https://developer.android.com/training/auto/audio/index.html#implement_browser): When your app's audio services are requested by a user through Android Auto, Android Auto contacts your app's media browser service. In your implementation of the onCreate() method, you must create and register a MediaSession object and its callback object._ – I'm_With_Stupid Jul 31 '17 at 05:14
  • So, since I don't have access to this activity, I can't fetch a reference to the MediaBrowser there. I only have the callbacks available in the MediaBrowserService since Android Auto is the one calling my MediaBrowserService and the one dealing with anything UI related. – I'm_With_Stupid Jul 31 '17 at 05:16
  • Yes, Android by default does but it is not must. Please go to the link that I provided above. They have not used MediaBrowser there instead they used MediaBrowserCompat. – Uddhav P. Gautam Jul 31 '17 at 05:38
  • Yes but I'm trying to implement this with Android Auto. In the code you've linked they don't have access to the Android Auto MediaBrowser/Mediabrowsercompat either – I'm_With_Stupid Aug 01 '17 at 00:51
  • @Because, please look there, they have used MediaBrowserCompat. Please look at https://github.com/googlesamples/android-MediaBrowserService/blob/master/Application/src/main/java/com/example/android/mediabrowserservice/BrowseFragment.java 67 line, they have used MediaBrowserCompat. We don't use MediaBrowser/MediaBrowserService because that is old. We now use MediaBrowserCompat/MediaBrowserCompat. Please try to understand the architecture that I explained to you. – Uddhav P. Gautam Aug 01 '17 at 00:55
  • First, instead of getting MediaBrowserCompat directly from the Activity, they used BrowserFragment because this is the correct way to do. This is the google sample, please learn the way they did there. Android Studio, still uses the old techniques. – Uddhav P. Gautam Aug 01 '17 at 00:57
  • I think we are talking about two different things. You are concerned about the difference between MediaBrowser and MediaBrowserCompat and fetching a reference to that from an activity. I am trying to fetch a reference of the MediaBrowserCompat used by Android Auto, who's activity is closed off. The Google Sample you linked does fetch a reference to MediaBrowserCompat, but when it comes time to connect to the Android Auto app it uses the Android Auto MediaBrowserCompat instead of its own. That is the reference I and other people are looking for. – I'm_With_Stupid Aug 01 '17 at 17:51
  • 1
    @I'm_With_Stupid, would you please send your whole project zip in infofuniversities@gmail.com, I will definitely try to help you – Uddhav P. Gautam Aug 01 '17 at 17:54
  • 1
    @I'm_With_Stupid, thanks, I understand, I'm going through it. – Uddhav P. Gautam Aug 01 '17 at 18:08
  • Do you understand the problem better now? – I'm_With_Stupid Aug 14 '17 at 15:30