Android耳机线控详解,蓝牙耳机按钮监听(仿酷狗线控效果)

转载请注明出处:http://blog.csdn.net/fengyuzhengfan/article/details/46461253

当耳机的媒体按键被单击后,Android系统会发出一个广播,该广播的携带者一个Action名为MEDIA_BUTTON的Intent。监听该广播便可以获取手机的耳机媒体按键的单击事件。

在Android中有个AudioManager类,该类会维护MEDIA_BUTTON广播的分发,所以要实现耳机按键监听需要向AudioManager注册一个用于接收耳机按键单击事件的接收器:

AudioManager audioManager = (AudioManager)context
      .getSystemService(Context.AUDIO_SERVICE);
ComponentName name = newComponentName(context.getPackageName(),
      MediaButtonReceiver.class.getName());
audioManager.registerMediaButtonEventReceiver(name);

该方法的原型为:

publicvoid registerMediaButtonEventReceiver (PendingIntent eventReceiver)

Added in API level 18

Registera component to be the sole receiver of MEDIA_BUTTON intents. This is like registerMediaButtonEventReceiver(android.content.ComponentName),
but allows the buttons to go to any PendingIntent. Note that you shouldonly use this form if you know you will continue running for the full timeuntil unregistering the PendingIntent.

Parameters


eventReceiver


target that will receive media button intents. The PendingIntent will be sent an ACTION_MEDIA_BUTTON event
when a media button action occurs, with EXTRA_KEY_EVENT added
and holding the key code of the media button that was pressed.

从API注释中可知:

1、 在AudioManager对象注册一个MediaoButtonRecevie,使它成为MEDIA_BUTTON的唯一接收器,也就是说只有我能收到,其他的都收不到这个广播了,否则的话大家都收到会照成一定的混乱;

2、该广播必须在AndroidManifest.xml文件中进行声明,否则就监听不到该MEDIA_BUTTON广播了。

注,因为当我们注册了AudioManager媒体按键的接收器,并且该接收器是媒体按键的唯一接收器,所以要在不使用按键监听的时候取消该注册:

AudioManager audioManager = (AudioManager)context    .getSystemService(Context.AUDIO_SERVICE);
ComponentName name = newComponentName(context.getPackageName(),    MediaButtonReceiver.class.getName());
audioManager.unregisterMediaButtonEventReceiver(name);

当耳机媒体键发生单击事件的时候Android系统会发出两次广播,第一次是按键按下去的时候,第二次是松开按键的时候,为了能够准确的获知用户单击了几次媒体键,我们只需要在按键松开的时候处理单击事件即可:

KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); //获得KeyEvent对象
if(keyEvent.getAction()== KeyEvent.ACTION_UP){
//在这里处理单击事件
}

下面就分别讲解一下为了实现线控效果所用到的几个类:

1.   耳机线控管理工具类HeadSetUtil:

package com.jph.lc;

import android.content.ComponentName;
import android.content.Context;
import android.media.AudioManager;
import android.util.Log;
/**
 * 耳机线控管理工具类 单例
 * @author JPH
 * @date 2015-6-9 下午4:03:45
 */
public class HeadSetUtil {

	private static HeadSetUtil headSetUtil;
	private OnHeadSetListener headSetListener = null;

	public static HeadSetUtil getInstance() {
		if (headSetUtil == null) {
			headSetUtil = new HeadSetUtil();
		}
		return headSetUtil;
	}

	/**
	 * 设置耳机单击双击监听接口 必须在open前设置此接口,否则设置无效
	 * @param headSetListener
	 */
	public void setOnHeadSetListener(OnHeadSetListener headSetListener) {
		this.headSetListener = headSetListener;
	}

	/**
	 * 为MEDIA_BUTTON 意图注册接收器(注册开启耳机线控监听, 请务必在设置接口监听之后再调用此方法,否则接口无效)
	 * @param context
	 */
	public void open(Context context) {
		if(headSetListener==null){
			throw new IllegalStateException("please set headSetListener");
		}
		AudioManager audioManager = (AudioManager) context
				.getSystemService(Context.AUDIO_SERVICE);
		ComponentName name = new ComponentName(context.getPackageName(),
				MediaButtonReceiver.class.getName());
		audioManager.registerMediaButtonEventReceiver(name);
		Log.i("ksdinf", "open");
	}
	/**
	 * 关闭耳机线控监听
	 * @param context
	 */
	public void close(Context context) {
		AudioManager audioManager = (AudioManager) context
				.getSystemService(Context.AUDIO_SERVICE);
		ComponentName name = new ComponentName(context.getPackageName(),
				MediaButtonReceiver.class.getName());
		audioManager.unregisterMediaButtonEventReceiver(name);
	}
	/**
	 * 删除耳机单机双击监听接口
	 */
	public void delHeadSetListener() {
		this.headSetListener = null;
	}

	/**
	 * 获取耳机单击双击接口
	 *
	 * @return
	 */
	protected OnHeadSetListener getOnHeadSetListener() {
		return headSetListener;
	}

