Android蓝牙音乐获取歌曲信息
由于我在蓝牙开发方面没有多少经验,如果只是获取一下蓝牙设备名称和连接状态那么前面的那篇文章就已经足够了,接下来的内容是转自一个在蓝牙音乐方面颇有经验的开发者的博客,他的这篇文章对我帮助很大.
今天,先说一下android播放音乐时如何在蓝牙设备上显示歌曲名、歌手、专辑等信息的。
在那个风和日丽、鸟语花香的日子,突然客服Miss Hu发来一个消息,问我说,有用户反馈说在车载蓝牙上播放歌曲看不到歌曲名、歌手、专辑等信息。
我当时虽然不是一脸懵逼,但对这个问题而言确实是只知其一不知其二。
其一,代码中并没有任何直接与蓝牙相关的任何操作;
其二,真不清楚如何控制蓝牙显示的。于是乎,开始深入这个问题......
一、首先,讲一下Android上面蓝牙的部分规范。
截止到现在,世界上已经发布了约40个蓝牙应用规范。先介绍一下最常用的2个。分别是:
1.Advanced Audio Distribution Profile 简称为A2DP(高质量音频分发规范)定义了如何将立体声质量的音频通过流媒体的方式从媒体源传输到接收器上,A2DP有两种应用场景分别是播放和录音。
2.Audio Video Remote Control Profile 简称为AVRCP,定义了蓝牙设备和audio/video控制功能通信的特点和过程。该Profile定义了AV/C数字命令控制集。命令和信息通过AVCTP协议进行传输。
也就是说,连接蓝牙耳机的时候一般使用A2DP协议,而控制和显示通过AVCTP协议实现。
上图来自Google I/O 2013 - Best Practices for Bluetooth Development
那么谷歌是怎么推荐通过Avrcp在蓝牙设备上显示歌曲信息的呢?请看下图
顺便附上视频链接,分秒都给你seek到了,看不了youtube的自己想办法
https://www.youtube.com/watch?v=EC5-cEbr520&feature=youtu.be&t=25m18s
二、那我们去深入一下RemoteControlClient和Avrcp (此时已是身不由己)
RemoteControlClient enables exposing information meant to be consumed by remote controls capable of displaying metadata, artwork and media transport control buttons.
RemoteControlClient暴露信息给具有遥控功能的显示媒体、艺术品和按钮控制设备。(请忽略本人的翻译不准确性)
根据谷歌的说法,先往AudioManager里面注册一个RemoteControlClient实例,然后获取MetaDataEditor,往里面填充信息,然后执行MetaDataEditor.apply(),就是这么easy;
MetaDataEditor是什么? 这个不要问了,随便瞟两眼就知道了。
那么apply里面做了什么呢?
先看一下Android 4.3的源码,这里为什么先说这个版本,因为5.0系统与这个不一样,后面再详细解释。
apply里面根据参数不同,执行了不同的代码,我们只看sendMetadata_syncCacheLock好了。
先从mRcDisplays里面取出DisplayInfoForClient,发送IRemoteControlDisplay.setMetadata接口。
实现IRemoteControlDisplay.setMetadata接口共有下面几个:
我想大家已经看到了,Avrcp实现了这个,但是还需要确认一下。
上面说了,这一切都是从mRcDisplays中来的,mRcDisplays又是什么?
它是一个ArrayList<DisplayInfoForClient>数组。
那么这么里面的DisplayInfoForClient又是哪里来的?
还是在RemoteControlClient这个类里面有一个方法onPlugDisplay里面有mRcDisplays.add(),从此处一一添加进去。
接着往下,onPlugDisplay是在一个MSG_PLUG_DISPLAY消息里面处理的。这个消息是从plugRemoteControlDisplay()这个方法里面执行的。
关键点来了,是谁搞了plugRemoteControlDisplay()这个?
讲到这里,开始跳入framework层代码,不卖关子了,这个是在AudioService里面执行了。
这个plugRemoteControlDisplaysIntoClient_syncRcStack方法是在
AudioService里面注册registerRemoteControlClient的时候调用了。
哈哈,看到这里,是不是想起了google官方介绍如何使用RemoteControlClient的,就是注册到AudioService。具体怎么玩,这里不讲了,因为不是这里的重点。
刚刚上面已经提到IRemoteControlDisplay.setMetadata去更新数据,那么这个IRemoteControlDisplay到底是哪里来的?
于是,上图已经给了答案了,是AudioService中的mRcDisPlays中的。
这个mRcDisPlays里面的内容是通过registerRemoteControlDisplay方法add进去的。
而registerRemoteControlDisplay是在AudioManager中调用的。查找引用,发现
也就是说Avrcp里面注册了这个registerRemoteControlDisplay。
这里就不说其他三个,重点还是蓝牙上面。
registerRemoteControlDisplay是在start里面执行的,start是在make里面执行的,make是在com.android.bluetooth.a2dp.A2dpServic.start里面的,而这个是在ProfileService启动的时候执行的,再往上就不深究了,这里已经有答案了。
这里的结论是IRemoteControlDisplay.setMetadata确实是发给Avrcp里面继承这个接口的元素了。
接下来看com.android.bluetooth.avrcp.Avrcp这个东西。
Avrcp中IRemoteControlDisplayWeak类继承扩展了这个接口,实现了setMetadata这个方法。
setMetadata执行了updateMetadata方法,将歌曲信息更新到内部的mMetadata里面。
至于如何发送,接收端如何显示,这里也不作详细解释了。
也就是说如果Android手机连接蓝牙播放,最好把歌曲信息、歌手、专辑等信息通过RemoteControlClient发送给蓝牙就行了,蓝牙设备就能对应的显示出来歌曲内容和播放状态。
还有一件事,不要忘了,上面讲的是Android4.3的系统。对于5.0以上系统和5.0以下系统是不一样的。
5.0系统RemoteControlClient中的MetaDataEditor.apply()是将MetaData给MediaSession传递过去的,在MediaSession中通过setMetadata(metadata)方法将metadata设置进去。
先调用AudioManager中的registerRemoteControlClient方法注册RemoteControlClient为rcClient。
而AudioManager中的registerRemoteControlClient有三个调用地方:
然后给rcClient注册MediaSessionLegacyHelper单例为helper。
rcClient.registerWithSession(MediaSessionLegacyHelper.getHelper(mContext));
helper是MediaSessionLegacyHelper.getHelper(mContext)获取到的。
registerWithSession中helper添加了监听helper.addRccListener,然后获取MediaSession。
MediaSession是从ArrayMap<PendingIntent, SessionHolder> mSessions中的SessionHolder中取出。
mSessions是在getHolder()方法中put进去的。
也就是在MediaSessionLegacyHelper.addRccListener(),MediaSessionLegacyHelper.removeRccListener(),
MediaSessionLegacyHelper.addMediaButtonListener(),MediaSessionLegacyHelper.removeMediaButtonListener()
四个方法中实现的。
这也就是说为什么google官网要求RemoteCntrolClinet要跟mediabutton的注册要一起了。
提到这里就伤心不已,后来因为优化了mediabutton的注册策略,引起了蓝牙显示问题,唉,都是演技....
哎呀,又刹不住车了,扯远了,回归正题。
现在说说MediaSession.setMetadata(metadata);
mBinder是什么?
mBinder是通过MediaSessionManager.createSession(mCbStub, tag, userId)得到的。
也就是MediaSessionService.createSession()得到的。
进而得知返回的是类型为MediaSessionRecord的实例。
也就是说MediaSession.setMetadata实际是执行了MediaSessionRecord.setMetadata();
MediaSessionRecord.setMetadata()里面发送MSG_UPDATE_METADATA这个消息由MessageHandler处理,调用pushMetadataUpdate()方法,交给cb.onMetadataChanged处理,cb就是ISessionControllerCallback。
再来看com.android.bluetooth.avrcp.Avrcp文件,
在Avrcp.start()时,将一个构建了mRemoteController类注册到AudioService中,
mRemoteController中的RemoteControllerWeak用来监听MediaSession的变化。
当AudioManager.registerRemoteController()时,调用rctlr.startListeningToSessions()。
然后构建出一个SessionsListenerRecord添加到ArrayList类型的mSessionsListeners中。
重点来了,通过MediaSessionManager.getActiveSessions()方法将其设置到MediaController里面。
MediaController已经实现了ISessionControllerCallback接口,当接收到onMetadataChanged()时,发送MSG_UPDATA_METADATA消息执行mCallback.onMetadataChanged();
接着调用MediaControllerCallback.onMetadataChanged()执行onNewMediaMetadata(metadata)。
onNewMediaMetadata内部调用Avrcp中的RemoteControllerWeak.onClientMetadataUpdate()从而将歌曲信息更新到Avrcp中。
这样就将RemoteControlClient和Avrcp连接起来了,使用的是MediaSession。
而Android 4.3使用的则是RemoteControlDisplay,这就是二者的区别,却在app层接口基本是一致的。
好了,到这里基本算是搞定了RemoteControlClient和Avrcp的关系了,也算完成了蓝牙播放显示歌曲信息的功能了。
唉,这会感觉快吐血了。其实这一段反反复复斟酌了好久文字,担心读者搞不清楚,结果差点把自己也绕进去,还好我是这篇文章的原创,这口血已经咽回去了。
三、如果感觉上面太深(各位大神请见谅我的自夸,),同为媒体组的兄弟们还是不要笑话我了,
直接Show Code吧(亮剑)
这里因为业务需要我已经把MediaMetadata信息装进了HashMap,大家还是按照官方要求就行了。
同志们,到这里,你觉得完成了这个需求了吗?
别怕打击,这个时候其实只完成了55%(数据不确定性,完全凭个人感觉捏造),还有很多手机不能显示。为什么呢?
因为RemoteControlClient是从Android 4.0才出现的,那之前的系统呢?
所以,蓝牙肯定还有一种取信息的方式,至少一种。
这种方法是广播com.android.music.metachanged。
直接Show Code吧
Intent mediaIntent =newIntent("com.android.music.metachanged");
mediaIntent.putExtra("artist","歌手名称");
mediaIntent.putExtra("track","歌曲名称");
mediaIntent.putExtra("album","专辑名称");
mediaIntent.putExtra("duration", (Long) duration));
mediaIntent.putExtra("playing", (boolean) playing); //播放状态
getContext().sendBroadcast(mediaIntent); //豆沙绿的背景看起来是不是眼睛舒服多了.......
做完这一步,98%的蓝牙设备都能正常显示了,但是请记住,发送完这个广播之后,如果不小心执行了metadata.clear(), metadata.apply(),你的信息可能就会被清除了。
但是别忘了,还有2%的解决不了,为什么呢?
部分三星手机搞不掂(在官网论坛看到一个说法,跟自身的适配有关);
部分车载蓝牙显示异常,最起码我的车是这样的
(前面增加数字相关的字符串,其实我想说,车载蓝牙可能是一个很混乱的行业,不过腾讯、苹果等土豪已经涉足合作了,也许未来能统一起来)
转自 http://blog.csdn.net/xutao123111/article/details/52979065#comments
再来看下蓝牙技术提供商给到的IVTDemo
public class A2dpTestFragment extends Fragment {
private static String TAG = A2dpTestFragment.class.getSimpleName(); private MainActivity mActivity; private BluetoothA2dpSink mA2dpService;
private BluetoothAvrcpController mAvrcpController;
private TextView mStatusView;
private TextView tvTitle;
private TextView tvAlbum;
private TextView tvArtist;
private TextView tvDuration;
private TextView tvCurrent;
private SeekBar sbProgress; private Button btnStop;
private Button btnConn;
private Button btnDisconn; private ImageView imgPlayPause;
private ImageView imgPrev;
private ImageView imgNext;
private ImageView imgShuffle;
private ImageView imgRepeat;
// private ImageButton private BluetoothDevice mDevice;
private MediaMetadata mediaMetadata;
private PlaybackState playbackState;
private BluetoothAvrcpPlayerSettings mAvrcpPlayerSettings;
private String title;
private String artist;
private long duration;
private String album; //status control
private static boolean isA2dpPlaying = false;
private static boolean isA2dpConnected = false;
//SETTING_EQUALIZER, SETTING_REPEAT, SETTING_SHUFFLE, SETTING_SCAN
private static int[] current_avrcp_setting_value = {0, 0, 0, 0}; private SimpleDateFormat timeFormat = new SimpleDateFormat("mm:ss"); private BroadcastReceiver mA2dpReciver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "action = " + action); if (action.equals(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED)) {
int preState = intent.getIntExtra(BluetoothA2dpSink.EXTRA_PREVIOUS_STATE, 0);
int state = intent.getIntExtra(BluetoothA2dpSink.EXTRA_STATE, 0);
Log.d(TAG, "action = " + action + ", state " + preState + " -> " + state);
if (state == BluetoothProfile.STATE_CONNECTED) {
isA2dpConnected = true;
} else if (state == BluetoothProfile.STATE_DISCONNECTED) {
isA2dpConnected = false;
} } else if (action.equals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (state == BluetoothProfile.STATE_CONNECTED) {
mStatusView.setText("AVRCP_CONTROLLER connected");
mStatusView.append("\r\nAvrcp devices: \r\n");
mStatusView.append(" - " + device.getName() + " " + device.getAddress() + "\r\n");
// FIXME:
mDevice = device;
} if (state == BluetoothProfile.STATE_DISCONNECTED) {
mStatusView.setText("AVRCP_CONTROLLER connected");
mStatusView.append("\r\nAvrcp devices: \r\n");
mStatusView.append(" - " + "no device");
imgRepeat.setAlpha(255);
imgRepeat.setClickable(true);
imgShuffle.setAlpha(255);
imgShuffle.setClickable(true);
} } else if (action.equals(BluetoothAvrcpController.ACTION_TRACK_EVENT)) {
mediaMetadata = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_METADATA);
playbackState = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_PLAYBACK);
if (mediaMetadata != null) {
Log.v(TAG, "Get MediaMetadata");
// TODO: 16-11-23 display the info of media
displayMediaInfo(mediaMetadata);
sbProgress.setMax((int) mediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION));
} // update progress bar
if (playbackState != null) {
Log.v(TAG, "Get PlaybackState");
Log.v(TAG, playbackState.toString());
// TODO: 16-11-23 change the progress
long postion = playbackState.getPosition();
Log.v(TAG, new SimpleDateFormat("HH:mm:ss").format(postion)); sbProgress.setProgress((int) postion);
tvCurrent.setText(timeFormat.format(postion)); } //update play state
if (playbackState != null) {
int state = playbackState.getState();
if (state == PlaybackState.STATE_PLAYING) {
isA2dpPlaying = true;
imgPlayPause.setImageResource(R.drawable.music_button_pause_disable);
}
if (state == PlaybackState.STATE_PAUSED) {
isA2dpPlaying = false;
imgPlayPause.setImageResource(R.drawable.music_button_play_disable);
}
} } else if (action.equals(BluetoothAvrcpController.ACTION_PLAYER_SETTING)) {
mAvrcpPlayerSettings = intent.getParcelableExtra(BluetoothAvrcpController.EXTRA_PLAYER_SETTING);
refreshAvrcpSettings(mAvrcpPlayerSettings);
}
}
}; @Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mActivity = (MainActivity) activity;
} catch (ClassCastException e) {
throw new ClassCastException(this.getClass().getSimpleName()
+ " can be only attached to " + MainActivity.class.getSimpleName());
}
} @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); mDevice = mActivity.mSelectedDevice; IntentFilter it = new IntentFilter();
it.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
it.addAction(BluetoothA2dpSink.ACTION_AUDIO_CONFIG_CHANGED);
it.addAction(BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED);
it.addAction(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED);
it.addAction(BluetoothAvrcpController.ACTION_TRACK_EVENT);
it.addAction(BluetoothAvrcpController.ACTION_PLAYER_SETTING);
mActivity.registerReceiver(mA2dpReciver, it);
} // @Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_avrcp, null); if (mDevice == null) {
Toast.makeText(mActivity, "Please select a device", Toast.LENGTH_SHORT).show();
return null;
} else if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
Toast.makeText(mActivity, "Bluetooth is not enabled", Toast.LENGTH_SHORT).show();
return null;
} findUiViewAndSetListener(view); timeFormat.setTimeZone(TimeZone.getTimeZone("GMT+00:00")); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bluetoothAdapter.getProfileProxy(mActivity, mA2dpServiceListener, 11); //BluetoothProfile.A2DP_SINK
bluetoothAdapter.getProfileProxy(mActivity, mAvrcpServiceListener, 12); //BluetoothProfile.AVRCP_CONTROLLER
mStatusView.setText("Connecting to the AVRCP_CONTROLLER service"); return view;
} @Override
public void onDestroy() {
super.onDestroy();
mActivity.unregisterReceiver(mA2dpReciver); BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// TODO mAvrcpController.removeCallback();
bluetoothAdapter.closeProfileProxy(11, mA2dpService); // BluetoothProfile.A2DP_SINK
bluetoothAdapter.closeProfileProxy(12, mAvrcpController); //BluetoothProfile.AVRCP_CONTROLLER
} private BluetoothProfile.ServiceListener mA2dpServiceListener = new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == 11) { // BluetoothProfile.A2DP_SINK
mA2dpService = (BluetoothA2dpSink) proxy; // mA2dpService.connect(mActivity.mSelectedDevice);
} //init the a2dp state
// if (mA2dpService.isA2dpPlaying(mDevice)) {
// isA2dpPlaying = true;
// imgPlayPause.setImageResource(R.drawable.music_button_pause_disable);
// } else {
// isA2dpPlaying = false;
// imgPlayPause.setImageResource(R.drawable.music_button_play_disable);
// }
} @Override
public void onServiceDisconnected(int profile) {
if (profile == 11) { // BluetoothProfile.A2DP_SINK
mA2dpService = null;
}
}
}; private BluetoothProfile.ServiceListener mAvrcpServiceListener = new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == 12) { // BluetoothProfile.AVRCP_CONTROLLER
mStatusView.setText("AVRCP_CONTROLLER connected");
Log.d(TAG, "AvrcpControllerService connected"); mAvrcpController = (BluetoothAvrcpController) proxy;
// TODO mAvrcpController.setCallback(new AvrcpControllerCallback()); mStatusView.append("\r\nAvrcp devices: \r\n");
List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();
if (devices.isEmpty()) {
mStatusView.append(" - " + "no device");
} else {
for (BluetoothDevice device : devices)
mStatusView.append(" - " + device.getName() + " " + device.getAddress() + "\r\n");
} //init settings (for the selected device)
if (mDevice != null) {
refreshAvrcpSettings(mAvrcpController.getPlayerSettings(mDevice));
}
}
} @Override
public void onServiceDisconnected(int profile) {
if (profile == 12) { //BluetoothProfile.AVRCP_CONTROLLER
mStatusView.setText("AVRCP_CONTROLLER disconnected");
Log.d(TAG, "AvrcpControllerService disconnected");
// TODO mAvrcpController.removeCallback();
mAvrcpController = null;
}
}
}; private View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play_pause_button:
onPlayPauseButtonClick();
break;
case R.id.prevButton:
onPrevButtonClick();
break;
case R.id.nextButton:
onNextButtonClick();
break;
case R.id.stopButton:
onStopButtonClick();
break;
case R.id.img_shuffle:
onShuffleClick();
break;
case R.id.img_repeat:
onRepeatClick();
break;
case R.id.btn_a2dp_conn:
onA2dpConnect();
break;
case R.id.btn_a2dp_disc:
onA2dpDisconnnect();
break;
default:
break;
} }
}; private void findUiViewAndSetListener(View view) {
mStatusView = (TextView) view.findViewById(R.id.status);
tvTitle = (TextView) view.findViewById(R.id.tv_title);
tvAlbum = (TextView) view.findViewById(R.id.tv_album);
tvArtist = (TextView) view.findViewById(R.id.tv_artist);
tvDuration = (TextView) view.findViewById(R.id.tv_duration);
tvCurrent = (TextView) view.findViewById(R.id.tv_current);
sbProgress = (SeekBar) view.findViewById(R.id.sb_progress);
btnStop = (Button) view.findViewById(R.id.stopButton);
imgPlayPause = (ImageView) view.findViewById(R.id.play_pause_button);
imgPrev = (ImageView) view.findViewById(R.id.prevButton);
imgNext = (ImageView) view.findViewById(R.id.nextButton);
imgShuffle = (ImageView) view.findViewById(R.id.img_shuffle);
imgRepeat = (ImageView) view.findViewById(R.id.img_repeat);
btnConn = (Button) view.findViewById(R.id.btn_a2dp_conn);
btnDisconn = (Button) view.findViewById(R.id.btn_a2dp_disc); btnStop.setOnClickListener(listener);
imgPlayPause.setOnClickListener(listener);
imgPrev.setOnClickListener(listener);
imgNext.setOnClickListener(listener);
imgShuffle.setOnClickListener(listener);
imgRepeat.setOnClickListener(listener);
btnConn.setOnClickListener(listener);
btnDisconn.setOnClickListener(listener);
} private void sendCommand(int keyCode) {
if (mAvrcpController == null)
return; List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();
for (BluetoothDevice device : devices) {
Log.d(TAG, "send command to device: " + device.getName() + " " + device.getAddress());
mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_PRESS);
mAvrcpController.sendPassThroughCmd(device, keyCode, BluetoothAvrcp.PASSTHROUGH_STATE_RELEASE);
}
} public void onPlayPauseButtonClick() {
if (isA2dpPlaying) {
sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_PAUSE);
} else if (!isA2dpPlaying) {
sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_PLAY);
}
} public void onStopButtonClick() {
//FIXME
sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_STOP); // int profileState = BluetoothAdapter.getDefaultAdapter().getConnectionState(); // Log.d(TAG + "FANGCX_FANGCX", "profileSate: " + profileState);
} public void onNextButtonClick() {
sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_FORWARD);
} public void onPrevButtonClick() {
sendCommand(BluetoothAvrcp.PASSTHROUGH_ID_BACKWARD);
} private void displayMediaInfo(MediaMetadata metaData) {
title = metaData.getString(MediaMetadata.METADATA_KEY_TITLE);
artist = metaData.getString(MediaMetadata.METADATA_KEY_ARTIST);
duration = metaData.getLong(MediaMetadata.METADATA_KEY_DURATION);
album = metaData.getString(MediaMetadata.METADATA_KEY_ALBUM); tvTitle.setText((title == null ? "Unknown" : title));
tvArtist.setText((artist == null ? "Unknown" : artist));
tvAlbum.setText((album == null ? "Unknown" : album));
tvDuration.setText(timeFormat.format(duration)); Log.d(TAG, "duration : " + new SimpleDateFormat("HH:mm:ss").format(duration));
} public void onGetAttrsButtonClick(View view) {
//for test
List<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();
for (BluetoothDevice device : devices) {
BluetoothAvrcpPlayerSettings ps = mAvrcpController.getPlayerSettings(device);
Log.d(TAG, "PlayerSetting id:" + ps.getSettings()); refreshAvrcpSettings(ps);
}
} //off -> shuffle -> off
public void onShuffleClick() {
int currentValue = current_avrcp_setting_value[2];
if (currentValue == BluetoothAvrcpPlayerSettings.STATE_OFF) {
if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE, BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK)) {
//TODO : if is not supported
}
}
if (currentValue == BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK) {
if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE, BluetoothAvrcpPlayerSettings.STATE_OFF)) {
// TODO: if is note supported
}
}
} //off -> single -> all -> off
public void onRepeatClick() {
int currentValue = current_avrcp_setting_value[1];
if (currentValue == BluetoothAvrcpPlayerSettings.STATE_OFF) {
if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_REPEAT, BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK)) {
// TODO: if is note supported
}
}
if (currentValue == BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK) {
if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_REPEAT, BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK)) {
// TODO: if is note supported
}
}
if (currentValue == BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK) {
if (!setAvrcpSettings(BluetoothAvrcpPlayerSettings.SETTING_REPEAT, BluetoothAvrcpPlayerSettings.STATE_OFF)) {
// TODO: if is note supported
}
}
} private void onA2dpConnect() {
if (mA2dpService != null) {
Toast.makeText(mActivity, "To Conn Dev : " + mDevice.getName(), Toast.LENGTH_LONG).show();
mA2dpService.connect(mDevice);
}
} private void onA2dpDisconnnect() {
if (mA2dpService != null) {
Toast.makeText(mActivity, "To Disc Dev : " + mDevice.getName(), Toast.LENGTH_LONG).show();
mA2dpService.disconnect(mDevice);
}
} private boolean setAvrcpSettings(int type, int value) {
BluetoothAvrcpPlayerSettings settings = new BluetoothAvrcpPlayerSettings(type);
settings.addSettingValue(type, value);
return mAvrcpController.setPlayerApplicationSetting(settings);
} private void refreshAvrcpSettings(BluetoothAvrcpPlayerSettings ps) {
if (ps == null) {
Log.d(TAG, "playerSettings is null");
return;
} int mSettings = ps.getSettings(); if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) == 0) {
imgRepeat.setAlpha(50);
imgRepeat.setClickable(false);
}
if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) == 0) {
imgShuffle.setAlpha(50);
imgShuffle.setClickable(false);
} if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) != 0) {
int equalizerValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE);
Log.d(TAG, "EQUALIZER value: " + equalizerValue);
switch (equalizerValue) {
case BluetoothAvrcpPlayerSettings.STATE_OFF:
break;
case BluetoothAvrcpPlayerSettings.STATE_ON:
break;
default:
return;
}
current_avrcp_setting_value[0] = equalizerValue;
}
if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) != 0) {
int repeatValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_REPEAT);
Log.d(TAG, "REPEAT value: " + repeatValue);
switch (repeatValue) {
case BluetoothAvrcpPlayerSettings.STATE_OFF:
imgRepeat.setImageResource(R.drawable.avrcp_setting_repeat_off);
break;
case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK:
imgRepeat.setImageResource(R.drawable.avrcp_setting_repeat_single);
break;
case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
imgRepeat.setImageResource(R.drawable.avrcp_setting_repeat_all);
break;
case BluetoothAvrcpPlayerSettings.STATE_GROUP:
break;
default:
return;
}
current_avrcp_setting_value[1] = repeatValue;
}
if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) != 0) {
int shuffleValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE);
Log.d(TAG, "SHUFFLE value: " + shuffleValue);
switch (shuffleValue) {
case BluetoothAvrcpPlayerSettings.STATE_OFF:
imgShuffle.setImageResource(R.drawable.avrcp_setting_shuffle_off);
break;
case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
imgShuffle.setImageResource(R.drawable.avrcp_setting_shuffle);
break;
case BluetoothAvrcpPlayerSettings.STATE_GROUP:
break;
default:
return;
}
current_avrcp_setting_value[2] = shuffleValue;
}
if (((byte) mSettings & BluetoothAvrcpPlayerSettings.SETTING_SCAN) != 0) {
int scanValue = ps.getSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE);
Log.d(TAG, "SCAN value: " + scanValue);
switch (scanValue) {
case BluetoothAvrcpPlayerSettings.STATE_OFF:
break;
case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
break;
case BluetoothAvrcpPlayerSettings.STATE_GROUP:
break;
default:
return;
}
current_avrcp_setting_value[3] = scanValue;
}
}
}
以上是我这边所有与蓝牙音乐有关的资源,分享给大家.不难看出,主要是协议建立-->广播监听-->播放控制和更新UI.
Android蓝牙音乐获取歌曲信息的更多相关文章
- 【风马一族_Android】Android 从命令行界面获取手机信息
Android 从命令行界面获取手机信息 1: cmd 打开命令行界面 2:adb devices 获取与电脑相连的设备,例如:模拟器.真机(手机) (右击“标记”,选择设备名称,点击“Ctrl+ ...
- Android 开发 ContentProvider 获取歌曲列表和联系人的样例
ContentProvider(内容提供者)是Android中的四大组件之中的一个. 主要用于对外共享数据.也就是通过ContentProvider把应用中的数据共享给其它应用訪问.其它应用能够通过C ...
- Android 手机卫士--获取联系人信息并显示与回显
前面的文章已经实现相关的布局,本文接着进行相关的功能实现 本文地址:http://www.cnblogs.com/wuyudong/p/5951794.html,转载请注明出处. 读取系统联系人 当点 ...
- Android学习笔记--获取传感器信息
相关资料: 传感器的坐标与读数:http://www.cnblogs.com/mengdd/archive/2013/05/19/3086781.html 传感器介绍及指南针原理:http://www ...
- Android开发程序获取GPS信息步骤
1.获取LOCATION_SERVICE系统服务.2.创建Criteria对象,调用该对象的set方法设置查询条件.3.调用LocationManager.getBestProvider(Criter ...
- Android GPS应用(获取定位信息)
1.介绍 2.使用方法 3.在AndroidManifest.xml文件中添加 <uses-permission android:name="android.permission.AC ...
- Android笔记:获取屏幕信息
像素密度(dpi) float xdpi = getResources().getDisplayMetrics().xdpi;float ydpi = getResources().getDispla ...
- android 获取手机信息工具类
package com.yqy.yqy_listviewheadview; import android.content.Context; import android.telephony.Telep ...
- Python Scrapy的QQ音乐爬虫 音乐下载、爬取歌曲信息、歌词、精彩评论
QQ音乐爬虫(with scrapy)/QQ Music Spider UPDATE 2019.12.23 已实现对QQ音乐文件的下载,出于版权考虑,不对此部分代码进行公开.此项目仅作为学习交流使用, ...
随机推荐
- 【转载】TabLayout 源码解析
原文地址:https://github.com/Aspsine/AndroidSdkSourceAnalysis/blob/master/article/TabLayout%E6%BA%90%E7%A ...
- Java工具类-转换字符编码
package common; /** *字符串处理公用类 */ public class DealString { /** * 转换字符编码 由“iso-8859-1”西文转换为简体中文 */ pu ...
- 货币金额javascript正则表达式
最多保留两位小数,货币金额(不能为0): /^(([1-9]\d*)(\.\d{1,2})?)$|^(0\.0?([1-9]\d?))$/
- 深度学习应用系列(二) | 如何使用keras进行迁移学习,以训练和识别自己的图片集
本文的keras后台为tensorflow,介绍如何利用预编译的模型进行迁移学习,以训练和识别自己的图片集. 官网 https://keras.io/applications/ 已经介绍了各个基于Im ...
- HDU 5967 小R与手机(动态树)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5967 [题目大意] 给出一张图,每个点仅连一条有向边,或者不连, 要求查询在可更改有向边的情况每个 ...
- 【容斥原理】【分解质因数】poj1091 跳蚤
题意转化为求一个线性组合a1*x1+a2*x2+...+an*xn+m*xn+1=1在什么时候可以有解.(ai在1~m的范围内任取) 易得当且仅当gcd(a1,a2,...,an)=1时可能有解. 然 ...
- hdu 4074 Darts
思路:p[n][m][0]表示A为n,B为m,A为先手胜的概率: p[n][m][1]表示A为n,B为m,B为先手胜的概率. d[i]表示圆盘上数字的大小. 容易得 ...
- [转] Spring@Autowired注解与自动装配
1 配置文件的方法 我们编写spring 框架的代码时候.一直遵循是这样一个规则:所有在spring中注入的bean 都建议定义成私有的域变量.并且要配套写上 get 和 set方法. Boss ...
- Struct2_使用Ajax调用Action方法并返回值
一.Login.jsp 1.<head>引入jquery: <script type="text/javascript" src="http://aja ...
- JavaMail入门:创建纯文本、HTML格式的邮件
转自:http://haolloyin.blog.51cto.com/1177454/353849/ 在 http://java.sun.com/products/javamail/ 下载了 Java ...