Android 通过A2DP/AVRCP发送航迹信息
我正试图通过A2DP/AVRCP发送音轨信息。现在,音乐是完美的流媒体,但在“接收器”(即:汽车音频)“曲目信息屏幕”是空白的(这不是使用流行播放器的情况)。Android 通过A2DP/AVRCP发送航迹信息,android,bluetooth,a2dp,avrcp,Android,Bluetooth,A2dp,Avrcp,我正试图通过A2DP/AVRCP发送音轨信息。现在,音乐是完美的流媒体,但在“接收器”(即:汽车音频)“曲目信息屏幕”是空白的(这不是使用流行播放器的情况)。 有什么想法吗?要将曲目元数据发送到主机,您需要发送一个意图 Intent avrcp = new Intent("com.android.music.metachanged"); avrcp.putExtra("track", "song title"); avrcp.putExtra("artist", "artist name");
有什么想法吗?要将曲目元数据发送到主机,您需要发送一个意图
Intent avrcp = new Intent("com.android.music.metachanged");
avrcp.putExtra("track", "song title");
avrcp.putExtra("artist", "artist name");
avrcp.putExtra("album", "album name");
Context.sendBroadcast(avrcp);
播放完歌曲后,用空字符串发送另一个意图作为putExtra方法的第二个参数。此代码对我有效:
private static final String AVRCP_PLAYSTATE_CHANGED = "com.android.music.playstatechanged";
private static final String AVRCP_META_CHANGED = "com.android.music.metachanged";
private void bluetoothNotifyChange(String what) {
Intent i = new Intent(what);
i.putExtra("id", Long.valueOf(getAudioId()));
i.putExtra("artist", getArtistName());
i.putExtra("album",getAlbumName());
i.putExtra("track", getTrackName());
i.putExtra("playing", isPlaying());
i.putExtra("ListSize", getQueue());
i.putExtra("duration", duration());
i.putExtra("position", position());
sendBroadcast(i);
}
根据您的播放状态(暂停/播放/元数据已更改),调用bluetoothNotifyChange具有适当的意图(定义见上文)。我花了很长时间才弄清楚。仅仅传播意图是行不通的。我通过发送意向和实施使AVRCP正常工作 以下是我使用的代码:
public void onCreate(){
super.onCreate();
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
ComponentName rec = new ComponentName(getPackageName(), MyReceiver.class.getName());
mAudioManager.registerMediaButtonEventReceiver(rec);
Intent i = new Intent(Intent.ACTION_MEDIA_BUTTON);
i.setComponent(rec);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
mRemoteControlClient = new RemoteControlClient(pi);
mAudioManager.registerRemoteControlClient(mRemoteControlClient);
int flags = RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
| RemoteControlClient.FLAG_KEY_MEDIA_NEXT
| RemoteControlClient.FLAG_KEY_MEDIA_PLAY
| RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
| RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
| RemoteControlClient.FLAG_KEY_MEDIA_STOP
| RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD
| RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
mRemoteControlClient.setTransportControlFlags(flags);
}
private void onTrackChanged(...) {
String title = ...;
String artist = ...;
String album = ...;
long duration = ...;
Intent i = new Intent("com.android.music.metachanged");
i.putExtra("id", 1);
i.putExtra("track", title);
i.putExtra("artist", artist);
i.putExtra("album", album);
i.putExtra("playing", "true");
sendStickyBroadcast(i);
RemoteControlClient.MetadataEditor ed = mRemoteControlClient.editMetadata(true);
ed.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, title);
ed.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, album);
ed.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, artist);
ed.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, track.getDuration());
ed.apply();
}
public void onDestroy(){
mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
super.onDestroy();
}
如果您只想将元数据信息从手机发送到已连接的AVRCP兼容音频蓝牙设备,而根本不想从蓝牙设备控制您的应用程序,您可能会发现下面的代码很有用。 而且,无需在AudioManager中实现并注册MediaButtonEventReceiver 我还包括API版本21(棒棒糖,5.0)的代码。根据API 21,不推荐使用RemoteControlClient,并鼓励使用 初始阶段:
if (mAudioManager == null) {
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (mRemoteControlClient == null) {
Log.d("init()", "API " + Build.VERSION.SDK_INT + " lower then " + Build.VERSION_CODES.LOLLIPOP);
Log.d("init()", "Using RemoteControlClient API.");
mRemoteControlClient = new RemoteControlClient(PendingIntent.getBroadcast(this, 0, new Intent(Intent.ACTION_MEDIA_BUTTON), 0));
mAudioManager.registerRemoteControlClient(mRemoteControlClient);
}
} else {
if (mMediaSession == null) {
Log.d("init()", "API " + Build.VERSION.SDK_INT + " greater or equals " + Build.VERSION_CODES.LOLLIPOP);
Log.d("init()", "Using MediaSession API.");
mMediaSession = new MediaSession(this, "PlayerServiceMediaSession");
mMediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setActive(true);
}
}
销毁时的清理:
@Override
public void onDestroy() {
super.onDestroy();
[..]
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
} else {
mMediaSession.release();
}
}
@覆盖
公共空间{
super.ondestory();
[..]
if(Build.VERSION.SDK\u INT
这是我的车载立体声系统的外观如果您使用的是Compat版本的组件,则无需控制SDK\u INT。 下面的代码使用许多车载蓝牙设备进行了测试,效果很好。 有些设备不理解某些密钥,因此最好使用可能的密钥。不要忘记在putBitmap之后而不是之前创建.build()
public static void sendTrackInfo() {
if(audioManager == null) {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
if (mMediaSession == null) {
mMediaSession = new MediaSessionCompat(this, "PlayerServiceMediaSession");
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setActive(true);
}
if (audioManager.isBluetoothA2dpOn()) {
try {
String songTitle = getTitle();
String artistTitle = getArtist();
String radioImageUri = getImagesArr().get(0);
String songImageUri = getImagesArr().get(1);
long duration = getDuration();
final MediaMetadataCompat.Builder metadata = new MediaMetadataCompat.Builder();
metadata.putString(MediaMetadataCompat.METADATA_KEY_TITLE, songTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, songTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artistTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, artistTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, radioImageUri);
metadata.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, radioImageUri);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, songImageUri);
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration);
imageCounter = 0;
Glide.with(act)
.load(Uri.parse(radioImageUri))
.asBitmap()
.into(new SimpleTarget<Bitmap>(250, 250) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap);
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, bitmap);
imageCounter = imageCounter + 1;
if(imageCounter == 2) {
mMediaSession.setMetadata(metadata.build());
}
}
});
Glide.with(act)
.load(Uri.parse(songImageUri))
.asBitmap()
.into(new SimpleTarget<Bitmap>(250, 250) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap);
imageCounter = imageCounter + 1;
if(imageCounter == 2) {
mMediaSession.setMetadata(metadata.build());
}
}
});
}
catch (JSONException e) {
e.printStackTrace();
}
}
public static void sendTrackInfo(){
如果(audioManager==null){
audioManager=(audioManager)getSystemService(Context.AUDIO\u服务);
}
如果(mMediaSession==null){
mMediaSession=新的MediaSessionCompat(即“PlayerServiceMediaSession”);
mmediasion.setFlags(MediaSessionCompat.FLAG处理传输控制);
mmediasion.setActive(真);
}
if(audioManager.isBluetoothA2dpOn()){
试一试{
字符串songTitle=getTitle();
字符串artistTitle=getArtist();
字符串radiomageri=getImagesArr().get(0);
字符串songImageUri=getImagesArr().get(1);
long duration=getDuration();
final MediaMetadataCompat.Builder元数据=新的MediaMetadataCompat.Builder();
putString(mediamatadatacompat.metadata\u KEY\u TITLE,songtile);
putString(mediamatadatacompat.metadata\u KEY\u DISPLAY\u TITLE,songtile);
metadata.putString(mediamatadatacompat.metadata\u KEY\u ARTIST,artistTitle);
metadata.putString(mediamatadatacompat.metadata\u KEY\u ALBUM\u ARTIST、artistTitle);
putString(mediamatadatacompat.metadata\u KEY\u ART\u URI,radiomageuri);
putString(mediamatadatacompat.metadata\u KEY\u DISPLAY\u ICON\u URI,radiomageuri);
putString(mediamatadatacompat.metadata\u KEY\u ALBUM\u ART\u URI,songimageri);
metadata.putLong(mediamatadatacompat.metadata\u KEY\u DURATION,DURATION);
imageCounter=0;
滑翔
.load(Uri.parse(放射性图像Uri))
.asBitmap()
.into(新的SimpleTarget(250250){
@凌驾
public void onResourceReady(位图、动画){
metadata.putBitmap(MediaMetadataCompat.metadata\u KEY\u ART,位图);
metadata.putBitmap(MediaMetadataCompat.metadata\u键\u显示\u图标,位图);
imageCounter=imageCounter+1;
如果(imageCounter==2){
mmediasion.setMetadata(metadata.build());
}
}
});
滑翔
.load(Uri.parse(songImageUri))
.asBitmap()
.into(新的SimpleTarget(250250){
@凌驾
public void onResourceReady(位图、动画){
metadata.putBitmap(MediaMetadataCompat.metadata\u KEY\u ALBUM\u ART,位图);
imageCounter=imageCounter+1;
如果(imageCounter==2){
mmediasion.setMetadata(metadata.build());
}
}
});
}
捕获(JSONException e){
e、 printStackTrace();
}
}
}此代码适用于我的蓝牙耳机,但仅显示曲目名称。。。将测试更多。Thx。这段代码在索尼耳机上对我不起作用,但谷歌音乐应用程序仍然可以在耳机上显示歌曲信息。哦,谢谢,我可以在索尼耳机上看到曲目名称,但艺术家/专辑信息,所以我的代码有问题。每当歌曲的状态改变时(播放前,暂停后),我都会调用bluetoothNotifyChange()函数,但歌曲的信息仍然没有显示在耳机上,当然我也不知道为什么:(您使用的是我的精确代码,所有值都用实际数据填充(没有空值)?当我试图省略值时,此代码最初不起作用,我认为所有的意图字段都是必需的。是的,我用所有有效值尝试了你的代码,但仍然没有按预期工作。我甚至尝试使用20个有效值(硬编码为),如谷歌音乐应用程序,但仍然没有成功。P/s:刚刚在你的ServeStream应用程序中发现一个错误:I.putExtra(“ListSize”,getQueue().LENGTH);而我还没有找到另一种方法来实现这一点。这似乎是一种黑客方法。看起来你依赖谷歌音乐应用程序来处理
@Override
public void onDestroy() {
super.onDestroy();
[..]
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
} else {
mMediaSession.release();
}
}
public static void sendTrackInfo() {
if(audioManager == null) {
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
}
if (mMediaSession == null) {
mMediaSession = new MediaSessionCompat(this, "PlayerServiceMediaSession");
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setActive(true);
}
if (audioManager.isBluetoothA2dpOn()) {
try {
String songTitle = getTitle();
String artistTitle = getArtist();
String radioImageUri = getImagesArr().get(0);
String songImageUri = getImagesArr().get(1);
long duration = getDuration();
final MediaMetadataCompat.Builder metadata = new MediaMetadataCompat.Builder();
metadata.putString(MediaMetadataCompat.METADATA_KEY_TITLE, songTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, songTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artistTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, artistTitle);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, radioImageUri);
metadata.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, radioImageUri);
metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, songImageUri);
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration);
imageCounter = 0;
Glide.with(act)
.load(Uri.parse(radioImageUri))
.asBitmap()
.into(new SimpleTarget<Bitmap>(250, 250) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, bitmap);
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, bitmap);
imageCounter = imageCounter + 1;
if(imageCounter == 2) {
mMediaSession.setMetadata(metadata.build());
}
}
});
Glide.with(act)
.load(Uri.parse(songImageUri))
.asBitmap()
.into(new SimpleTarget<Bitmap>(250, 250) {
@Override
public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap);
imageCounter = imageCounter + 1;
if(imageCounter == 2) {
mMediaSession.setMetadata(metadata.build());
}
}
});
}
catch (JSONException e) {
e.printStackTrace();
}
}