淘先锋技术网

首页 1 2 3 4 5 6 7

设置中音量相关的Fragment调整到了NotificationSettings.java中。

我们可以在R.xml.notification_settings中看到如下

xref: /packages/apps/Settings/res/xml/notification_settings.xml

android:key="media_volume"

android:icon="@*android:drawable/ic_audio_vol"

android:title="@string/media_volume_option_title" />

android:key="alarm_volume"

android:icon="@*android:drawable/ic_audio_alarm"

android:title="@string/alarm_volume_option_title" />

android:key="ring_volume"

android:icon="@*android:drawable/ic_audio_ring_notif"

android:title="@string/ring_volume_option_title" />

android:key="notification_volume"

android:icon="@*android:drawable/ic_audio_ring_notif"

android:title="@string/notification_volume_option_title" />

这分别代表多媒体、闹钟、来电铃声和系统通知音量。所以在UI层面上用户是通过调节VolumeSeekBarPreference这个seekbar来最终对系统音量进行调整。

回头看NotificationSettings.java中初始化流程如下

xref: /packages/apps/Settings/src/com/android/settings/notification/NotificationSettings.java

//sound是包含上面4个SeekBar的PreferenceCategory

final PreferenceCategory sound = (PreferenceCategory) findPreference(KEY_SOUND);

initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC);

initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM);

//判断是否支持通话,支持移除NOTIFICATION_VOLUME否则移除RING_VOLUME

if (mVoiceCapable) {

mRingOrNotificationPreference =

initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING);

sound.removePreference(sound.findPreference(KEY_NOTIFICATION_VOLUME));

} else {

mRingOrNotificationPreference =

initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION);

sound.removePreference(sound.findPreference(KEY_RING_VOLUME));

}

initRingtones(sound);

private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();

private VolumeSeekBarPreference initVolumePreference(String key, int stream) {

final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key);

设置callback

volumePref.setCallback(mVolumeCallback);

//设置AudioManager的stream,可以在AudioSystem中看到具体int值代表的意义

volumePref.setStream(stream);

return volumePref;

}

mVolumeCallback如下,当你拖动seekbar对音量进行调节时这个callback会回馈响应,在界面上进行相应的调整。

private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {

//SeekBarVolumizer是主要负责对系统音量进行调整的类,会在后面描述

private SeekBarVolumizer mCurrent;

@Override

public void onSampleStarting(SeekBarVolumizer sbv) {

if (mCurrent != null && mCurrent != sbv) {

mCurrent.stopSample();

}

mCurrent = sbv;

if (mCurrent != null) {

mHandler.removeMessages(H.STOP_SAMPLE);

mHandler.sendEmptyMessageDelayed(H.STOP_SAMPLE, SAMPLE_CUTOFF);

}

}

@Override

public void onStreamValueChanged(int stream, int progress) {

if (stream == AudioManager.STREAM_RING) {

mHandler.removeMessages(H.UPDATE_RINGER_ICON);

mHandler.obtainMessage(H.UPDATE_RINGER_ICON, progress, 0).sendToTarget();

}

}

public void stopSample() {

if (mCurrent != null) {

mCurrent.stopSample();

}

}

};

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case UPDATE_PHONE_RINGTONE:

mPhoneRingtonePreference.setSummary((CharSequence) msg.obj);

break;

case UPDATE_NOTIFICATION_RINGTONE:

mNotificationRingtonePreference.setSummary((CharSequence) msg.obj);

break;

case STOP_SAMPLE:

mVolumeCallback.stopSample();

break;

case UPDATE_RINGER_ICON:

updateRingOrNotificationIcon(msg.arg1);

break;

}

}

private SeekBarVolumizer mVolumizer;

final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;

if (mVolumizer == null) {

mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc) {

// we need to piggyback on SBV's SeekBar listener to update our icon

@Override

public void onProgressChanged(SeekBar seekBar, int progress,

boolean fromTouch) {

super.onProgressChanged(seekBar, progress, fromTouch);

//这里的mCallback就是VolumePreferenceCallback对象

mCallback.onStreamValueChanged(mStream, progress);

}

};

}

