系统音量条相关

1、音量键处理

image-20200718161014564.png

如上,对于一般场景下,按音量上/下键,其事件传递到焦点窗口的根节点布局时(一般的Activity根节点布局都是DecorView)会走到DecorView的dispatchKeyEvent方法中,这里主要有两个分支,一个是Window.Callback类型的cb处理事件(这里如果Activity有处理音量键事件并返回true,则具体音量事件逻辑看应用自身逻辑,如果对应Activity在处理音量键逻辑中什么都不做,那音量键相当于也没有什么实际效果,也不会弹出系统音量条),还有一个分支则是PhoneWindow中调用onKeyDown和onKeyUp处理事件,这里也是一般的按音量键弹出音量条所走的逻辑

image-20200718163848255.png
image-20200718164021778.png

如上所示,在PhoneWindow的onKeyDown和onKeyUp方法中,对于音量上/下键事件的处理是调用MediaSessionManager的dispatchVolumeKeyEventAsSystemService方法,MediaSessionManager的服务端是MediaSessionService,MediaSessionManager的前面的方法调用都会调用到其服务端MediaSessionService中,在一般显示系统音量条过程中,MediaSessionService中的主要作用是构建调用AudioService调节音量的方法(包括调节的通道、调节的粒度和flags构建等),然后在AudioService中才是真正调节音量的主要代码,在音量调节中如果音量有改变,则会发送"android.media.VOLUME_CHANGED_ACTION"的广播通知音量变更,在音量调节后会调用sendVolumeUpdate方法去告知SystemUI音量变更

image-20200718172628942.png

这里调用了VolumeController的postVolumeChanged,其中VolumeController是AudioService的静态内部类

image-20200718202148663.png

VolumeController的postVolumeChanged方法即调用了其mController变量的volumeChanged方法,这里mController变量是调用VolumeController的setController方法进行设置的

image-20200718202306217.png

在AudioService的setVolumeController方法中会调用VolumeController的setController方法

image-20200718202420757.png

在AudioManager中也有setVolumeController,其会调用到AudioService的setVolumeController方法,而在SystemUI中有调用该方法,其设置的VolumeController是VolumeDialogControllerImpl的内部类VC的对象

所以上述音量变更后通知SystemUI既是调用了VolumeDialogControllerImpl的内部类VC的volumeChanged方法回调

2、系统音量条显示逻辑

image-20200724224905501.png
2.1、音量条显示逻辑

在前面AudioService中音量变更后回调到SystemUI的VolumeDialogControllerImpl.VC的volumeChanged时,即会触发显示系统音量条的逻辑(注意,这里有传入两个参数,streamType和flags),然后会调用到VolumeDialogControllerImpl的onVolumeChangedW方法中

image-20200724201644934.png

这里会根据flags和当前场景、一些定制来判断是否显示系统音量条,其中flags相关主要是需要包含AudioManager.FLAG_SHOW_UI,如果判断需要显示,则最后会调用到VolumeDialogImpl.java中的onShowRequested方法中去显示系统音量条

image-20200724202243363.png

onShowRequested方法中会调用showH方法去显示系统音量条

image-20200724202433129.png

在showH方法中有两处比较重要,一是rescheduleTimeoutH方法,该方法是计算系统音量条自动消失时间,发送延时消息去dismiss音量条,二是mDialog.show,在SystemUI启动时就已准备好了mDialog,在音量相关变更时会更新相关信息,然后在这里只需调用mDialog.show方法即会去显示系统音量条

image-20200724211928846.png
2.2、音量条布局

在SystemUI启动时,会调用到VolumeDialogImpl的init方法初始化,其中initDialog方法即是对音量条的显示的重要部分,这里会创建mDialog并加载对应布局和设置显示相关参数,其中加载具体各通道音量条的相关代码如下

image-20200724214506628.png

这里会对各通道的音量调用addRow方法,以将各通道的音量布局加载添加到mDialog布局中

image-20200724214732911.png

在initRow方法中会根据传入的参数构建VolumeRow对象,其中会添加布局等相关信息

2.2.1、音量通道布局的显示与隐藏

前面可以看到,音量条一般有加载七个通道的布局,但其实一般的话可能只会显示一个通道的音量

在构建mDialog和音量相关变更时(onStateChanged)会触发updateRowsH方法调用

image-20200724220752911.png

在updateRowsH方法中会遍历各通道的VolumeRow,调用shouldBeVisibleH方法来判断是否需要显示对应通道的音量条,然后设置对应控件可见性为VISIBLE或GONE

在音量相关变更时,会更新相关音量信息如音量大小等,然后会触发onStateChanged调用,其会调用到VolumeDialogImpl的onStateChangedH方法中更新视图相关,包括控件可见性,其也会调用updateVolumeRowH方法来更新各通道控件的信息如seekbar的进度

2.3、音量通道

前面有介绍系统音量条一般有加载七个不同通道的音量条,但其实通道并不止七个

image-20200724222036667.png

其定义都在AudioSystem中

image-20200724222302846.png

虽然有11个通道,但因为有些通道变更都是一起的,在音量变更时会有个映射,传到SystemUI的是其映射后的通道(其实在音量变更时,即使设置单个通道音量变更,其也会影响映射相关的多个通道)

image-20200724222521284.png

