前一阵子完成了用有线耳机控制Android手机App的音频播放,具体实现了用耳机线的按键完成播放、暂停、上一曲、下一曲的功能。在网上查阅了一些资料,但不是特别尽如人意,记得有一篇写的很不错的这方面的文章里有两处很细微的错误,我不经分辨拿来用的时候没有达到我想要的线控效果。后来从头做起,每写完一块代码,就进行打Log测试,查看输出日志是否实现了我期待的结果。就这样一步一步,最后完美解决。我想这种方式可能是我以后处理问题的一种可以遵循的步骤,故此在这里简略记录我当时的操作过程,也许未来于己于人有细微的帮助呢~
在AndroidManifest.xml 中添加:
".audio.PlayerService.HeadsetButtonReceiver">
"1000">
"android.intent.action.MEDIA_BUTTON" />
public class HeadsetButtonReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive");
if(Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())){
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:");
KeyEvent keyEvent = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if(keyEvent.KEYCODE_MEDIA_PLAY == keyEvent.getKeyCode()){
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:"+"if");
}
}
}
}
并不能拦截按键事件,启动App音频后点击耳机”播放“键,直接启动系统音乐播放器,而不是暂停App音频。
继续网络搜索,发现需要首先注册/取消注册receiver,故在HeadsetButtonReceiver类中添加如下两个方法:
public void registerHeadsetReceiver(Context context) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);// 另说context.AUDIO_SERVICE
ComponentName name = new ComponentName(context.getPackageName(), HeadsetButtonReceiver.class.getName());// 另说MediaButtonReceiver.class.getName()
audioManager.registerMediaButtonEventReceiver(name);
}
public void unregisterHeadsetReceiver(Context context){
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
ComponentName name = new ComponentName(context.getPackageName(), HeadsetButtonReceiver.class.getName());
audioManager.unregisterMediaButtonEventReceiver(name);
}
之后在App定义的音频播放类的onCreate方法中调用registerHeadsetReceiver,在该类的onDestroy方法中调用un registerHeadsetReceiver。
打开app,按压耳机的”播放“按钮,可以监听到:
03-01 15:08:16.432 11059-11059/com.xxxxx.xxx E/headSet: HeadsetButtonReceiver:onReceive
03-01 15:08:16.432 11059-11059/com.xxxxx.xxx E/headSet: HeadsetButtonReceiver:onReceive:if:
因为尝试2的结果只打印出了两个Log,程序不满足第三个if的判断条件。故更新该类(广播接收器)中的onReceive方法如下:
public void onReceive(Context context, Intent intent) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive");
if(Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())){
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:");
KeyEvent keyEvent = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if(keyEvent.KEYCODE_MEDIA_PLAY == keyEvent.getKeyCode()){
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:"+"if");
}else {
Log.e("headSet","keyEvent="+keyEvent);
}
}
}
打开app,按压耳机的”播放“按钮,可以监听到:
03-01 15:08:16.432 11059-11059/com.xxxxx.xxx E/headSet: HeadsetButtonReceiver:onReceive
03-01 15:08:16.432 11059-11059/com.xxxxx.xxx E/headSet: HeadsetButtonReceiver:onReceive:if:
03-01 15:08:16.435 11059-11059/com.xxxxx.xxx E/headSet: keyEvent=KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_HEADSETHOOK, scanCode=164, metaState=0, flags=0x8, repeatCount=0, eventTime=19326753, downTime=19326534, deviceId=4, source=0x101 }
03-01 15:08:16.445 11059-11059/com.xxxxx.xxx E/headSet: HeadsetButtonReceiver:onReceive
03-01 15:08:16.445 11059-11059/com.xxxxx.xxx E/headSet: HeadsetButtonReceiver:onReceive:if:
03-01 15:08:16.445 11059-11059/com.xxxxx.xxx E/headSet: keyEvent=KeyEvent { action=ACTION_UP, keyCode=KEYCODE_HEADSETHOOK, scanCode=164, metaState=0, flags=0x8, repeatCount=0, eventTime=19326753, downTime=19326534, deviceId=4, source=0x101 }
从输出的Log日志可以看到,按压耳机的播放/暂停键时,广播接收器接收到了两个动作,分别是action=ACTION_DOWN和action=ACTION_UP,keyCode均为keyCode=KEYCODE_HEADSETHOOK。故此修改尝试3中的代码,并充实onReceive方法。要实现的功能是播放/暂停,下一曲,上一曲。针对市面上有线耳机按键大多数包括两个+-键,一个功能键,需要处理两种情况:1)+-为音量键,单击功能键实现播放/暂停,双击实现下一曲,三连击实现上一曲。2)+-为下一曲/上一曲键,单击功能键实现播放/暂停。如下:
@Override
public void onReceive(Context context, Intent intent) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:");
if(Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:");
KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK && keyEvent.getAction() == KeyEvent.ACTION_UP) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:"+"if"+" HEADSETHOOK");
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_NEXT) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:"+"if"+" KEYCODE_HEADSETHOOK");
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {
Log.e("headSet","HeadsetButtonReceiver:"+"onReceive:"+"if:"+"if"+ " KEYCODE_MEDIA_PREVIOUS");
}
}
}
打开app,按压耳机的”播放“按钮,可以监听到Log: “HeadsetButtonReceiver:onReceive:if:if KEYCODE_HEADSETHOOK”
到目前为止,可以收到耳机按键广播,并可进入指定位置。接下来开始实现具体功能,即:三连击中键,实现上一曲;双击中键,实现下一曲;单击中键,实现播放/暂停。要求在1000ms内监听按中键的次数,并将监听按键次数的动作、响应按键次数的事件放到子线程中执行。故丰富onReceive()方法,新建定时器类HeadsetTimerTask,执行定时结束后的响应;新建子线程,由子线程执行具体的响应。
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK && keyEvent.getAction() == KeyEvent.ACTION_UP) {
clickCount = clickCount + 1;
if(clickCount == 1){
HeadsetTimerTask headsetTimerTask = new HeadsetTimerTask();
timer.schedule(headsetTimerTask,1000);
}
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_NEXT) {
handler.sendEmptyMessage(2);
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {
handler.sendEmptyMessage(3);
}
}
}
class HeadsetTimerTask extends TimerTask {
@Override
public void run() {
try{
if(clickCount==1){
handler.sendEmptyMessage(1);
}else if(clickCount==2){
handler.sendEmptyMessage(2);
}else if(clickCount>=3){
handler.sendEmptyMessage(3);
}
clickCount=0;
}catch (Exception e){
e.printStackTrace();
}
}
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
if (msg.what == 1) {
headsetListener.playOrPause();
}else if(msg.what == 2){
headsetListener.playNext();
}else if(msg.what == 3){
headsetListener.playPrevious();
}
}catch (Exception e){
e.printStackTrace();
}
}
};
interface onHeadsetListener{
void playOrPause();
void playNext();
void playPrevious();
}
public void setOnHeadsetListener(onHeadsetListener newHeadsetListener){
headsetListener = newHeadsetListener;
}
耳机线控一共分为三部分:
在AndroidManifest.xml中定义广播接收器,以及需要接收的广播:
".audio.PlayerService.HeadsetButtonReceiver">
"1000">
"android.intent.action.MEDIA_BUTTON" />
实现该广播接收器:
package com.wdbible.app.wedevotebible.audio.PlayerService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Jerre on 2018/3/1.
*/
public class HeadsetButtonReceiver extends BroadcastReceiver {
private Context context;
private Timer timer = new Timer();
private static int clickCount;
private static onHeadsetListener headsetListener;
public HeadsetButtonReceiver(){
super();
}
public HeadsetButtonReceiver(Context ctx){
super();
context = ctx;
headsetListener = null;
registerHeadsetReceiver();
}
@Override
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK && keyEvent.getAction() == KeyEvent.ACTION_UP) {
clickCount = clickCount + 1;
if(clickCount == 1){
HeadsetTimerTask headsetTimerTask = new HeadsetTimerTask();
timer.schedule(headsetTimerTask,1000);
}
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_NEXT) {
handler.sendEmptyMessage(2);
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {
handler.sendEmptyMessage(3);
}
}
}
class HeadsetTimerTask extends TimerTask {
@Override
public void run() {
try{
if(clickCount==1){
handler.sendEmptyMessage(1);
}else if(clickCount==2){
handler.sendEmptyMessage(2);
}else if(clickCount>=3){
handler.sendEmptyMessage(3);
}
clickCount=0;
}catch (Exception e){
e.printStackTrace();
}
}
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
try {
if (msg.what == 1) {
headsetListener.playOrPause();
}else if(msg.what == 2){
headsetListener.playNext();
}else if(msg.what == 3){
headsetListener.playPrevious();
}
}catch (Exception e){
e.printStackTrace();
}
}
};
interface onHeadsetListener{
void playOrPause();
void playNext();
void playPrevious();
}
public void setOnHeadsetListener(onHeadsetListener newHeadsetListener){
headsetListener = newHeadsetListener;
}
public void registerHeadsetReceiver() {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
ComponentName name = new ComponentName(context.getPackageName(), HeadsetButtonReceiver.class.getName());
audioManager.registerMediaButtonEventReceiver(name);
}
public void unregisterHeadsetReceiver(){
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
ComponentName name = new ComponentName(context.getPackageName(), HeadsetButtonReceiver.class.getName());
audioManager.unregisterMediaButtonEventReceiver(name);
}
}
在音频管理类中的构造方法中新建该广播接收器的实例对象(新建实例对象的过程中已完成该接收器的注册),在该类的onDestory()或者close()方法中注销该广播接收器。
以上。