mVolumizer.setSeekBar(mSeekBar);

mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);

mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());

@Override

public void onProgressChanged(SeekBar seekBar, int progress,

boolean fromTouch) {

super.onProgressChanged(seekBar, progress, fromTouch);

mCallback.onStreamValueChanged(mStream, progress);

}

这里我们进行下梳理在NotificationSettings.java内initVolumePreference函数的volumePref.setCallback(mVolumeCallback);把VolumePreferenceCallback这个callbak设置到VolumeSeekBarPreference.java内的mCallback中

使得SeekBarVolumizer内的onProgressChanged进行回调时NotificationSettings.java的相应回调函数都响应。

现在看下/frameworks/base/core/java/android/preference/的SeekBarVolumizer.java

private final Context mContext;

private final Handler mHandler;

private final H mUiHandler = new H();

private final Callback mCallback;

private final Uri mDefaultUri;

private final AudioManager mAudioManager;

private final int mStreamType;

private final int mMaxStreamVolume;

private final Receiver mReceiver = new Receiver();

private final Observer mVolumeObserver;

private int mOriginalStreamVolume;

private Ringtone mRingtone;

private int mLastProgress = -1;

private SeekBar mSeekBar;

private int mVolumeBeforeMute = -1;

private static final int MSG_SET_STREAM_VOLUME = 0;

private static final int MSG_START_SAMPLE = 1;

private static final int MSG_STOP_SAMPLE = 2;

private static final int MSG_INIT_SAMPLE = 3;

private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;

public SeekBarVolumizer(Context context, int streamType, Uri defaultUri,

Callback callback) {

mContext = context;

mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

mStreamType = streamType;

mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);

HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");

thread.start();

mHandler = new Handler(thread.getLooper(), this);

mCallback = callback;

mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);

mVolumeObserver = new Observer(mHandler);

mContext.getContentResolver().registerContentObserver(

System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),

false, mVolumeObserver);

mReceiver.setListening(true);

//mDefaultUri为调整seekbar时听到的调试音

if (defaultUri == null) {

if (mStreamType == AudioManager.STREAM_RING) {

defaultUri = Settings.System.DEFAULT_RINGTONE_URI;

} else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {

defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;

} else {

defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;

}

}

mDefaultUri = defaultUri;

mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);

}

public void setSeekBar(SeekBar seekBar) {

if (mSeekBar != null) {

mSeekBar.setOnSeekBarChangeListener(null);

}

mSeekBar = seekBar;

mSeekBar.setOnSeekBarChangeListener(null);

mSeekBar.setMax(mMaxStreamVolume);

mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume);

mSeekBar.setOnSeekBarChangeListener(this);

}

@Override

public boolean handleMessage(Message msg) {

switch (msg.what) {

//真正调节系统音量的地方

case MSG_SET_STREAM_VOLUME:

mAudioManager.setStreamVolume(mStreamType, mLastProgress,

AudioManager.FLAG_SHOW_UI_WARNINGS);

break;

case MSG_START_SAMPLE:

onStartSample();

break;

case MSG_STOP_SAMPLE:

onStopSample();

break;

case MSG_INIT_SAMPLE:

onInitSample();

break;

default:

Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);

}

return true;

}

//初始化mRingtone,我们知道系统播放的铃声最后是通过mRingtone.play()来实现的

private void onInitSample() {

mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri);

if (mRingtone != null) {

mRingtone.setStreamType(mStreamType);

}

}

private void postStartSample() {

mHandler.removeMessages(MSG_START_SAMPLE);

mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),

isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);

}

//开始播放调试音

private void onStartSample() {

if (!isSamplePlaying()) {

if (mCallback != null) {

mCallback.onSampleStarting(this);

}

if (mRingtone != null) {

try {

mRingtone.play();

} catch (Throwable e) {

Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e);

}

}

}

}