其中mStreamVolumeAlias映射关系是在AudioService中赋值的

image-20200724222806145.png

比如一般手机是PLATFORM_VOICE,则mStreamVolumeAlias则是STREAM_VOLUME_ALIAS_VOICE

image-20200724223455799.png

上面即可看出其映射关系

2.4、activeStream

在AudioService中有个getActiveStreamType方法,其可获取当前activeStream,同一时间只有一个activeStream,一般使用中的音频通道即为activeStream,如播放音乐时一般activeStream是媒体通道STREAM_MUSIC,但也有其他情况,该方法中也说明了在多个音频通道同时播放或无音频播放时的activeStream

3、安全音量

image-20200725200542853.png

在插入耳机后在调大音量时可能会弹出如上Dialog,在通过AudioService调节音量时会调用checkSafeMediaVolume方法

image-20200725203104454.png

在一些特定设备如有线耳机等,且调节的音量通道是媒体通道,且调节音量大于安全音量时,checkSafeMediaVolume方法返回false,此时会调用AudioService$VolumeController的postDisplaySafeVolumeWarning方法

image-20200725203751056.png

然后其流程与volumeChanged差不多,其会调用到SystemUI的VolumeDialogImpl的showSafetyWarningH方法来显示上面提示

image-20200725203947694.png

4、自定义音量按键事件处理

很多视频播放界面都会自定义音量键处理和音量控件,其主要包括两部分,一是获取音量键事件,并调节音量,且注意应不让系统音量条显示,二是监听音量变更实现自定义的音量显示控件

image-20200727220151731.png

如上,自己本地写了个小demo,在拦截音量上/下键并自己处理返回true后,在该界面按音量上/下键就不会弹系统音量条了(注意,这里adjustStreamVolume是调节音量的api,其第二个参数如果包含了AudioManager.FLAG_SHOW_UI那么仍是会弹出系统音量条的)

image-20200727221711842.png

如上自己本地demo中写了个简单的seekbar的控件,然后监听了"android.media.VOLUME_CHANGED_ACTION"广播,收到广播后刷新seekbar进度

当然,如果需要音量控件是可滑动调节的,那么还需要监听对应控件的change事件,然后可调用AudioManager的setStreamVolume方法来设置音量

5、RingerMode

Android有三种响铃模式:响铃、振动、静音,在音量条上方按钮点击可切换不同模式,其在不同模式下主要会影响铃声相关的声音,如通知等

image-20200801161505257.png

先查看下点击切换响铃模式按钮相关逻辑,响铃按钮是mRingerIcon,其点击事件如下

image-20200801161340942.png

这里主要切换响铃模式逻辑是其中根据当前的模式和机器相关情况,计算新的响铃模式,然后调用mController.setRingerMode方法来设置新的响铃模式,其中mController是VolumeDialogControllerImpl的对象,其最后会调用到AudioManager的接口来设置新的响铃模式,这里按照上面逻辑会调用其setRingerModeInternal方法

image-20200801162528162.png

AudioManager会直接调到AudioService中

image-20200801162915940.png

这里主要会调用到AudioService的setRingerMode方法

image-20200801163446151.png

其主要代码有三部分

一是setRingerModeInt方法,这里是主要部分逻辑,放在后面查看

二是mRingerModeDelegate相关回调,这里mRingerModeDelegate其实是ZenModeHelper的内部类RingerModeDelegate的对象,这里回调会记录响铃模式和可能调整勿扰模式

三是调用setRingerModeExt方法,这里会发送"android.media.RINGER_MODE_CHANGED"的广播

这里继续查看setRingerModeInt方法:

image-20200801165201500.png

这里逻辑也比较多,简要介绍下

1、muteRingerModeStreams,该方法是这里主要部分逻辑,也是响铃模式切换的主要逻辑,在该方法中会根据响铃模式和其他如勿扰模式、设备等相关情况,判断是否需要将一些通道静音,如一般情况下,从响铃模式切换到振动或静音模式时,会将铃声相关通道mute,音量设为0,而切回响铃模式时,又会将音量设回去

2、如果persist参数为true,则一般会将新响铃模式设置到Settings.Global.MODE_RINGER

3、如果响铃模式变更,这里会发送"android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION"广播

5.1、音量调节触发RingerMode变更及VolumePolicy

在调节铃声音量时,我们可能注意到,在响铃模式,将铃声音量调制最低时,会自动变为振动模式,而在振动模式,将铃声音量调高时,会自动退出振动模式变为响铃模式

这是因为在调节音量时会有相关检查

在设置音量时,其在AudioService中会触发onSetStreamVolume调用

image-20200801172241812.png

其中getNewRingerMode一般会根据铃声音量来计算新的响铃模式

image-20200801172418078.png

在调节音量时也可能会触发checkForRingerModeChange方法调用

image-20200801172845958.png

这里未将checkForRingerModeChange方法全部贴出来,其也是会根据当前相关情景调节响铃模式

在上面一些判断中,除了手机有没有振动器等的判断,还有mVolumePolicy的变量的判断,VolumePolicy是个单独的类,其主要是保存有几个变量

image-20200801174249287.png

一般这个volumepolicy是SystemUI调用setVolumePolicy有设置的,

image-20200801173739711.png

你可能感兴趣的:(系统音量条相关)