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.