void postStopSample() {

// remove pending delayed start messages

mHandler.removeMessages(MSG_START_SAMPLE);

mHandler.removeMessages(MSG_STOP_SAMPLE);

mHandler.sendMessage(mHandler.obtainMessage(MSG_STOP_SAMPLE));

}

//停止播放调试音

private void onStopSample() {

if (mRingtone != null) {

mRingtone.stop();

}

}

public void stop() {

postStopSample();

mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);

mSeekBar.setOnSeekBarChangeListener(null);

mReceiver.setListening(false);

mHandler.getLooper().quitSafely();

}

public void revertVolume() {

mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0);

}

public void onProgressChanged(SeekBar seekBar, int progress,

boolean fromTouch) {

if (!fromTouch) {

return;

}

postSetVolume(progress);

}

void postSetVolume(int progress) {

// Do the volume changing separately to give responsive UI

mLastProgress = progress;

mHandler.removeMessages(MSG_SET_STREAM_VOLUME);

mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));

}

public void onStartTrackingTouch(SeekBar seekBar) {

}

public void onStopTrackingTouch(SeekBar seekBar) {

postStartSample();

}

//判断是否正在播放铃声

public boolean isSamplePlaying() {

return mRingtone != null && mRingtone.isPlaying();

}

public void startSample() {

postStartSample();

}

public void stopSample() {

postStopSample();

}

public SeekBar getSeekBar() {

return mSeekBar;

}

public void changeVolumeBy(int amount) {

mSeekBar.incrementProgressBy(amount);

postSetVolume(mSeekBar.getProgress());

postStartSample();

mVolumeBeforeMute = -1;

}

public void muteVolume() {

if (mVolumeBeforeMute != -1) {

mSeekBar.setProgress(mVolumeBeforeMute);

postSetVolume(mVolumeBeforeMute);

postStartSample();

mVolumeBeforeMute = -1;

} else {

mVolumeBeforeMute = mSeekBar.getProgress();

mSeekBar.setProgress(0);

postStopSample();

postSetVolume(0);

}

}

public void onSaveInstanceState(VolumeStore volumeStore) {

if (mLastProgress >= 0) {

volumeStore.volume = mLastProgress;

volumeStore.originalVolume = mOriginalStreamVolume;

}

}

public void onRestoreInstanceState(VolumeStore volumeStore) {

if (volumeStore.volume != -1) {

mOriginalStreamVolume = volumeStore.originalVolume;

mLastProgress = volumeStore.volume;

postSetVolume(mLastProgress);

}

}

private final class H extends Handler {

private static final int UPDATE_SLIDER = 1;

@Override

public void handleMessage(Message msg) {

if (msg.what == UPDATE_SLIDER) {

if (mSeekBar != null) {

mSeekBar.setProgress(msg.arg1);

mLastProgress = mSeekBar.getProgress();

}

}

}

public void postUpdateSlider(int volume) {

obtainMessage(UPDATE_SLIDER, volume, 0).sendToTarget();

}

}

private final class Observer extends ContentObserver {

public Observer(Handler handler) {

super(handler);

}

@Override

public void onChange(boolean selfChange) {

super.onChange(selfChange);

if (mSeekBar != null && mAudioManager != null) {

final int volume = mAudioManager.getStreamVolume(mStreamType);

mUiHandler.postUpdateSlider(volume);

}

}

}

这里忽视其他所有操作关注一下主要流程,我们知道这个类implements了OnSeekBarChangeListener,这个listener包含了当值发生变化响应的onProgressChanged和开始完成操作的onStartTrackingTouch和onStopTrackingTouch 所以当开始拖动seekbar时onStartTrackingTouch响应无操作,然后onProgressChanged发生变化postSetVolume(progress);从而调用最关键函数mAudioManager.setStreamVolume(mStreamType, mLastProgress, AudioManager.FLAG_SHOW_UI_WARNINGS);实现音量调整,然后调用onStopTrackingTouch,通过handler调用onStartSample函数来播放调试音,在这过程中通过和setting模块绑定的 callback来对setting模块进行响应,这就时和上层setting模块互动的一个主要流程。