	/**
	 * 耳机按钮单双击监听
	 */
	public interface OnHeadSetListener {
		/**
		 * 单击触发,主线程。 此接口真正触发是在单击操作1秒后 因为需要判断1秒内是否仍监听到点击,有的话那就是双击了
		 */
		public void onClick();
		/**
		 * 双击触发,此接口在主线程,可以放心使用
		 */
		public void onDoubleClick();
		/**
		 * 三连击
		 */
		public void onThreeClick();
	}
}

该类主要负责媒体按键接收器的注册和自定义媒体按键回调监听器的设置。该类中包含一个OnHeadSetListener接口,该接口中的onClick(),onDoubleClick(),onThreeClick()三个方法分别会在单击事件,双击事件,以及三连击事件发生时被回调。需要指出的是,单击和双击事件会有1秒的延迟,这是因为在这1秒内需要监听是否还有单击发生的原因,当然这1s也不是绝对的,你也可以根据实际的业务需要自定义事件。在下面讲解的这个类中将会解开酷狗线控的原理。

2.耳机媒体按键广播接收器MediaButtonReceiver:

package com.jph.lc;

import java.util.Timer;
import java.util.TimerTask;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;

import com.jph.lc.HeadSetUtil.OnHeadSetListener;

/**
 * MEDIA_BUTTON耳机媒体按键广播接收器
 * @author JPH
 * @Date2015-6-9 下午8:35:40
 */
public class MediaButtonReceiver extends BroadcastReceiver{

	private Timer timer = null;
	private OnHeadSetListener headSetListener = null;
	private static MTask myTimer = null;
	/**单击次数**/
	private static int clickCount;
	public MediaButtonReceiver(){
		timer = new Timer(true);
		this.headSetListener = HeadSetUtil.getInstance().getOnHeadSetListener();
	}
	@Override
	public void onReceive(Context context, Intent intent) {
		Log.i("ksdinf", "onReceive");
		 String intentAction = intent.getAction() ;
	        if(Intent.ACTION_MEDIA_BUTTON.equals(intentAction)){
	        	KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); //获得KeyEvent对象
	        	if(headSetListener != null){
	        		try {
	        			if(keyEvent.getAction() == KeyEvent.ACTION_UP){
	        				if (clickCount==0) {//单击
	        					clickCount++;
	        					myTimer = new MTask();
	        					timer.schedule(myTimer,1000);
							}else if (clickCount==1) {//双击
								clickCount++;
							}else if (clickCount==2) {//三连击
								clickCount=0;
								myTimer.cancel();
								headSetListener.onThreeClick();
							}
	        			}
					} catch (Exception e) {
					}
	        	}
	        }
	        abortBroadcast();//终止广播(不让别的程序收到此广播,免受干扰)
	}
	/**
	 * 定时器,用于延迟1秒,判断是否会发生双击和三连击
	 */
	class MTask extends TimerTask{
			@Override
			public void run() {
				try {
					if (clickCount==1) {
						mhHandler.sendEmptyMessage(1);
					}else if (clickCount==2) {
						mhHandler.sendEmptyMessage(2);
					}
					clickCount=0;
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
	};
	/**
	 * 此handle的目的主要是为了将接口在主线程中触发
	 * ,为了安全起见把接口放到主线程触发
	 */
	Handler mhHandler = new Handler(){
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			if(msg.what==1){//单击
				headSetListener.onClick();
			}else if (msg.what==2) {//双击
				headSetListener.onDoubleClick();
			}else if (msg.what==3) {//三连击
				headSetListener.onThreeClick();
			}
		}
	};

}

该类主要负责接收系统发出的媒体案件的单击事件,并对单击事件做相应的处理以达到单击,双击,三连击的效果。需要指出的是该类在实例化的时候会获取OnHeadSetListener监听器,所以要在调用HeadSetUtil类的open方法用之前设置OnHeadSetListener,否则将不会对媒体按键事件做处理。

该类中有个名为Mtask的内部类,该内部类是一个定时任务,该任务会在指定的时间里分析是否会发生双击和三连击。

另外,该类中还有一个myHandler对象,该对象是为了将回调监听发生在UI线程中,以方便UI的更新。

3.监听器的使用类MainActivity:

package com.jph.lc;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.jph.lc.HeadSetUtil.OnHeadSetListener;
/**
 * 耳机线控实例,蓝牙耳机按钮监听(仿酷狗线控效果)
 * @author JPH
 * @Date2015-6-10 上午9:49:02
 */
public class MainActivity extends Activity {

	TextView txt = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		txt = (TextView) findViewById(R.id.text);
		HeadSetUtil.getInstance().setOnHeadSetListener(headSetListener);
		HeadSetUtil.getInstance().open(this);
	}
	@Override
	protected void onDestroy() {
		super.onDestroy();
		HeadSetUtil.getInstance().close(this);
	}
	OnHeadSetListener headSetListener = new OnHeadSetListener() {
		@Override
		public void onDoubleClick() {
			txt.setText("双击");
			Log.i("ksdinf", "双击");
		}
		@Override
		public void onClick() {
			txt.setText("单击");
			Log.i("ksdinf", "单击");
		}
		@Override
		public void onThreeClick() {
			txt.setText("三连击");
			Log.i("ksdinf", "三连击");
		}
	};
}

