最近在android5.1上面做一个功能,类似苹果的一键静音功能。山寨大国,一直模仿,但从未超越,别人做什么,砸门跟着做。下面就来看看android究竟怎么实现这个功能呢?
刚开始我也不知到怎么做,怎么办呢?只好想方设法的找线索,首先先把按键驱动实现了,上层能读到按键事件后,那就好办了,好,那我们就先实现按键驱动。
高通源码里面已经实现了按键的驱动,只需要添加我们所需的节点就好了,下面以具体代码来说明
第一步:按键驱动
1.驱动代码
kernel\drivers\switch\switch_gpio.c
kernel\drivers\switch\switch_class.c
2.设备节点
kernel\arch\arm64\boot\dts\chinachip\msm8916-mtp.dtsi
gpio_swtich{
compatible = "qcom,gpio-switch";
pinctrl-names = "default","sleep";
pinctrl-0 = <&ringerkey_default>;
pinctrl-1 = <&ringerkey_sleep>;
interrupt-parent = <&msm_gpio>;
interrupts = <36 0x2003>;
switch-name = "ringerkey";
switch-gpio = <&msm_gpio 36 0x2003>;
};
kernel\arch\arm64\boot\dts\chinachip\msm8916-pinctrl.dtsi
ringerkey_int_pin {
qcom,pins = <&gp 36>;
qcom,pin-func = <0>;
qcom,num-grp-pins = <1>;
label = "ringerkey-irq";
ringerkey_default: ringerkey_default {
drive-strength = <6>;
bias-pull-up;
};
ringerkey_sleep: ringerkey_sleep {
drive-strength = <2>;
bias-pull-down;
};
};
到此,kernel部分就完成了。
第二部分:应用
在不知道怎么加的情况下,那就搜索中文字,通过logcat分析,这部分的代码在framework里面,frameworks\base\packages\SystemUI\src\com\android\systemui\volume\。
android铃声模式分为三种, 在AudioManager.java里有定义,
public static final int RINGER_MODE_SILENT = 0; //静音模式
public static final int RINGER_MODE_VIBRATE = 1; // 振动模式
public static final int RINGER_MODE_NORMAL = 2; // 一般模式
android控制静音功能分为两部分,一部分是内部控制,一部分外部控制,但是这两部分控制逻辑是不一样的,当内部发送一个RINGER_MODE_SILENT,会调用ZenModeHelper.onSetRingerModeInternal()方法,系统设置的是全部静音,当外部发送一个RINGER_MODE_SILENT,会调用ZenModeHelper.onSetRingerModeExternal()方法,系统设置的是优先模式,那么我们要设置响铃模式,可以调用AudioManager.setRingerMode()方法设置。
在DockObserver.java里面添加自己的服务,
import android.media.AudioManager;
private static final String Ringer_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/ringerkey";
private static final String Ringer_STATE_MATCH = "/sys/class/switch/ringerkey/state";
public static final int MSG_RINGER_MODE_SILENT = 0;
public static final int MSG_RINGER_MODE_NORMAL = 2;
private final Handler mHandler = new Handler(true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
/* case MSG_DOCK_STATE_CHANGED:
handleDockStateChange();
mWakeLock.release();
break;*/
case MSG_RINGER_MODE_SILENT:
Slog.i(TAG, "RINGER_MODE_SILENT: ");
RingerModeSwitch(AudioManager.RINGER_MODE_SILENT);
break;
case MSG_RINGER_MODE_NORMAL:
Slog.i(TAG, "RINGER_MODE_NORMAL ");
RingerModeSwitch(AudioManager.RINGER_MODE_NORMAL);
break;
case MSG_CABLE_OFF_CHANGED:
Slog.i(TAG, "MSG_CABLE_OFF_CHANGED: ");
NoticCableSwitch(true);
break;
case MSG_CHARGER_OFF_CHANGED:
Slog.i(TAG, "MSG_CHARGER_OFF_CHANGED: ");
NoticChargerSwitch(true);
break;
}
}
};
private final UEventObserver mObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "Dock UEVENT: " + event.toString());
}
Slog.v(TAG, "Dock UEVENT: " + event.toString());
String misc_name = event.get("DEVNAME");
if(misc_name!=null&&misc_name.equals("mpu6880_collision")){
Slog.v(TAG, "Dock UEVENT: mpu6880_collision");
mHandler.sendEmptyMessage(MSG_CABLE_OFF_CHANGED);
return;
}
String switch_name = event.get("SWITCH_NAME");
String switch_state = event.get("SWITCH_STATE");
Slog.e(TAG, "switch_name " + switch_name);
Slog.e(TAG, "switch_state " + switch_state);
if(switch_name.equals("ringerkey")){
int newState = Integer.parseInt(switch_state);
if(newState==1){
mHandler.sendEmptyMessage(MSG_RINGER_MODE_SILENT);
}else{
mHandler.sendEmptyMessage(MSG_RINGER_MODE_NORMAL);
}
return;
}else if(switch_name.equals("charger_cable")){
int mState = Integer.parseInt(switch_state);
if(mState==0)
mHandler.sendEmptyMessage(MSG_CHARGER_OFF_CHANGED);
Slog.e(TAG, "return ");
return;
}
try {
synchronized (mLock) {
setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
}
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
};
private void RingerModeSwitch(int ringermode){
Slog.i(TAG, "NoticeRingerSwitch"+ringermode);
mAudioManager.setRingerMode(ringermode);
}
下面我们就来分析下,两种方法:
public int onSetRingerModeInternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeExternal) {
final boolean isChange = ringerModeOld != ringerModeNew;
int ringerModeExternalOut = ringerModeNew;
int newZen = -1;
switch (ringerModeNew) {
case AudioManager.RINGER_MODE_SILENT:
if (isChange) {
if (mZenMode != Global.ZEN_MODE_NO_INTERRUPTIONS) {
newZen = Global.ZEN_MODE_NO_INTERRUPTIONS;
}
}
break;
case AudioManager.RINGER_MODE_VIBRATE:
case AudioManager.RINGER_MODE_NORMAL:
if (isChange && ringerModeOld == AudioManager.RINGER_MODE_SILENT
&& mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
}
break;
}
if (newZen != -1) {
setZenMode(newZen, "ringerModeInternal", false /*setRingerMode*/);
}
if (isChange || newZen != -1 || ringerModeExternal != ringerModeExternalOut) {
ZenLog.traceSetRingerModeInternal(ringerModeOld, ringerModeNew, caller,
ringerModeExternal, ringerModeExternalOut);
}
return ringerModeExternalOut;
}
public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeInternal) {
int ringerModeInternalOut = ringerModeNew;
final boolean isChange = ringerModeOld != ringerModeNew;
final boolean isVibrate = ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
int newZen = -1;
switch (ringerModeNew) {
case AudioManager.RINGER_MODE_SILENT:
if (isChange) {
if (mZenMode == Global.ZEN_MODE_OFF &&
mContext.getResources().getBoolean(com.android.internal.R.bool.config_setZenModeWhenSilentModeOn))
{
newZen = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE
: AudioManager.RINGER_MODE_NORMAL;
} else {
ringerModeInternalOut = ringerModeInternal;
}
break;
case AudioManager.RINGER_MODE_VIBRATE:
case AudioManager.RINGER_MODE_NORMAL:
if (mZenMode != Global.ZEN_MODE_OFF) {
newZen = Global.ZEN_MODE_OFF;
}
break;
}
if (newZen != -1) {
setZenMode(newZen, "ringerModeExternal", false /*setRingerMode*/);
}
ZenLog.traceSetRingerModeExternal(ringerModeOld, ringerModeNew, caller, ringerModeInternal,
ringerModeInternalOut);
return ringerModeInternalOut;
}
比较这两个函数,当isChange为真的时候,newZen 的值是不一样的,这个地方就是就是设下去的方法。要想设置成苹果静音键的功能,在来电过程中也能静音控制,那就需要修改onSetRingerModeExternal()里的方法,
case AudioManager.RINGER_MODE_SILENT:
if (isChange) {
if (mZenMode == Global.ZEN_MODE_OFF &&
mContext.getResources().getBoolean(com.android.internal.R.bool.config_setZenModeWhenSilentModeOn))
{
newZen = Global.ZEN_MODE_NO_INTERRUPTIONS;
}
ringerModeInternalOut = isVibrate ? AudioManager.RINGER_MODE_VIBRATE
: AudioManager.RINGER_MODE_SILENT;
} else {
ringerModeInternalOut = ringerModeInternal;
}
break;
这样修改后,就实现了苹果一样的功能了。