由於目前的Android系統如果系統的SYSTEM(STREAM_SYSTEM)中的音量設置或者調節為0,相關的STREAM_*的音量也會被Mute住,比如說,我現在的系統將對應的STREAM_SYSTEM和STREAM_RING/STREAM_NOTIFICATION在Mute功能方面綁定的,如果我將STREAM_SYSTEM的volume調節為0,那么Android系統會將STREAM_SYSTEM這個音量MUTE,記住MUTE住和volume為0是兩回事,對應的volume=0,依然能夠聽到聲音,只是非常的小而已,但是如果是MUTE了,無論volume是否為0,均在聽不到聲音,只是現在Android系統在當使用者將STREAM_SYSTEM調節為0時,會開啟Mute功能,將STREAM_SYSTEM種類的徹底MUTE住,另外RING/NOTIFICATION是兩關相關的,所以他們也會被MUTE住,具體參看下面的程序:
<0> : 修改的文件名大致在./framework/base/media/java/android/media/AudioService.java,路徑可能有不正確,但是修改的文件是AudioService.java
<1> :現將各自的關系分離開:
private final int[] STREAM_VOLUME_ALIAS = new int[] { AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM //--- AudioSystem.STREAM_RING, // STREAM_SYSTEM AudioSystem.STREAM_RING, // STREAM_RING AudioSystem.STREAM_MUSIC, // STREAM_MUSIC AudioSystem.STREAM_ALARM, // STREAM_ALARM AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION //--- AudioSystem.STREAM_RING, // STREAM_NOTIFICATION AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO AudioSystem.STREAM_SYSTEM_ENFORCED, // STREAM_SYSTEM_ENFORCED //--- AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED AudioSystem.STREAM_DTMF, // STREAM_DTMF //--- AudioSystem.STREAM_RING, // STREAM_DTMF AudioSystem.STREAM_TTS, // STREAM_TTS //--- AudioSystem.STREAM_MUSIC // STREAM_TTS }; private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] { AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM AudioSystem.STREAM_RING, // STREAM_RING AudioSystem.STREAM_MUSIC, // STREAM_MUSIC AudioSystem.STREAM_ALARM, // STREAM_ALARM AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION //AudioSystem.STREAM_RING, // STREAM_NOTIFICATION AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED AudioSystem.STREAM_MUSIC, // STREAM_DTMF AudioSystem.STREAM_MUSIC // STREAM_TTS };
<2> : 修改private void readPersistedSettings()里面的"或"關系,將RING/NOTIFICATION將其不要跟隨STREAM_SYSTEM一起MUTE,只是MUTE STREAM_SYSTEM一個就好了,總共在這個方法中修改三處:
下面有兩處:
// make sure settings for ringer mode are consistent with device type: non voice capable // devices (tablets) include media stream in silent mode whereas phones don't. mRingerModeAffectedStreams = Settings.System.getIntForUser(cr, Settings.System.MODE_RINGER_STREAMS_AFFECTED, (/*(1 << AudioSystem.STREAM_RING)/*|(1 << AudioSystem.STREAM_NOTIFICATION)|*/ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)), UserHandle.USER_CURRENT); // ringtone, notification and system streams are always affected by ringer mode mRingerModeAffectedStreams |= /*(1 << AudioSystem.STREAM_RING)| (1 << AudioSystem.STREAM_NOTIFICATION)|*/ (1 << AudioSystem.STREAM_SYSTEM);
第三處:
// only effort system itself mMuteAffectedStreams = System.getIntForUser(cr, System.MUTE_STREAMS_AFFECTED, (/*(1 << AudioSystem.STREAM_MUSIC)| (1 << AudioSystem.STREAM_RING)|*/ (1 << AudioSystem.STREAM_SYSTEM)), UserHandle.USER_CURRENT);
其實我還修改了另外一個方法public void setStreamMute(int streamType, boolean state, IBinder cb)里面的內容:
/** @see AudioManager#setStreamMute(int, boolean) */ public void setStreamMute(int streamType, boolean state, IBinder cb) { if(streamType==AudioManager.STREAM_RING || streamType==AudioManager.STREAM_NOTIFICATION){ return ; } if (isStreamAffectedByMute(streamType)) { mStreamStates[streamType].mute(cb, state); } }
我增加了三行,其實我個人覺得,我前面分離的它們所有者的關系,這一步應該不需要了,但是我為了以防萬一,我還是在它准備調用mute功能里面再次做了一次檢查,其他它下面的isStreamAffectedByMute(streamType)條件就可以判斷了.當然反過來想一想,如果我沒有修改前面三處,直接在這里增加的三行同樣可以擺平MUTE功能不會施加在RING和NOTIFICATION上.當然我的Android系統被改成RING/NOTIFICATION的volume大小始終保持一致了,所以同樣還是要修改前面3點,保證所有的STREAM_**不會被其他的干擾.
修改完以后,build一下,生成framework.jar,放到系統里面去,發現還是不行,沒有達到目的,但是RING/NOTIFICATION兩者的關系是分開了,但是當STREAM_SYSTEM調節為0時,還是會MUTE住RING/NOTIFICATION兩個音量,經過查找,發現還需要修改一個文件,不過這個文件時SettingsProvider.apk工程中的.修改如下:
路徑大概在./framework/base/packages/SettingsProvider/DatabaseHelper.java下,修改DatabaseHelper.java文件,修改的內容如下:
/** * Loads the default volume levels. It is actually inserting the index of * the volume array for each of the volume controls. * * @param db the database to insert the volume levels into */ private void loadVolumeLevels(SQLiteDatabase db) { SQLiteStatement stmt = null; try { stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); loadSetting(stmt, Settings.System.VOLUME_MUSIC, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_MUSIC]); loadSetting(stmt, Settings.System.VOLUME_RING, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_RING]); loadSetting(stmt, Settings.System.VOLUME_SYSTEM, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]); loadSetting( stmt, Settings.System.VOLUME_VOICE, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]); loadSetting(stmt, Settings.System.VOLUME_ALARM, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_ALARM]); loadSetting( stmt, Settings.System.VOLUME_NOTIFICATION, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_NOTIFICATION]); loadSetting( stmt, Settings.System.VOLUME_BLUETOOTH_SCO, AudioManager.DEFAULT_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]); // By default: // - ringtones, notification, system and music streams are affected by ringer mode // on non voice capable devices (tablets) // - ringtones, notification and system streams are affected by ringer mode // on voice capable devices (phones) int ringerModeAffectedStreams = /*(1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |*/ (1 << AudioManager.STREAM_SYSTEM) | (1 << AudioManager.STREAM_SYSTEM_ENFORCED); if (!mContext.getResources().getBoolean( com.android.internal.R.bool.config_voice_capable)) { ringerModeAffectedStreams |= (1 << AudioManager.STREAM_MUSIC); } loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED, ringerModeAffectedStreams); loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED, ((1 << AudioManager.STREAM_MUSIC) | /*(1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |*/ (1 << AudioManager.STREAM_SYSTEM))); } finally { if (stmt != null) stmt.close(); } loadVibrateWhenRingingSetting(db); }
注意上面的幾個<<運算,將其中RING/NOTIFICATION去掉就可以了.
讓后再build一次,將會生成SettingsProvider.apk,將其放到系統里面,重啟再試一下,結果發現還是不行,暈吧.
通過終端進入,將系統的/data/*下所有的數據刪除,然后再重啟一下系統,就OK了.
下面給出測試DEMO程序:
package com.example.androidvolumedemo; import android.media.AudioManager; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.view.KeyEvent; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; public class MainActivity extends Activity implements OnSeekBarChangeListener { private AudioManager mAudioManager; private SeekBar mSpkSeekBar; private SeekBar mHandSeekBar; private SeekBar mHeadSeekBar; private SeekBar mRingSeekBar; private SeekBar mNotifSeekBar; private Button mUpdateBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); mSpkSeekBar = (SeekBar) findViewById(R.id.tf_gro_speaker_bar); mSpkSeekBar.setOnSeekBarChangeListener(this); mSpkSeekBar.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM)); mHandSeekBar = (SeekBar) findViewById(R.id.tf_gro_handset_bar); mHandSeekBar.setOnSeekBarChangeListener(this); mHandSeekBar.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)); mHeadSeekBar = (SeekBar) findViewById(R.id.tf_gro_headset_bar); mHeadSeekBar.setOnSeekBarChangeListener(this); mHeadSeekBar.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL)); mRingSeekBar = (SeekBar) findViewById(R.id.tf_gro_ring_bar); mRingSeekBar.setOnSeekBarChangeListener(this); mRingSeekBar.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING)); mNotifSeekBar = (SeekBar) findViewById(R.id.tf_gro_notif_bar); mNotifSeekBar.setOnSeekBarChangeListener(this); mNotifSeekBar.setMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION)); mUpdateBtn=(Button)findViewById(R.id.updatebtn); mUpdateBtn.setOnClickListener(new OnClickListener(){ @Override public void onClick(View arg0) { // TODO Auto-generated method stub updateUI(); } }); updateUI(); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: return true; } return super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { // TODO Auto-generated method stub switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: return true; case KeyEvent.KEYCODE_VOLUME_UP: return true; } return super.onKeyUp(keyCode, event); } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { // TODO Auto-generated method stub int id = seekBar.getId(); switch (id) { case R.id.tf_gro_speaker_bar: mAudioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, progress, AudioManager.FLAG_PLAY_SOUND); break; case R.id.tf_gro_handset_bar: mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, progress, AudioManager.FLAG_PLAY_SOUND); break; case R.id.tf_gro_headset_bar: mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, progress, AudioManager.FLAG_PLAY_SOUND); break; case R.id.tf_gro_ring_bar: mAudioManager.setStreamVolume(AudioManager.STREAM_RING, progress, AudioManager.FLAG_PLAY_SOUND); break; case R.id.tf_gro_notif_bar: mAudioManager.setStreamVolume(AudioManager.STREAM_NOTIFICATION, progress, AudioManager.FLAG_PLAY_SOUND); break; } // updateUI(); } private void updateUI(){ this.mSpkSeekBar.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_SYSTEM)); this.mHandSeekBar.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL)); this.mHeadSeekBar.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL)); this.mRingSeekBar.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_RING)); this.mNotifSeekBar.setProgress(mAudioManager.getStreamVolume(AudioManager.STREAM_NOTIFICATION)); } @Override public void onStartTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } @Override public void onStopTrackingTouch(SeekBar seekBar) { // TODO Auto-generated method stub } }
xml文件內容:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <LinearLayout android:layout_width="match_parent" android:layout_height="195dp" android:orientation="vertical" > <!-- system --> <TextView android:id="@+id/tev1" android:layout_width="wrap_content" android:layout_height="32dp" android:paddingLeft="10dip" android:text="speaker"/> <TableLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imagev4" android:paddingRight="10dip" android:paddingLeft="10dip" android:src="@drawable/ic_action_volume_on" /> <SeekBar android:id="@+id/tf_gro_speaker_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> </TableRow> </TableLayout> <!-- voice --> <TextView android:id="@+id/tev2" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="32dp" android:paddingLeft="10dip" android:text="handset"/> <TableLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imagev1" android:paddingRight="10dip" android:paddingLeft="10dip" android:src="@drawable/ic_action_ring_volume" /> <SeekBar android:id="@+id/tf_gro_handset_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> </TableRow> </TableLayout> <TextView android:id="@+id/tev3" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="32dp" android:paddingLeft="10dip" android:text="headset"/> <TableLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imagev3" android:paddingRight="10dip" android:paddingLeft="10dip" android:src="@drawable/ic_action_headset" /> <SeekBar android:id="@+id/tf_gro_headset_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> </TableRow> </TableLayout> <TextView android:id="@+id/tev5" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="32dp" android:paddingLeft="10dip" android:text="ring"/> <TableLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imagev2" android:paddingRight="10dip" android:paddingLeft="10dip" android:src="@drawable/ic_action_volume_on" /> <SeekBar android:id="@+id/tf_gro_ring_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> </TableRow> </TableLayout> <TextView android:id="@+id/tev2" android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="32dp" android:paddingLeft="10dip" android:text="notifications"/> <TableLayout android:layout_width="fill_parent" android:layout_height="wrap_content"> <TableRow android:layout_width="fill_parent" android:layout_height="wrap_content"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imagev2" android:paddingRight="10dip" android:paddingLeft="10dip" android:src="@drawable/ic_action_warning" /> <Button android:id="@+id/updatebtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="update UI" /> <SeekBar android:id="@+id/tf_gro_notif_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> </TableRow> </TableLayout> </LinearLayout> </ScrollView>
OK,就可以了.
如果要增加相關關系,即如果STREAM_SYSTEM的volume調節為0時,MUTE住了STREAM_SYSTEM,還要增加對STREAM_MUSIC的MUTE作用,可以依照RING修改的做法進行依葫蘆畫瓢,進行逆向操作,修改的文件都是一樣的.