该类中举要介绍了媒体按键监听的使用。

源码下载:http://download.csdn.net/detail/fengyuzhengfan/8797357

GitHub:https://github.com/crazycodeboy/HeadSetControl

时间: 2024-08-02 06:44:17

Android耳机线控详解,蓝牙耳机按钮监听(仿酷狗线控效果)的相关文章

Android的事件处理机制详解(二)-----基于监听的事件处理机制

基于监听的事件处理机制 前言: 我们开发的app更多的时候是需要与用户的交互----即对用户的操作进行响应 这就涉及到了android的事件处理机制; android给我们提供了两套功能强大的处理机制: ①基于监听的事件处理机制 ②基于回调的事件处理机制 在这一节中,我们会先介绍一下基于监听的事件处理机制 好了,废话不多说! 我们要先了解一下监听处理机制的模型 监听的处理模型: 处理模型图: 文字表述: 事件监听机制中由事件源,事件,事件监听器三类对象组成 处理流程: step 1:为某个事件源

ANDROID L——Material Design详解(UI控件)

转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lollipop(5.0). 前几天发现Android5.0正式版的sdk已经可以下载了,而且首次搭载Android L系统的Nexus 6和 Nexus 9也即将上市. 所以是时候开始学习Android L了! 关于Android L如何配置模拟器和创建项目,如果大家有兴趣的话可以看看我之前的一篇文章: A

给 Android 开发者的 RxJava 详解

作者:扔物线 前言 我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个月,我也发现国内越来越多的人开始提及 RxJava .有人说『RxJava 真是太好用了』,有人说『RxJava 真是太难用了』,另外更多的人表示:我真的百度了也谷歌了,但我还是想问: RxJava 到底是什么? 鉴于 RxJava 目前这种既火爆又神秘的现状,而我又在一年的使用

转:给 Android 开发者的 RxJava 详解

转自:  http://gank.io/post/560e15be2dca930e00da1083 评注:多图解析,但是我还是未看懂. 前言 我从去年开始使用 RxJava ,到现在一年多了.今年加入了 Flipboard 后,看到 Flipboard 的 Android 项目也在使用 RxJava ,并且使用的场景越来越多 .而最近这几个月,我也发现国内越来越多的人开始提及 RxJava .有人说『RxJava 真是太好用了』,有人说『RxJava 真是太难用了』,另外更多的人表示:我真的百度

Android开发之WebView详解

概述: 一个显示网页的视图.这个类是你可以滚动自己的Web浏览器或在你的Activity中简单地显示一些在线内容的基础.它使用了WebKit渲染引擎来显示网页,包括向前和向后导航的方法(通过历史记录),放大和缩小,执行文本搜索等. 需要注意的是:为了让你的应用能够使用WebView访问互联网和加载网页,你必须添加Internet的权限在Android Manifest文件中: <uses-permission android:name="android.permission.INTERNE

Android事件分发机制详解(1)----探究View的事件分发

探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.d("TAG", "onClick execute"); } }); 如果在需要一个触摸事件 [java] view plaincopy button.setO

Android事件分发机制详解(2)----分析ViewGruop的事件分发

首先,我们需要 知道什么是ViewGroup,它和普通的View有什么区别? ViewGroup就是一组View的集合,它包含很多子View和ViewGroup,是Android 所有布局的父类或间接父类. 但ViewGroup也是一个View,只不过比起View,它可以包含子View和定义布局参数的功能. 现在,通过一个Demo演示Android中ViewGroup的事件分发机制. 首先我们来自定义一个布局,命名为MyLayout,继承自LinearLayout,如下 所示: public c

Android目录结构(详解)

Android目录结构(详解) 下面是HelloAndroid项目在eclipse中的目录层次结构: 由上图可以看出项目的根目录下共有九个文件(夹),下面就这九个文件(夹)进行详解: 1.1src文件夹和assets文件夹: 每个Android程序都包含资源目录(src)和资产目录(assets),资源和资产听起来感觉没有多大差别,但在存储外部内容时用资源(src)比较多,其中它们的区别在于存放在资源(src)下的内容可以通过应用程序的R类进行访问,而存放在资产(assets)下的内容会保持原始

ANDROID L——Material Design详解(动画篇)

转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! Android L: Google已经确认Android L就是Android Lollipop(5.0). 前几天发现Android5.0正式版的sdk已经可以下载了,而且首次搭载Android L系统的Nexus 6和 Nexus 9也即将上市. 所以是时候开始学习Android L了! 关于Android L如何配置模拟器和创建项目,如果大家有兴趣的话可以看看我之前的一篇文章: A