前言:网上关于添加静音键的解决方案,用起来多多少少有些问题,这里提供一种添加静音键的解决方案。
android 添加mute键(普通模式)
android音量控制分为两种,一种master模式,这里不做讨论,只探讨普通模式。
首先说说通过按加减键使得音量mute的原理
按加减音量键直至mute的流程
每一次按加减音量键时,都可以分为两个步骤:
1. ADJUST_LOWER or ADJUST_RAISER(加减音量)
2. 第二次则是ADJUST_SAME(在UI上显示当前音量)
具体调用如下:
handleKeyDown -> adjustSuggestedStreamVolume -> adjustStreamVolume -> ADJUST_LOWER or ADJUST_RAISER
handleKeyUp -> adjustSuggestedStreamVolume -> adjustStreamVolume -> ADJUST_SAME
这个流程,起初我还没太在意,直到当我想通过模拟多次按减音量键来实现mute时,遇到了问题,发现:
音量只能减到0,不能变成mute。
这是为什么呢?
查看如下代码才明白,也通过常规的操作验证了这一点,一次性按减音量键,是无法变为mute的。
mute状态的判定流程如下:
adjustStreamVolume -> checkForRingerModeChange
-
/**
-
* Checks if the adjustment should change ringer mode instead of just
-
* adjusting volume. If so, this will set the proper ringer mode and volume
-
* indices on the stream states.
-
*/
-
private boolean checkForRingerModeChange(int oldIndex, int direction, int step) {
-
boolean adjustVolumeIndex = true;
-
int ringerMode = getRingerMode();
-
-
-
//Log.i(TAG, "debug =====================> ring mode begin " + ringerMode);
-
-
-
switch (ringerMode) {
-
case RINGER_MODE_NORMAL:
-
if (direction == AudioManager.ADJUST_LOWER) {
-
if (mHasVibrator) {
-
// "step" is the delta in internal index units corresponding to a
-
// change of 1 in UI index units.
-
// Because of rounding when rescaling from one stream index range to its alias
-
// index range, we cannot simply test oldIndex == step:
-
// (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
-
if (step <= oldIndex && oldIndex < 2 * step) {
-
//Log.i(TAG, "debug =====================> ring mode a ");
-
ringerMode = RINGER_MODE_VIBRATE;
-
}
-
} else {
-
// (oldIndex < step) is equivalent to (old UI index == 0)
-
if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
-
//Log.i(TAG, "debug =====================> ring mode b ");
-
ringerMode = RINGER_MODE_SILENT;
-
}
-
}
-
}
-
break;
-
-
-
......
-
-
//Log.i(TAG, "debug =====================> ring mode end " + ringerMode + " vol index " + adjustVolumeIndex);
-
setRingerMode(ringerMode);
-
-
-
mPrevVolDirection = direction;
-
-
-
return adjustVolumeIndex;
-
}
我们需要走到ring mode b中,也就是说,mPrevVolDirection != AudioManager.ADJUST_LOWER必须满足,
如果一直长按,mPrevVolDirection必定等于AudioManager.ADJUST_LOWER,所以必须等到下一次按
ADJUST_LOWER,
才可以真正切到mute状态,这是为什么呢,因为之前提到,每一次按键结束,VolDirection都被重置为
ADJUST_SAME。
通过看到这个流程,我们知道,不能这样做来实现我们的静音功能,于是看看直接设置绝对音量值的流程,看能否实现mute键。
设置绝对音量值的流程
直接设置音量,参考代码如下:
-
int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
-
setStreamVolume(AudioManager.STREAM_MUSIC, 0, flags);
只要音量值是0,就会变为mute状态,这个流程要简洁很多,可以自行查看:
setStreamVolume -> onSetStreamVolume
添加mute键解决方案
如下这种方式相对而言比较简单,至少不会有音量调节的bug。
-
+ private void workAroundGlobalMute(int stream) {
-
+ int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
-
+ setStreamVolume(AudioManager.STREAM_SYSTEM, 0, FLAG_VIBRATE);
-
+ setStreamVolume(AudioManager.STREAM_NOTIFICATION, 0, FLAG_VIBRATE);
-
+ setStreamVolume(AudioManager.STREAM_MUSIC, 0, flags);
-
+ }
-
+
-
+ private void workAroundGlobalUnmute(int stream) {
-
+ int showUIFlags = FLAG_SHOW_UI | FLAG_VIBRATE;
-
+ adjustSuggestedStreamVolume(ADJUST_RAISE, stream, showUIFlags);
-
+ }
-
+
-
-
-
case KeyEvent.KEYCODE_VOLUME_MUTE:
-
if (event.getRepeatCount() == 0) {
-
if (mUseMasterVolume) {
-
setMasterMute(!isMasterMute());
-
} else {
-
// TODO: Actually handle MUTE.
-
+ if (!mute) {
-
+ workAroundGlobalMute(stream);
-
+ } else {
-
+ workAroundGlobalUnmute(stream);
-
+ }
-
}
-
}
-
break;
方案的缺点
这种方案的一个明显缺点在于,音量mute之后,再按mute想un-mute时,无法记住mute之前的音量值。
参考
相关代码文件:
framework/base/media/java/android/media/AudioManager.java
framework/base/media/java/android/media/AudioService.java
控制master模式的开关:
frameworks/base/core/res/res下的values/config.xml中找到如下一行:
-
<!-- Flag indicating that the media framework should allow changing
-
master volume stream and nothing else . -->
-
<bool name="config_useMasterVolume">false</bool>
可以mute的STREAM类型 -- 谢谢该文章的介绍 http://blog.csdn.net/njuitjf/article/details/7028357
函数loadVolumeLevels中对此进行了设定:
-
loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
-
((1 << AudioManager.STREAM_MUSIC) |
-
(1 << AudioManager.STREAM_RING) |
-
(1 << AudioManager.STREAM_NOTIFICATION) |
-
(1 << AudioManager.STREAM_SYSTEM)));
调试的方式:
android 修改AudioService以及AudioManager后只需要更新framework.jar,如果修改了res资源文件,则需要更新framework-res.apk
备注
网上的一种方案,也是一种尝试,但是会有bug:
http://blog.csdn.net/huanggenda568/article/details/7477891
原因在于,setStreamMute并不是全局的,而是绑定于特定进程的功能,每一个进程有一个对应的
death handler用来处于静音,当进程死掉,death handler会回收,如果进程不死,mute状态无法被
其他进程所改变,所以会导致在一个apk中静音,在另外一个apk中是无法取消静音的,因为audio service
有一个计数,只有当所有进程的mute被unmute或者死掉,才会真正取消静音。
阅读(4701) | 评论(0) | 转发(0) |