實際場景:
應付客戶電子產品的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);
}