1. 程式人生 > >Android耳機線控詳解,藍芽耳機按鈕監聽(仿酷狗線控效果)

Android耳機線控詳解,藍芽耳機按鈕監聽(仿酷狗線控效果)

當耳機的媒體按鍵被單擊後,Android系統會發出一個廣播,該廣播的攜帶者一個Action名為MEDIA_BUTTONIntent。監聽該廣播便可以獲取手機的耳機媒體按鍵的單擊事件。

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註釋中可知:

1AudioManager物件註冊一個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", "三連擊");
		}
	};
}

該類中舉要介紹了媒體按鍵監聽的使用。