android 实现耳机的线控

最近对安卓的耳机线控进行了粗略的研究。目的是监听线控耳机上的按钮。

说下,首先我看到在onKeyDown方法里面,可以监听到耳机按钮。但是,这只能是在对应的activity激活在最前面的时候,锁屏同样无法监听到耳机上面的按钮。

所以这种办法具有较大的局限性,不建议采用,往往我们使用耳机的时候,手机放在兜兜里,或者在驾驶。

接下去进入正题。我要讲的是利用广播来实现对耳机线控的监听,在线控按钮摁下去的时候,android就会有自己的广播,我们只要定义一个广播接收者来接收到这个广播就ok了。这个广播的意图是 android.intent.action.MEDIA_BUTTON 。

下面说一下重点。注册普通的广播接收器我们都知道,有两个常见的办法,一种是在代码中动态注册,还有一种的在项目的Manifest里面注册。但是,这个广播特么简直就是个奇葩,我花了一个下午的时间才搞明白。这个广播要注册两遍,Manifest里一遍(常规办法),代码中一遍(借助多媒体服务注册),下面我注册广播的代码贴出来

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

解除广播的代码,因为两种注册方式缺一不可,所以,解除了一种,它的监听作用也就失效了


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

我封装了一个线控助手类,为单例,为什么是单例,后面我再讲,可以借助这个类的一些方法来开启监听和解除监听,设置接口

HeadSetReceiver是我的广播接受者类,里面延迟1秒处理单双击问题。

package com.example.headset.helper;

import android.content.ComponentName;
import android.content.Context;
import android.media.AudioManager;

/**
 * 耳机线控管理助手类
 * 	单例
 * @author 
 *
 */
public class HeadSetHelper {

	private static HeadSetHelper headSetHelper;
	
	private OnHeadSetListener headSetListener = null;
	
	public static HeadSetHelper getInstance(){
		if(headSetHelper == null){
			headSetHelper = new HeadSetHelper();
		}
		return headSetHelper;
	}
	
	/**
	 * 设置耳机单击双击监听接口
	 * 必须在open前设置此接口,否则设置无效
	 * @param headSetListener
	 */
	public void setOnHeadSetListener(OnHeadSetListener headSetListener){
		this.headSetListener = headSetListener;
	}
	
	/**
	 * 开启耳机线控监听,
	 * 请务必在设置接口监听之后再调用此方法,否则接口无效
	 * @param context
	 */
	public void open(Context context){
		AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
		ComponentName name = new ComponentName(context.getPackageName(),
				HeadSetReceiver.class.getName());
		audioManager.registerMediaButtonEventReceiver(name);
	}
	/**
	 * 关闭耳机线控监听
	 * @param context
	 */
	public void close(Context context){
		AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
		ComponentName name = new ComponentName(context.getPackageName(),
				HeadSetReceiver.class.getName());
		audioManager.unregisterMediaButtonEventReceiver(name);
	}
	/**
	 * 删除耳机单机双击监听接口
	 */
	public void delHeadSetListener()
	{
		this.headSetListener=null;
	}
	
	/**
	 * 获取耳机单击双击接口
	 * @return
	 */
	protected OnHeadSetListener getOnHeadSetListener() {
		return headSetListener;
	}
	
	/**
	 * 耳机按钮单双击监听
	 * @author 
	 *
	 */
	public interface OnHeadSetListener{
		/**
		 * 单击触发,主线程。
		 * 此接口真正触发是在单击操作1秒后
		 * 因为需要判断1秒内是否仍监听到点击,有的话那就是双击了。<p>
		 * 如果您有更好的解决办法,请联系我:
		 * qq495389040
		 */
		public void onClick();
		/**
		 * 双击触发,此接口在主线程,可以放心使用
		 */
		public void onDoubleClick();
	}
}

package com.example.headset.helper;

import java.util.Timer;
import java.util.TimerTask;
import com.example.headset.helper.HeadSetHelper.OnHeadSetListener;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;

public class HeadSetReceiver extends BroadcastReceiver{

	Timer timer = null;
	OnHeadSetListener headSetListener = null;
	private static boolean isTimerStart = false;
	private static MyTimer myTimer = null;
	//重写构造方法,将接口绑定。因为此类的初始化的特殊性。
	public HeadSetReceiver(){
		timer = new Timer(true);
		this.headSetListener = HeadSetHelper.getInstance().getOnHeadSetListener();
	}
	
	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		 String intentAction = intent.getAction() ;
	        if(Intent.ACTION_MEDIA_BUTTON.equals(intentAction)){
	        	//获得KeyEvent对象  
	        	KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 
	        	if(headSetListener != null){
	        		try {
	        			if(keyEvent.getAction() == KeyEvent.ACTION_UP){
	        				if(isTimerStart){
	        					myTimer.cancel();
	        					isTimerStart = false;
	        					headSetListener.onDoubleClick();
	        				}else{
	        					myTimer = new MyTimer();
	        					timer.schedule(myTimer,1000);
	        					isTimerStart = true;
	        				}
	        			}
					} catch (Exception e) {
						// TODO: handle exception
					}
	        	}	
	        }
	        //终止广播(不让别的程序收到此广播,免受干扰)  
	        abortBroadcast();
	}
	/*
	 * 定时器,用于延迟1秒,内若无操作则为单击
	 */
	class MyTimer extends TimerTask{
			
			@Override
			public void run() {
				try {
					myHandle.sendEmptyMessage(0);
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
	};
	/*
	 * 此handle的目的主要是为了将接口在主线程中触发
	 * ,为了安全起见把接口放到主线程触发
	 */
	Handler myHandle = new Handler(){

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			headSetListener.onClick();
			isTimerStart = false;
		} http://
		
	};
		
}

为什么要用单例模式,因为我的助手类中的接口真正的触发是在HeadSetReceiver里面,而这个广播的独特注册方式,让我无法获取被注册的这个接受者的实体,也就是说我无法将接口传递给这个接受者。我在HeadSetReceiver的默认构造函数里面去获取这个单例,再把这个单例助手 绑定的那个接口拿过来,在HeadSetReceiver里面去触发。这样才能保证这两个地方的接口就是同一个接口。


具体实例和封装请点下面链接,有不懂的或者建议欢迎提问交流 


最后补充一点,痛恨安卓严重的碎片化。或许在很多场合,耳机的长按功能用起来会更方便,比较双击容易出现误操作。

在安卓4.1版本中,线控长按的广播被谷歌保留了,长按后开启的是谷歌自己的语音搜索。不知道后面的版本是怎样的。


点我下载案例项目

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值