Audio聲音模式定製--聲音大小定製

實際場景:

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

源碼下載地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章