アプリに Android Auto サポートを実装していますが、正しく動作しません。Google のドキュメントといくつかのチュートリアルに従いましたが、音声が機能しません。コントロール、アルバム アート、アーティスト名など、すべて問題なく表示されます。
奇妙な動作: Auto エミュレーターを介して Spotify アプリを実行し、曲を再生した後、自分のアプリに移動してオーディオを再生しようとすると、機能します!
私が実装したサービスクラスは次のとおりです。
/**
* Created by FelipeRRM on 8/13/2016.
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class AutoMediaBrowserService extends MediaBrowserServiceCompat {
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;
private static final String MY_MEDIA_ROOT_ID = "meuiddaraiz";
MediaSessionCompat mSession;
@Override
public void onCreate() {
super.onCreate();
mSession = new MediaSessionCompat(this, "session tag");
setSessionToken(mSession.getSessionToken());
// Set a callback object to handle play control requests, which
// implements MediaSession.Callback
mSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
super.onPlay();
playMedia( PreferenceManager.getDefaultSharedPreferences( getApplicationContext() ).getInt( CURRENT_MEDIA_POSITION, 0 ), null );
}
//This is called when the pause button is pressed, or when onPlayFromMediaId is called in
//order to pause any currently playing media
@Override
public void onPause() {
super.onPause();
setMediaPlaybackState(PAUSE);
pauseMedia();
}
@Override
public void onStop() {
super.onStop();
setMediaPlaybackState(STOPPED);
if( mediaPlayer != null ) {
pauseMedia();
mediaPlayer.release();
PreferenceManager.getDefaultSharedPreferences(AutoMediaBrowserService.this).edit().putInt( CURRENT_MEDIA_POSITION,
0 ).commit();
}
}
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
super.onPlayFromMediaId(mediaId, extras);
mSession.setMetadata(new MediaMetadataCompat.Builder()
.putText(MediaMetadataCompat.METADATA_KEY_TITLE, "primeira musica")
.putText(MediaMetadataCompat.METADATA_KEY_ARTIST, "Joãozinho das Rezas")
.putText(MediaMetadataCompat.METADATA_KEY_GENRE, "Gospel")
.putText(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, "http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/01.jpg")
.build()
);
playMedia(0, mediaId);
}
@Override
public void onPlayFromSearch(String query, Bundle extras) {
super.onPlayFromSearch(query, extras);
}
});
mSession.setActive(true);
}
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;
}
mSession.setPlaybackState( playbackState );
}
private void playMedia(final int position, String id ) {
setMediaPlaybackState(BUFFERING);
if( mediaPlayer != null )
try {
mediaPlayer.reset();
}
catch (Exception e){
mediaPlayer = new MediaPlayer();
e.printStackTrace();
}
else
mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource("http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/Mateus-01.mp3");
mediaPlayer.prepareAsync();
}
catch (Exception e){
e.printStackTrace();
}
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
if( position > 0 )
mediaPlayer.seekTo( position );
setMediaPlaybackState(PLAY);
}
});
mediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mediaPlayer, int state, int extra) {
switch (state){
case MediaPlayer.MEDIA_INFO_BUFFERING_START:{
Log.d("MediaPlayer","StartBuffer");
setMediaPlaybackState(BUFFERING);
break;
}
case MediaPlayer.MEDIA_INFO_BUFFERING_END:{
Log.d("MediaPlayer","EndBuffer");
setMediaPlaybackState(PLAY);
}
}
return true;
}
});
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mediaPlayer, int state, int extra) {
Log.e("MediaPlayer Error", String.valueOf(state));
return false;
}
});
}
private void pauseMedia() {
if( mediaPlayer != null ) {
mediaPlayer.pause();
PreferenceManager.getDefaultSharedPreferences( this ).edit().putInt( CURRENT_MEDIA_POSITION,
mediaPlayer.getCurrentPosition() ).commit();
}
}
@Nullable
@Override
public BrowserRoot onGetRoot(String s, int i, Bundle bundle) {
return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
}
@Override
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
// Check if this is the root menu:
if (parentId.equals(MY_MEDIA_ROOT_ID)) {
mediaItems.add(new MediaBrowserCompat.MediaItem(new MediaDescriptionCompat.Builder().setMediaId("iddopastor").setTitle("Pastor").build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE));
} else if (parentId.equals("iddopastor")) {
mediaItems.add(new MediaBrowserCompat.MediaItem(new MediaDescriptionCompat.Builder().setMediaId("iddopastor2").setTitle("Pastor 2").build(), MediaBrowserCompat.MediaItem.FLAG_BROWSABLE));
// examine the passed parentMediaId to see which submenu we're at,
// and put the children of that menu in the mediaItems list
}
else if(parentId.equals("iddopastor2")){
mediaItems.add(new MediaBrowserCompat.MediaItem(new MediaDescriptionCompat.Builder().setMediaId("genealogia")
.setTitle("Tomé filho de José")
.setMediaUri(Uri.parse("http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/Mateus-01.mp3"))
.setIconUri(Uri.parse("http://70.38.6.72/~vivafe/web/wp-content/uploads/2016/08/01.jpg"))
.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE));
}
result.sendResult(mediaItems);
}
@Override
public void onDestroy() {
mSession.release();
if( mediaPlayer != null ) {
pauseMedia();
mediaPlayer.release();
PreferenceManager.getDefaultSharedPreferences( this ).edit().putInt( CURRENT_MEDIA_POSITION,
0 ).commit();
}
super.onDestroy();
}
}
長いコードで申し訳ありませんが、エラーはこのクラスのどこかにある可能性があります!
これも AndroidManifest.xml に追加しました。
<meta-data android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" />
<service android:name=".Auto.AutoMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name=
"android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
そして、私は res\xml\automotive_app_desc.xml にこれを持っています:
<automotiveApp>
<uses name="media" />
</automotiveApp>