Chinaunix首页 | 论坛 | 博客
  • 博客访问: 164253
  • 博文数量: 14
  • 博客积分: 255
  • 博客等级: 二等列兵
  • 技术积分: 621
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-27 13:12
个人简介

热爱推理

文章分类
文章存档

2015年(2)

2014年(1)

2013年(5)

2011年(1)

2010年(5)

我的朋友

分类: Android平台

2015-12-01 23:02:56


前言:网上关于添加静音键的解决方案,用起来多多少少有些问题,这里提供一种添加静音键的解决方案。


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

  1. /**
  2.      * Checks if the adjustment should change ringer mode instead of just
  3.      * adjusting volume. If so, this will set the proper ringer mode and volume
  4.      * indices on the stream states.
  5.      */
  6.     private boolean checkForRingerModeChange(int oldIndex, int direction, int step) {
  7.         boolean adjustVolumeIndex = true;
  8.         int ringerMode = getRingerMode();


  9.         //Log.i(TAG, "debug =====================> ring mode begin " + ringerMode);


  10.         switch (ringerMode) {
  11.         case RINGER_MODE_NORMAL:
  12.             if (direction == AudioManager.ADJUST_LOWER) {
  13.                 if (mHasVibrator) {
  14.                     // "step" is the delta in internal index units corresponding to a
  15.                     // change of 1 in UI index units.
  16.                     // Because of rounding when rescaling from one stream index range to its alias
  17.                     // index range, we cannot simply test oldIndex == step:
  18.                     // (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
  19.                     if (step <= oldIndex && oldIndex < 2 * step) {
  20.                         //Log.i(TAG, "debug =====================> ring mode a ");
  21.                         ringerMode = RINGER_MODE_VIBRATE;
  22.                     }
  23.                 } else {
  24.                     // (oldIndex < step) is equivalent to (old UI index == 0)
  25.                     if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
  26.                         //Log.i(TAG, "debug =====================> ring mode b ");
  27.                         ringerMode = RINGER_MODE_SILENT;
  28.                     }
  29.                 }
  30.             }
  31.             break;


  32.         ......
  33.         
  34.         //Log.i(TAG, "debug =====================> ring mode end " + ringerMode + " vol index " + adjustVolumeIndex);
  35.         setRingerMode(ringerMode);


  36.         mPrevVolDirection = direction;


  37.         return adjustVolumeIndex;
  38.     }

我们需要走到ring mode b中,也就是说,mPrevVolDirection != AudioManager.ADJUST_LOWER必须满足,
如果一直长按,mPrevVolDirection必定等于AudioManager.ADJUST_LOWER,所以必须等到下一次按ADJUST_LOWER
才可以真正切到mute状态,这是为什么呢,因为之前提到,每一次按键结束,VolDirection都被重置为ADJUST_SAME。

通过看到这个流程,我们知道,不能这样做来实现我们的静音功能,于是看看直接设置绝对音量值的流程,看能否实现mute键。

设置绝对音量值的流程

直接设置音量,参考代码如下:

  1. int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
  2. setStreamVolume(AudioManager.STREAM_MUSIC, 0, flags);

只要音量值是0,就会变为mute状态,这个流程要简洁很多,可以自行查看:
setStreamVolume -> onSetStreamVolume

添加mute键解决方案

如下这种方式相对而言比较简单,至少不会有音量调节的bug。

  1. + private void workAroundGlobalMute(int stream) {
  2. +     int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
  3. +     setStreamVolume(AudioManager.STREAM_SYSTEM, 0, FLAG_VIBRATE);
  4. +     setStreamVolume(AudioManager.STREAM_NOTIFICATION, 0, FLAG_VIBRATE);
  5. +     setStreamVolume(AudioManager.STREAM_MUSIC, 0, flags);
  6. + }
  7. +
  8. + private void workAroundGlobalUnmute(int stream) {
  9. +     int showUIFlags = FLAG_SHOW_UI | FLAG_VIBRATE;
  10. +     adjustSuggestedStreamVolume(ADJUST_RAISE, stream, showUIFlags);
  11. + }
  12. +


  13.      case KeyEvent.KEYCODE_VOLUME_MUTE:
  14.          if (event.getRepeatCount() == 0) {
  15.              if (mUseMasterVolume) {
  16.                  setMasterMute(!isMasterMute());
  17.              } else {
  18.                 // TODO: Actually handle MUTE.
  19. +               if (!mute) {
  20. +                   workAroundGlobalMute(stream);
  21. +               } else {
  22. +                   workAroundGlobalUnmute(stream);
  23. +               }
  24.              }
  25.          }
  26.          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中找到如下一行:

  1. <!-- Flag indicating that the media framework should allow changing
  2.          master volume stream and nothing else . -->
  3.     <bool name="config_useMasterVolume">false</bool>

可以mute的STREAM类型 -- 谢谢该文章的介绍 http://blog.csdn.net/njuitjf/article/details/7028357
函数loadVolumeLevels中对此进行了设定:

  1. loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
  2.                     ((1 << AudioManager.STREAM_MUSIC) |
  3.                      (1 << AudioManager.STREAM_RING) |
  4.                      (1 << AudioManager.STREAM_NOTIFICATION) |
  5.                      (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) |
0

上一篇:搭建自己的repo server极简入门

下一篇:没有了

给主人留下些什么吧!~~