由于我在蓝牙开发方面没有多少经验,如果只是获取一下蓝牙设备名称和连接状态那么前面的那篇文章就已经足够了,接下来的内容是转自一个在蓝牙音乐方面颇有经验的开发者的博客,他的这篇文章对我帮助很大.

今天,先说一下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 ImageButtonprivate BluetoothDevice mDevice;private MediaMetadata mediaMetadata;private PlaybackState playbackState;private BluetoothAvrcpPlayerSettings mAvrcpPlayerSettings;private String title;private String artist;private long duration;private String album;//status controlprivate static boolean isA2dpPlaying = false;private static boolean isA2dpConnected = false;//SETTING_EQUALIZER,  SETTING_REPEAT, SETTING_SHUFFLE, SETTING_SCANprivate static int[] current_avrcp_setting_value = {0, 0, 0, 0};private SimpleDateFormat timeFormat = new SimpleDateFormat("mm:ss");private BroadcastReceiver mA2dpReciver = new BroadcastReceiver() {@Overridepublic 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 barif (playbackState != null) {Log.v(TAG, "Get PlaybackState");Log.v(TAG, playbackState.toString());// TODO: 16-11-23 change the progresslong postion = playbackState.getPosition();Log.v(TAG, new SimpleDateFormat("HH:mm:ss").format(postion));sbProgress.setProgress((int) postion);tvCurrent.setText(timeFormat.format(postion));}//update play stateif (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);}}};@Overridepublic 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());}}@Overridepublic 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
    @Overridepublic 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_SINKbluetoothAdapter.getProfileProxy(mActivity, mAvrcpServiceListener, 12); //BluetoothProfile.AVRCP_CONTROLLERmStatusView.setText("Connecting to the AVRCP_CONTROLLER service");return view;}@Overridepublic void onDestroy() {super.onDestroy();mActivity.unregisterReceiver(mA2dpReciver);BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();// TODO mAvrcpController.removeCallback();bluetoothAdapter.closeProfileProxy(11, mA2dpService); // BluetoothProfile.A2DP_SINKbluetoothAdapter.closeProfileProxy(12, mAvrcpController); //BluetoothProfile.AVRCP_CONTROLLER
    }private BluetoothProfile.ServiceListener mA2dpServiceListener = new BluetoothProfile.ServiceListener() {@Overridepublic void onServiceConnected(int profile, BluetoothProfile proxy) {if (profile == 11) { // BluetoothProfile.A2DP_SINKmA2dpService = (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);
//            }
        }@Overridepublic void onServiceDisconnected(int profile) {if (profile == 11) { // BluetoothProfile.A2DP_SINKmA2dpService = null;}}};private BluetoothProfile.ServiceListener mAvrcpServiceListener = new BluetoothProfile.ServiceListener() {@Overridepublic void onServiceConnected(int profile, BluetoothProfile proxy) {if (profile == 12) { // BluetoothProfile.AVRCP_CONTROLLERmStatusView.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));}}}@Overridepublic void onServiceDisconnected(int profile) {if (profile == 12) { //BluetoothProfile.AVRCP_CONTROLLERmStatusView.setText("AVRCP_CONTROLLER disconnected");Log.d(TAG, "AvrcpControllerService disconnected");// TODO mAvrcpController.removeCallback();mAvrcpController = null;}}};private View.OnClickListener listener = new View.OnClickListener() {@Overridepublic 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 testList<BluetoothDevice> devices = mAvrcpController.getConnectedDevices();for (BluetoothDevice device : devices) {BluetoothAvrcpPlayerSettings ps = mAvrcpController.getPlayerSettings(device);Log.d(TAG, "PlayerSetting id:" + ps.getSettings());refreshAvrcpSettings(ps);}}//off -> shuffle -> offpublic 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 -> offpublic 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蓝牙音乐获取歌曲信息相关推荐

  1. Android 9.0 10蓝牙音乐获取歌手、歌曲等信息

    Android 9.0中蓝牙音乐其实实现很简单,蓝牙远程服务处理在Bluetooth中. 修改代码路径: android/packages/apps/Bluetooth./jni/com_androi ...

  2. android蓝牙音乐之AVRCP介绍和使用

    引言 最近做的车载蓝牙音乐开发,遇到很多问题,记录一下.也是到处东拼西凑的,勉强看看吧. AVRCP:Audio/Video Remote Control Profile,音视频远端控制协议,所以该协 ...

  3. Android蓝牙音乐

    一 蓝牙权限 在apk中的AndroidManifest.xml中要有以下语句获得蓝牙相关权限: 注:注意版本 <uses-permission android:name="andro ...

  4. Android蓝牙音乐(基于Android10)

    Android蓝牙音乐 项目背景: 展示蓝牙音乐信息(歌曲名称.播放进度.歌手.歌词等等)和控制操作蓝牙音乐 1.监听蓝牙设备状态 注意点 :需要动态注册广播,和动态申请权限,不然会接收不到广播 cl ...

  5. 乐鑫Esp32学习之旅11 入门 乐鑫esp-adf 音频框架开发,造一个蓝牙耳机,实现切换歌曲,获取歌曲信息等功能。(附带Demo)

    本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途.如有不对之处,请留言,本人及时更改. 1. 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 " ...

  6. php获取酷狗mv播放链接,酷狗歌单获取歌曲信息的接口源码

    <?php /** *@authorYoungxj *@time2018年6月22日 *@todo利用酷狗歌单进行获取酷狗歌曲信息 */ // 访问短网址出现的地址 // http://m.ku ...

  7. 获取歌曲信息歌曲详细信息

    # -*- coding: utf-8 -*- from mutagen.mp3 import MP3 import osclass GetMp3Info():'''获取歌曲信息'''def __in ...

  8. Android 蓝牙开发——Avrcp协议获取歌曲信息(十八)

    通过前面的 Avrcp 协协议的学习,我们知道蓝牙音乐的控制是通过媒体播放器进行的.而媒体播放器的数据发生变化时会通过 MediaSeesion 通知客户端,本篇文章主要分析一下歌曲信息是如何传递的. ...

  9. Python爬虫入门——2. 2爬取酷狗音乐top1-500歌曲信息

    有了第一个程序的基础,我们现在来爬取酷狗音乐top500的歌曲信息.连接http://www.kugou.com/yy/rank/home/1-8888.html 我们第一个程序只爬取了一个页面的数据 ...

最新文章

  1. appium+python自动化45-夜神模拟器连不上(adb server version (36) doesn't match this client (39); killing...)...
  2. 差分能量分析介绍(四)
  3. rgba的兼容性 IE
  4. Saas与传统软件对比
  5. webflux系列--reactor源码(一)
  6. 集合框架(一) ----------Map集合遍历的方法
  7. 通用Key-Value存储系统的存储管理策略解析
  8. 特斯拉在美国召回947辆电动汽车 因倒车影像显示延迟
  9. 实现较低的计时器粒度以重传TCP(RTO):时间轮算法如何减少开销
  10. php+select为空,SELECT时候,如何处理某字段空值?
  11. TEXT到EPUB格式转换
  12. java列举生活中类和对象_趣味解读Python面向对象编程 (类和对象)
  13. 阿里云的应用(day02)
  14. 计算机二级c好考吗,计算机二级MS Office和C语言哪个比较好考?
  15. 宝山区助行业强主体稳增长若干政策措施的实施细则(20条)(征求意见稿)
  16. auto.js悬浮窗按钮的实际使用
  17. 一行命令统计出多个文件夹中的多种类型的代码行数
  18. python#高级变量类型1
  19. LINUX学习之路(学LINUX必看)
  20. 【徐禾 政治经济学概论】 读书笔记 §1.3劳动的二因素:具体劳动和抽象劳动(上.价值质的规定性)

热门文章

  1. “绿多多”绿色资产资讯:良设板+“空间优造”亮相雄安 绿色生态进击!
  2. 嵌入式开发 学习指导
  3. 使用音频分析工具audacity分析wave文件
  4. Day621.Spring Test 常见错误 -Spring编程常见错误
  5. 禅道 mysql 错误
  6. 开源项目推荐:Qt有关的GitHub/Gitee开源项目(★精品收藏★)
  7. ROS机器人的远程启动和控制过程
  8. linux 远程启动WebLogic
  9. 教你用安卓手机以及在Linux下连接校园联通无线宽带
  10. CentOS 7 部署RabbitMQ 服务