RingerMode 设置和勿扰模式关系

来源:互联网 发布:无线音响 知乎 编辑:程序博客网 时间:2024/06/05 15:53

问题引入,在项目中,Android6.0项目中,客户要求去掉勿扰模式,当时我们只是去掉了勿扰模式的界面显示部分,即在SystemUI 的VolumeDialog.java类当中去掉了勿扰模式显示的部分和设置中界面显示部分。而实际的功能并为做修改。这样做之后又出现了新问题。

android5.1以后,在按音量下键时,将音量调到0之后,在继续按音量下键,会将情景模式调到勿扰模式,无法直接调到静音模式。我们在界面修改,在铃声到0后震动模式后,再次点击音量下键时,在更新界面处显示静音图标,以为这样可以规避问题满足客户需求,结果发现,当选择震动模式后,点击音量下到静音,这样没有问题。但是在设置中,先选择震动模式,然后关闭静音时震动,则应该到静音模式,实际上按音量下键调出volumeDialog时,显示的还是震动图标,而此时状态栏中显示的确实是静音图标?如果我们直接从正常的铃音模式切换到静音模式,那么图标都显示正常的。why?为什么这么神奇?为了解决上面问题,到代码中查找原因:

 

首先先说一下,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;

 

此处务必记住, AudioManager中定义的铃声类型(RingerMode)只有以上三种,并没有勿扰模式。所以说,勿扰模式是属于情景模式,但是不属于铃声模式的范畴。

 

其次, AudioManager.java这个类还定义了设置和获取铃声模式的public方法,getRingerMode(),setRingerMode(int)


进入到setRingerMode(int)发现,调用的是AudioService.java的setRingerModeExternal(),

继续调用到AudioService.java的setRingerMode(ringerMode, caller, true /*external*/)。

而设置铃声模式主要的实现就是在AudioService.java的setRingerMode()中,此处第三个参数传入的是true,setRingerMode的方法如下


在setRingerMode中前面的判断先不关注,直接看try语句里面的部分,当调用AudioManager的setRingerMode方法后,传入的参数external 是true,那么会进入到try语句中的上半部分。即调用setRingerModeExt(),


这个方法先判断传入的ringerMode值是否和mRingerModeExternal相等,若不同的话,直接将传入的ringerMode值赋给了mRingerModeExternal,然后发送RINGER_MODE_CHANGED_ACTION广播。

接下来setRingerMode()方法会去检查是否要去更新mRingerModeInternal这个变量的值。



而此时ringerMode又是从mRingerModeDelegate.onSetRingerModeExternal中从新更新了依次。Ringermodedelegate类是一个接口,而ZenModeHelper.java的RingerModeDelegate内部类实现了该接口,接下来看RingerModeDelegate类的onSetRingerModeExternal方法,现在才到了今天想分享的关键地方,也是引起项目出现问题的地方。

先看onSetRingerModeExternal实现


这个方法参数有点多,先解释下:

ringerModeOld  本次还未设置之前的ringerMode值,对应与Audioservice中的mRingerModeExternal的值

ringerModeNew  本次即将要设置的ringerMode值,该值是从第三方应用或系统应用,如设置,systemUI下来栏中图标传过来的值。

caller 调用者,那个应用来调的(分析该问题,可不关注)

ringerModeInternal 当前AudioService中mRingerMode的值,

policy 可不关注

 

搞清楚这些参数意思之后,再来分析代码,

boolean isChange 是判断当前ringerMode(mRingerModeExternal)值和即将要设置的ringerMode值是否相等,

isVibrate 是当前ringerModeInternal(对应与AudioService的mRingerMode值)是否是震动模式

 

然后判断即将要设置的ringerMode是那种类型,震动或者normal模式时,直接关闭勿扰模式,返回ringerModeInternalOut,也就是ringerModeNew,当ringerMode是静音模式时,判断isChange是否为true,true则进入勿扰,然后更新ringerModeInternalOut,再返回ringerModeInternalOut,而出现问题的地方是这里。先说流程,后面在分析问题。

 

返回之后又回到了AudioService的setRingerMode方法,再判断返回的ringerMode和当前的ringerModeInternal是否相等,若不相等,则调用setRingerModeInt()方法,



ringerModeInternal是否相等,若不相等,则调用setRingerModeInt()方法,

 

该方法即去同步设置mRingMode的值,然后发送INTERNAL_RINGER_MODE_CHANGED_ACTION广播。可见AudioService中的mRingerMode和mRingerModeExternal是否需要同步起作用的还是在Ringermodedelegate中。

 

现在来分析出现问题的原因,问题是:在设置或者systemUI中先打开静音模式开关,在打开静音时震动开关,此时是震动模式,然后关闭静音时震动开关,此时状态栏中显示的是静音模式,按音量下键,查看volumeDialog发现,是震动图标。这个问题的原因还是要看onSetRingerModeExternal方法。

这种情况下,onSetRingerModeExternal的几个参数相当与:

ringerModeOld  是震动模式

ringerModeNew  是静音模式

ringerModeInternal  震动模式

isChange true

isVibrate true  

这样的话按照代码流程,ringerModeInternalOut最终赋的值还是AudioManager.RINGER_MODE_VIBRATE,那么进入AudioService中会将mRingterMode的值设置成AudioManager.RINGER_MODE_VIBRATE,但是此时mRingerModeExternal的值是AudioManager.RINGER_MODE_SILENT,而设置中和systemUI状态栏使用的是mRingerModeExternal,所以显示的是静音,而且状态栏和设置中监听的广播也是RINGER_MODE_CHANGED_ACTION,即mRingerModeExternal改变后发出的广播。而VolumeDialog.java中用到的是mRingterMode(这个可到该类中去看,是通过AudioManager.getRingerModeInternal()获取的),当然显示的是震动图标了。

 

另一个问题,为什么我先设置成normal模式,再设置成静音模式时,都显示正常呢?

这种情况下,因为我们先设置了normal模式,那么onSetRingerModeExternal的参数相当与:

ringerModeOld  正常模式

ringerModeNew  静音模式

ringerModeInternal  正常模式

isChange true

isVibrate false

 

 此时依然会将情景模式切换到勿扰,但因为现在isVibrate是false,所以返回的ringerModeInternalOut是AudioManager.RINGER_MODE_SILENT,然后AudioService中同样会将mRingerMode设置成AudioManager.RINGER_MODE_SILENT,所以此时mRingerMode和mRingerModeExternal相同,都是AudioManager.RINGER_MODE_SILENT,所以这时候无论在systemUI,Settings还是VolumeDialog.java都显示静音。

原创粉丝点击