实际场景:
应付客户电子产品的3C认证,声音大小必须小于85DB,但是产品以K歌为特色,外响声音必须大。
解决思路:
设备定义两个声音模式,成人模式和儿童模式。成人模式的音量为原始音量,儿童模式的音量最大值为原始音量最大值得一半,或者更小
解决方案:
- 自己应用层实现,音量条最大值是自己定义的。
- 直接系统层Framework层,在Audio层写代码,定制自己的功能。
说明:方案一太LowB,下面具体分析方案二的实现。
需求分析:
- 模式需要切换,设置儿童模式、是否儿童模式
- 设置当前儿童模式中Alarm最大音量,获取当前儿童模式最大Alarm音量
- 设置当前儿童模式Call最大音量,获取当前儿童模式下最大Call音量
- 设置当前儿童模式Music最大音量,获取当前儿童模式下最大Music音量
特别说明
本文不做Audio层各种流程,各种的原理,只做现有功能逻辑的实现和分析。
IAudioService.aidl 添加接口。
/**
* {@hide}
*/
interface IAudioService {
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage);
void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage);
void adjustMasterVolume(int direction, int flags, String callingPackage);
boolean childrenMode();
void setChildrenMode(boolean ischildren);
void setAlarmMaxVolumeInChildMode(int index);
void setCallMaxVolumeInChildMode(int index);
void setMusicMaxVolumeInChildMode(int index);
int getAlarmMaxIndexInChildMode();
int getCallMaxIndexInChildMode();
int getMusicMaxIndexInChildMode();
}
AudioService.java 添加具体实现。
第一步:添加默认的声音大小,默认就是儿童模式
private int mAlarmMaxIndexInChildMode=3;
private int mMusicMaxIndexInChildMode=6;
private int mCallMaxIndexInChildMode=2;
private boolean mIsChildren=true;
可以对比下源码中默认的声音大小
/** @hide Maximum volume index values for audio streams */
private static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15 // STREAM_TTS
};
private static int[] DEFAULT_STREAM_VOLUME = new int[] {
3, // STREAM_VOICE_CALL
4, // STREAM_VOICE_CALL
4, // STREAM_RING
9, // STREAM_MUSIC
4, // STREAM_ALARM
4, // STREAM_NOTIFICATION
9, // STREAM_BLUETOOTH_SCO
4, // STREAM_SYSTEM_ENFORCED
9, // STREAM_DTMF
9 // STREAM_TTS
};
第二步 实现定义的方法
public void setChildrenMode(boolean ischildren){
mIsChildren=ischildren;
}
public boolean childrenMode(){
return mIsChildren;
}
public int getAlarmMaxIndexInChildMode(){
return mAlarmMaxIndexInChildMode;
}
public int getMusicMaxIndexInChildMode(){
return mMusicMaxIndexInChildMode;
}
public int getCallMaxIndexInChildMode(){
return mCallMaxIndexInChildMode;
}
public void setAlarmMaxVolumeInChildMode(int index){
mAlarmMaxIndexInChildMode=index;
}
public void setMusicMaxVolumeInChildMode(int index){
mMusicMaxIndexInChildMode=index;
}
public void setCallMaxVolumeInChildMode(int index){
mCallMaxIndexInChildMode=index;
}
第三步,添加应用层和Framework应用层可以设置并获取当前最大音量的方法。
public int getStreamMaxIndex(int streamType){
int index=-1;
Log.d("TAG","streamType="+streamType);
switch(streamType){
case AudioManager.STREAM_VOICE_CALL:
index=getCallMaxIndexInChildMode();
break;
case AudioManager.STREAM_ALARM:
index=getAlarmMaxIndexInChildMode();
break;
case AudioManager.STREAM_MUSIC:
index=getMusicMaxIndexInChildMode();
break;
default:
index=3;
break;
}
Log.d("TAG","index="+index);
return index;
}
public void resetMaxVolume(){
Log.d("TAG","resetMaxVolume");
Log.d("TAG","mStreamType="+mStreamType);
mIndexMax = MAX_STREAM_VOLUME[mStreamType];
Log.d("TAG","mIndexMax="+mIndexMax);
AudioSystem.initStreamVolume(mStreamType, 0, mIndexMax);
mIndexMax *= 10;
readSettings();
}
接下来看下控制器Audiomanager的控制逻辑
逻辑控制的常规操作,入口在AudioManager控制类中,实际是通过aidl调用AudioService的
第四步,控制逻辑实现
/**
* AudioManager provides access to volume and ringer mode control.
* <p>
* Use <code>Context.getSystemService(Context.AUDIO_SERVICE)</code> to get
* an instance of this class.
*/
获取当前最大音量值
public int getStreamMaxIndex(int streamType){
int index=-1;
Log.d("TAG","streamType="+streamType);
switch(streamType){
case STREAM_VOICE_CALL:
index=getCallMaxIndexInChildMode();
break;
case STREAM_ALARM:
index=getAlarmMaxIndexInChildMode();
break;
case STREAM_MUSIC:
index=getMusicMaxIndexInChildMode();
break;
default:
index=3;
break;
}
Log.d("TAG","index="+index);
return index;
}
是否是儿童模式
public boolean childrenMode(){
boolean result=false;
IAudioService service = getService();
try {
result=service.childrenMode();
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
}
return result;
}
/**
*设置是否是儿童模式
*
*/
public void setChildrenMode(boolean ischildren){
IAudioService service = getService();
try {
service.setChildrenMode(ischildren);
} catch (RemoteException e) {
Log.e(TAG, "Can't call setChildrenMode(boolean ischildren) on AudioService:", e);
}
}
/**
*获取儿童模式下闹铃的最大音量
*/
public int getAlarmMaxIndexInChildMode(){
int result=-1;
IAudioService service = getService();
try {
result=service.getAlarmMaxIndexInChildMode();
} catch (RemoteException e) {
Log.e(TAG, "Can't call setMaxIndexInChildrenMode(int maxIndex) on AudioService:", e);
}
return result;
}
/**
*获取儿童模式下打电话的最大音量
*/
public int getCallMaxIndexInChildMode(){
int result=-1;
IAudioService service = getService();
try {
result=service.getCallMaxIndexInChildMode();
} catch (RemoteException e) {
Log.e(TAG, "Can't call setMaxIndexInChildrenMode(int maxIndex) on AudioService:", e);
}
return result;
}
public int getMusicMaxIndexInChildMode(){
int result=-1;
IAudioService service = getService();
try {
result=service.getMusicMaxIndexInChildMode();
} catch (RemoteException e) {
Log.e(TAG, "Can't call getMusicMaxIndexInChildMode(int index) on AudioService:", e);
}
return result;
}
public void setAlarmMaxVolumeInChildMode(int index){
IAudioService service = getService();
try {
service.setAlarmMaxVolumeInChildMode(index);
} catch (RemoteException e) {
Log.e(TAG, "Can't call setAlarmMaxVolumeInChildMode(int index) on AudioService:", e);
}
}
public void setMusicMaxVolumeInChildMode(int index){
IAudioService service = getService();
try {
service.setMusicMaxVolumeInChildMode(index);
} catch (RemoteException e) {
Log.e(TAG, "Can't call setMusicMaxVolumeInChildMode(int index) on AudioService:", e);
}
}
public void setCallMaxVolumeInChildMode(int index){
IAudioService service = getService();
try {
service.setCallMaxVolumeInChildMode(index);
} catch (RemoteException e) {
Log.e(TAG, "Can't call setCallMaxVolumeInChildMode(int index) on AudioService:", e);
}
}
基于以上已经完成了Audio层的接口扩展,现在怎么应用呢?
对于自带的系统进度条,
- 首先看是否是儿童模式,
- 获取最大的音量值给系统音量条【进度条】作为最大值
那么就需要更改VolumePanel.java了。
private int getStreamMaxVolume(int streamType) {
if (streamType == STREAM_MASTER) {
return mAudioManager.getMasterMaxVolume();
} else if (streamType == STREAM_REMOTE_MUSIC) {
if (mStreamControls != null) {
StreamControl sc = mStreamControls.get(streamType);
if (sc != null && sc.controller != null) {
PlaybackInfo ai = sc.controller.getPlaybackInfo();
return ai.getMaxVolume();
}
}
return -1;
} else {
if(mAudioManager.childrenMode()){ //判断一次,如果是儿童模式就直接获取自己定义的值就可以了
return mAudioManager.getStreamMaxIndex(streamType);
}else{
return mAudioManager.getStreamMaxVolume(streamType);
}
}
}
以上也是整个系统层的设置,逻辑功能实现,页面展现实现。那么实际切换应该放在应用层实现。
代码如下:
每次进入app默认设置一下音频大小、模式。对于Launcher定制案子,每次进入Launcher也就是开机后就初始化一下状态
调用方法 setMusicMaxVolumeInChildMode setCallMaxVolumeInChildMode
private void initVolume() {
LauncherApp.getInstance().getAsrAsynHandler().postDelayed(new Runnable() {
@Override
public void run() {
try {
setChildrenMode(PreferencesUtils.getBoolean(LauncherApp.getInstance(),PreferencesUtils.mIsChildMode,true));
}catch (Exception e){
}
AudioManager audioManager= (AudioManager) getSystemService(AUDIO_SERVICE);
try {
Method method=audioManager.getClass().getDeclaredMethod("setMusicMaxVolumeInChildMode",int.class);
method.setAccessible(true);
method.invoke(audioManager,ConfigConst.mMusicMaxVolumeChildrenMode);
}catch (Exception e){
e.printStackTrace();
}
try {
Method method=audioManager.getClass().getDeclaredMethod("setCallMaxVolumeInChildMode",int.class);
method.setAccessible(true);
method.invoke(audioManager,ConfigConst.mCallMaxVolumeChildrenMode);
}catch (Exception e){
e.printStackTrace();
}
if (audioManager == null) {
return;
}
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) , AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}
},2000);
}
如果需要重置恢复到正常的音频模式,声音大小:
调用方法: setChildrenMode(true); resetChildVolume();
public void setChildrenMode(boolean child){
LogUtils.d("EgMainActivity setChildrenMode:"+child);
AudioManager audioManager= (AudioManager) getSystemService(AUDIO_SERVICE);
try {
Method method=audioManager.getClass().getDeclaredMethod("setChildrenMode",boolean.class);
method.setAccessible(true);
method.invoke(audioManager,child);
} catch (Exception e) {
e.printStackTrace();
}
}
public void resetChildVolume(){
AudioManager audioManager= (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
ConfigConst.mMusicMaxVolumeChildrenMode,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
audioManager.setStreamVolume(AudioManager.STREAM_ALARM,
ConfigConst.mAlarmMaxVolumeChildrenMode,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
ConfigConst.mCallMaxVolumeChildrenMode,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}
如果切换到儿童模式:
调用方法 setChildrenMode(true); resetChildVolume();
public void setChildrenMode(boolean child){
AudioManager audioManager= (AudioManager) getSystemService(AUDIO_SERVICE);
try {
Method method=audioManager.getClass().getDeclaredMethod("setChildrenMode",boolean.class);
method.setAccessible(true);
method.invoke(audioManager,child);
} catch (Exception e) {
e.printStackTrace();
}
}
public void resetChildVolume(){
AudioManager audioManager= (AudioManager) getSystemService(AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
ConfigConst.mMusicMaxVolumeChildrenMode,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
audioManager.setStreamVolume(AudioManager.STREAM_ALARM,
ConfigConst.mAlarmMaxVolumeChildrenMode,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
ConfigConst.mCallMaxVolumeChildrenMode,
AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}