如果我尝试Spotify应用程序,它确实显示了播放控件。如何使我的MP3应用程序显示控件?
这里是我的MediaPlayBackservice(我一定缺少一些东西!):
public class MediaPlaybackService extends MediaBrowserServiceCompat {
private static final String MEDIA_ROOT_ID = "media_root_id";
private static final String EMPTY_MEDIA_ROOT_ID = "empty_root_id";
private static final String PLAYLIST = "playlist";
private static final String ALBUMS = "albums";
private static final String ALBUM = "album";
private static final String ARTISTS = "artists";
private static final String ARTIST = "artist";
private static final String LOG_TAG = "mediaPlaybackService";
private static final String SONG = "song";
private MediaSessionCompat mediaSession;
private Result<List<MediaBrowserCompat.MediaItem>> saveResult;
private DaoHelper daoHelper;
private MyDatabase db;
List<SongItem> songItemList = new ArrayList<>();
private static final String CURRENT_MEDIA_POSITION = "media_position_key";
private static final int PLAY = 1;
private static final int PAUSE = 2;
private static final int BUFFERING = 3;
private static final int CONNECTING = 4;
private static final int STOPPED = 5;
//MediaPlayer mediaPlayer;
MediaService mediaService;
String TAG = "DJ";
FileContentProvider fileProvider;
@Override
public void onCreate() {
super.onCreate();
db = MyDatabase.getDatabase(this, "/data/data/com.emrick.dj");
if (db == null) {
Log.e(TAG,"In oncreate, db is null");
}
daoHelper = DaoHelper.getInstance(db, null);
if (daoHelper ==null) {
Log.e(TAG,"DaoHelper is null");
}
// Create a MediaSessionCompat
Context context = getBaseContext();
try {
mediaSession = new MediaSessionCompat(context, TAG);
} catch (Exception ex) {
Log.e(TAG,"Exception while creating media session " + ex.getMessage());
}
if (mediaSession == null) {
Log.e(TAG, "initMediaSession: mediaSession = null");
return;
}
mediaSession.setActive(true);
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaService = MediaService.getInstance();
if (mediaService.isPlaying()) {
setMediaPlaybackState(PLAY);
} else {
setMediaPlaybackState(STOPPED);
}
fileProvider = new FileContentProvider();
mediaSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPrepare() {
Log.d(TAG, "prepare called");
}
@Override
public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
Log.d(TAG, "onMediaButtonEvent called: " + mediaButtonIntent);
return false;
}
@Override
public void onPause() {
Log.d(TAG, "onPause called (media button pressed)");
super.onPause();
setMediaPlaybackState(PAUSE);
mediaService.pause();
}
@Override
public void onPlay() {
Log.d(TAG, "onPlay called (media button pressed)");
super.onPlay();
mediaService.resume();
}
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
Log.d(TAG, "onPlayFromMediaId");
String[] tokens = mediaId.split("_");
if (tokens.length > 0) {
if (mediaId.toLowerCase().equals("playlist")) {
if (songItemList != null && songItemList.size() > 0) {
mediaService.play(songItemList.get(0));
}
} else {
String trackIdString = tokens[1];
long trackId = Long.parseLong(trackIdString);
boolean found = false;
for (SongItem item : songItemList) {
if (item.getTrackid() == trackId) {
found = true;
mediaService.play(item);
setMediaPlaybackState(PLAY);
break;
}
}
if (!found) {
Log.d(TAG, "Song item not found in list for trackId " + trackId);
}
}
} else {
Log.e(TAG,"No track id in mediaId? " + mediaId);
}
}
@Override
public void onStop() {
Log.d(TAG, "onStop called (media button pressed)");
super.onStop();
setMediaPlaybackState(STOPPED);
mediaService.stop();
}
@Override
public void onRewind () {
Log.d(TAG, "rewind called");
}
@Override
public void onSkipToNext () {
Log.d(TAG, "skip to next called");
}
@Override
public void onSkipToPrevious () {
Log.d(TAG, "skip to previous called");
}
}) ;
// Set the session's token so that client activities can communicate with it.
setSessionToken(mediaSession.getSessionToken());
}
@Nullable
@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
Bundle rootHints) {
Log.d(TAG,"onGetRoot called");
Bundle extras = new Bundle();
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
return new BrowserRoot(MEDIA_ROOT_ID, extras);
}
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
Log.d(TAG,"onLoadChildren called " + parentId);
saveResult = result;
// Browsing not allowed
if (TextUtils.equals(EMPTY_MEDIA_ROOT_ID, parentId)) {
result.sendResult(null);
return;
}
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
MediaDescriptionCompat.Builder descBuilder = new MediaDescriptionCompat.Builder();
// Check if this is the root menu:
if (MEDIA_ROOT_ID.equals(parentId)) {
MediaBrowserCompat.MediaItem item =
new MediaBrowserCompat.MediaItem(descBuilder
.setMediaId(PLAYLIST)
.build(),MediaBrowserCompat.MediaItem.FLAG_BROWSABLE );
mediaItems.add(item);
result.sendResult(mediaItems);
} else {
// Examine the passed parentMediaId to see which submenu we're at,
// and put the children of that menu in the mediaItems list...
if (parentId.equals((PLAYLIST))) {
Log.d(TAG,"getChildren playlist");
daoHelper.getPlaylistSongs(this,Playlist.PLAYING_LIST);
result.detach();
} else if (parentId.startsWith(SONG)) {
Log.d(TAG, "getChildren song " + parentId);
String idArray[] = parentId.split("_");
String id = idArray[1];
long trackId = Long.parseLong(id);
for (SongItem songItem : songItemList) {
if (songItem.getTrackid() == trackId) {
Uri uri = null;
try {
uri = Uri.parse(songItem.getLocation());
} catch (Exception e) {
throw new RuntimeException(e);
}
Uri artUri = null;
try {
artUri = Uri.parse(songItem.getArtLocation());
} catch (Exception e) {
throw new RuntimeException(e);
}
MediaBrowserCompat.MediaItem item =
new MediaBrowserCompat.MediaItem(descBuilder.setMediaId(id)
.setDescription(songItem.getName())
.setMediaUri(uri)
.setIconUri(artUri)
.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
mediaItems.add(item);
}
}
result.sendResult(mediaItems);
}
}
}
public void setSongItemList(List<SongItem> songList) {
if (songItemList == null) {
songItemList = new ArrayList<SongItem>();
}
songItemList.clear();
songItemList.addAll(songList);
songItemList= songList;
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
MediaDescriptionCompat.Builder descBuilder = new MediaDescriptionCompat.Builder();
int count = 0;
for (SongItem songItem:songItemList) {
String id = SONG + "_" + songItem.getTrackid();
Uri uri = null;
Uri mediaUri = null;
Bitmap art = null;
if (songItem.getArtLocation() != null) {
File f = new File(songItem.getArtLocation());
if (f.exists()) {
//Must use parse, not fromFile!
uri = Uri.parse(songItem.getArtLocation());
uri = fileProvider.mapUri(uri);
} else {
Log.d(TAG,"File doesn't exist:" + songItem.getArtLocation());
}
f = new File(songItem.getLocation());
if (f.exists()) {
//Must use parse, not fromFile!
mediaUri = Uri.parse(songItem.getLocation());
mediaUri = fileProvider.mapUri(mediaUri);
} else {
Log.d(TAG, "File doesn't exist: " + songItem.getLocation());
}
}
Bundle extras = new Bundle();
MediaBrowserCompat.MediaItem item =
new MediaBrowserCompat.MediaItem(descBuilder.setMediaId(id)
.setDescription(songItem.getName())
.setSubtitle(songItem.getArtist())
.setIconUri(uri)
.setMediaUri(mediaUri)
.setMediaId(id)
.setTitle(songItem.getName())
.setExtras(extras)
.build(),MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
mediaItems.add(item);
count++;
if (count > 20) {
break;
}
}
saveResult.sendResult(mediaItems);
}
private void setMediaPlaybackState( int state ) {
PlaybackStateCompat playbackState = null;
switch (state) {
case PLAY:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS )
.setState( PlaybackStateCompat.STATE_PLAYING, 0, 1 )
.build();
break;
case PAUSE:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_PLAY_PAUSE )
.setState(PlaybackStateCompat.STATE_PAUSED, 0, 1)
.build();
break;
case BUFFERING:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_STOP )
.setState(PlaybackStateCompat.STATE_BUFFERING, 0, 1)
.build();
break;
case CONNECTING:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_STOP )
.setState(PlaybackStateCompat.STATE_CONNECTING, 0, 1)
.build();
break;
case STOPPED:
playbackState = new PlaybackStateCompat.Builder()
.setActions( PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID )
.setState(PlaybackStateCompat.STATE_STOPPED, 0, 1)
.build();
break;
}
mediaSession.setPlaybackState( playbackState );
}
}
这是我在AndroidManifest中添加的内容:
<service
android:name=".MediaService"
android:enabled="true"
android:icon="@mipmap/ic_launcher"/>
<meta-data android:name="com.android.automotive"
android:resource="@xml/automotive_app_desc"/>
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<receiver android:name="androidx.media.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
<service android:name="com.emrick.dj.MediaPlaybackService"
android:exported="true"
android:enabled="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
<provider
android:name=".utils.FileContentProvider"
android:authorities="com.emrick.dj"
android:exported="true" />
Media3图书馆作者建议支持该服务中的两个动作,以供遗产和新的浏览器支持:
You didn't post it, but assuming your automotive_app_desc.xml file contains
<?xml version="1.0" encoding="utf-8" ?>
<automotiveAPP>
<uses name="media" />
</automotiveAPP>