1.简介
主要看下Ring¬ification volume ,支持语音通话的时候显示的是这个,不支持语音通话的话显示的是另外一个Notification volume(如果显示的话,在Alarm volume下),具体见小节2.1布局
1.1.截图
1.2.top_level_settings.xml
<com.android.settings.widget.HomepagePreference
android:fragment="com.android.settings.notification.SoundSettings"
android:icon="@drawable/ic_volume_up_24dp"
android:key="top_level_sound"
android:order="-90"
android:title="@string/sound_settings"
android:summary="@string/sound_dashboard_summary"
settings:highlightableMenuKey="@string/menu_key_sound"/>
2.SoundSettings
public class SoundSettings extends DashboardFragment implements OnActivityResultListener {
protected int getPreferenceScreenResId() {
return R.xml.sound_settings;
}
2.1.sound_settings.xml
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/sound_settings"
android:key="sound_settings"
settings:keywords="@string/keywords_sounds">
<!-- Remote volume group -->
<PreferenceCategory
android:key="remote_media_group"
android:title=""
android:order="-185"
settings:allowDividerBelow="true"
settings:controller="com.android.settings.notification.RemoteVolumeGroupController">
</PreferenceCategory>
<!-- Media volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="media_volume"
android:icon="@drawable/ic_media_stream"
android:title="@string/media_volume_option_title"
android:order="-180"
settings:controller="com.android.settings.notification.MediaVolumePreferenceController"/>
<!-- Media output switcher -->
<Preference
android:key="media_output"
android:title="@string/media_output_title"
android:dialogTitle="@string/media_output_title"
android:order="-175"
settings:searchable="false"
settings:controller="com.android.settings.sound.MediaOutputPreferenceController"/>
<!-- Call volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="call_volume"
android:icon="@drawable/ic_local_phone_24_lib"
android:title="@string/call_volume_option_title"
android:order="-170"
settings:controller="com.android.settings.notification.CallVolumePreferenceController"/>
<!-- Hands free profile output switcher -->
<ListPreference
android:key="take_call_on_output"
android:title="@string/take_call_on_title"
android:dialogTitle="@string/take_call_on_title"
android:order="-165"
settings:searchable="false"
settings:controller="com.android.settings.sound.HandsFreeProfileOutputPreferenceController"/>
<!-- Ring & Notification volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="ring_volume"
android:icon="@drawable/ic_notifications"
android:title="@string/ring_volume_option_title"
android:order="-160"
settings:controller="com.android.settings.notification.RingVolumePreferenceController"/>
<!-- Alarm volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="alarm_volume"
android:icon="@*android:drawable/ic_audio_alarm"
android:title="@string/alarm_volume_option_title"
android:order="-150"
settings:controller="com.android.settings.notification.AlarmVolumePreferenceController"/>
<!-- Notification volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="notification_volume"
android:icon="@drawable/ic_notifications"
android:title="@string/notification_volume_option_title"
android:order="-140"
settings:controller="com.android.settings.notification.NotificationVolumePreferenceController"/>
<!-- DO Not Disturb 勿扰模式 -->
<!-- Interruptions -->
<com.android.settingslib.RestrictedPreference
android:key="zen_mode"
android:title="@string/zen_mode_settings_title"
android:fragment="com.android.settings.notification.zen.ZenModeSettings"
android:order="-130"
settings:useAdminDisabledSummary="true"
settings:keywords="@string/keywords_sounds_and_notifications_interruptions"
settings:controller="com.android.settings.notification.zen.ZenModePreferenceController"/>
<!-- Phone ringtone ,手机铃声-->
<com.android.settings.DefaultRingtonePreference
android:key="phone_ringtone"
android:title="@string/ringtone_title"
android:dialogTitle="@string/ringtone_title"
android:summary="@string/summary_placeholder"
android:ringtoneType="ringtone"
android:order="-120"
settings:keywords="@string/sound_settings"/>
<!-- Live Caption -110 and Now Playing -105-->
<!-- Spatial audio -->
<Preference
android:key="spatial_audio_summary"
android:title="@string/spatial_audio_title"
android:fragment="com.android.settings.notification.SpatialAudioSettings"
android:order="-107"
settings:controller="com.android.settings.notification.SpatialAudioParentPreferenceController"/>
<Preference
android:key="media_controls_summary"
android:title="@string/media_controls_title"
android:fragment="com.android.settings.sound.MediaControlsSettings"
android:order="-100"
settings:controller="com.android.settings.sound.MediaControlsParentPreferenceController"
settings:keywords="@string/keywords_media_controls"/>
<!-- Also vibration -->
<Preference
android:fragment="com.android.settings.accessibility.VibrationSettings"
android:key="vibration_and_haptics"
android:title="@string/accessibility_vibration_settings_title"
android:order="-90"
settings:controller="com.android.settings.accessibility.VibrationPreferenceController"
settings:keywords="@string/keywords_vibration"/>
<com.android.settingslib.PrimarySwitchPreference
android:key="gesture_prevent_ringing_sound"
android:title="@string/gesture_prevent_ringing_sound_title"
android:order="-80"
android:fragment="com.android.settings.gestures.PreventRingingGestureSettings"
settings:controller="com.android.settings.gestures.PreventRingingParentPreferenceController"/>
<!-- Default notification ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="notification_ringtone"
android:title="@string/notification_ringtone_title"
android:dialogTitle="@string/notification_ringtone_title"
android:summary="@string/summary_placeholder"
android:ringtoneType="notification"
android:order="-70"/>
<!-- Default alarm ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="alarm_ringtone"
android:title="@string/alarm_ringtone_title"
android:dialogTitle="@string/alarm_ringtone_title"
android:summary="@string/summary_placeholder"
android:persistent="false"
android:ringtoneType="alarm"
android:order="-60"/>
<!-- Dial pad tones -->
<SwitchPreference
android:key="dial_pad_tones"
android:title="@string/dial_pad_tones_title"
android:order="-50"/>
<!-- Screen locking sounds -->
<SwitchPreference
android:key="screen_locking_sounds"
android:title="@string/screen_locking_sounds_title"
android:order="-45"/>
<!-- Charging sounds -->
<SwitchPreference
android:key="charging_sounds"
android:title="@string/charging_sounds_title"
android:order="-40"/>
<!-- Docking sounds -->
<SwitchPreference
android:key="docking_sounds"
android:title="@string/docking_sounds_title"
android:order="-35"/>
<!-- Touch sounds -->
<SwitchPreference
android:key="touch_sounds"
android:title="@string/touch_sounds_title"
android:order="-30"/>
<!-- Show vibrate icon in status bar -->
<SwitchPreference
android:key="vibrate_icon"
android:title="@string/vibrate_icon_title"
android:order="-27"/>
<!-- Dock speaker plays -->
<DropDownPreference
android:key="dock_audio_media"
android:title="@string/dock_audio_media_title"
android:summary="%s"
android:order="-20"/>
<!-- Boot sounds -->
<SwitchPreference
android:key="boot_sounds"
android:title="@string/boot_sounds_title"
android:order="-15"/>
<!-- Emergency tone -->
<DropDownPreference
android:key="emergency_tone"
android:title="@string/emergency_tone_title"
android:summary="%s"
android:order="-10"/>
<!-- Call connected tones -->
<SwitchPreference
android:key="call_connected_tones"
android:title="@string/call_connected_tones_title" />
<Preference
android:key="sound_work_settings"
android:title="@string/sound_work_settings"
android:fragment="com.android.settings.notification.SoundWorkSettings"
android:order="100"
settings:controller="com.android.settings.notification.WorkSoundsPreferenceController"/>
</PreferenceScreen>
2.2.onAttach
public void onAttach(Context context) {
super.onAttach(context);
ArrayList<VolumeSeekBarPreferenceController> volumeControllers = new ArrayList<>();
volumeControllers.add(use(AlarmVolumePreferenceController.class));
volumeControllers.add(use(MediaVolumePreferenceController.class));
//暂时研究这两个,2选一,正常来讲只会显示其中一个
volumeControllers.add(use(RingVolumePreferenceController.class));
volumeControllers.add(use(NotificationVolumePreferenceController.class));
volumeControllers.add(use(CallVolumePreferenceController.class));
use(HandsFreeProfileOutputPreferenceController.class).setCallback(listPreference ->
onPreferenceDataChanged(listPreference));
mHfpOutputControllerKey =
use(HandsFreeProfileOutputPreferenceController.class).getPreferenceKey();
for (VolumeSeekBarPreferenceController controller : volumeControllers) {
//回调见2.5
controller.setCallback(mVolumeCallback);
getSettingsLifecycle().addObserver(controller);
}
}
2.3.buildPreferenceControllers
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
SoundSettings fragment, Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
// Volumes are added via xml
// === Phone & notification ringtone ===
controllers.add(new PhoneRingtonePreferenceController(context));
controllers.add(new AlarmRingtonePreferenceController(context));
controllers.add(new NotificationRingtonePreferenceController(context));
// === Other Sound Settings ===
final DialPadTonePreferenceController dialPadTonePreferenceController =
new DialPadTonePreferenceController(context, fragment, lifecycle);
final ScreenLockSoundPreferenceController screenLockSoundPreferenceController =
new ScreenLockSoundPreferenceController(context, fragment, lifecycle);
final ChargingSoundPreferenceController chargingSoundPreferenceController =
new ChargingSoundPreferenceController(context, fragment, lifecycle);
final DockingSoundPreferenceController dockingSoundPreferenceController =
new DockingSoundPreferenceController(context, fragment, lifecycle);
final TouchSoundPreferenceController touchSoundPreferenceController =
new TouchSoundPreferenceController(context, fragment, lifecycle);
final DockAudioMediaPreferenceController dockAudioMediaPreferenceController =
new DockAudioMediaPreferenceController(context, fragment, lifecycle);
final BootSoundPreferenceController bootSoundPreferenceController =
new BootSoundPreferenceController(context);
final EmergencyTonePreferenceController emergencyTonePreferenceController =
new EmergencyTonePreferenceController(context, fragment, lifecycle);
final VibrateIconPreferenceController vibrateIconPreferenceController =
new VibrateIconPreferenceController(context, fragment, lifecycle);
controllers.add(dialPadTonePreferenceController);
controllers.add(screenLockSoundPreferenceController);
controllers.add(chargingSoundPreferenceController);
controllers.add(dockingSoundPreferenceController);
controllers.add(touchSoundPreferenceController);
controllers.add(vibrateIconPreferenceController);
controllers.add(dockAudioMediaPreferenceController);
controllers.add(bootSoundPreferenceController);
controllers.add(emergencyTonePreferenceController);
controllers.add(new PreferenceCategoryController(context,
"other_sounds_and_vibrations_category").setChildren(
Arrays.asList(dialPadTonePreferenceController,
screenLockSoundPreferenceController,
chargingSoundPreferenceController,
dockingSoundPreferenceController,
touchSoundPreferenceController,
vibrateIconPreferenceController,
dockAudioMediaPreferenceController,
bootSoundPreferenceController,
emergencyTonePreferenceController)));
return controllers;
}
2.4.onPreferenceTreeClick
RingtonePreference的点击事件单独处理,点击弹框选择铃声
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof RingtonePreference) {
writePreferenceClickMetric(preference);
mRequestPreference = (RingtonePreference) preference;
mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent());
getActivity().startActivityForResultAsUser(
mRequestPreference.getIntent(),
REQUEST_CODE,
null,
UserHandle.of(mRequestPreference.getUserId()));
return true;
}
return super.onPreferenceTreeClick(preference);
}
2.5.mVolumeCallback
主要作用就是延迟2秒停止播放示例声音
final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback();
3.RingVolumePreferenceController
public class RingVolumePreferenceController extends VolumeSeekBarPreferenceController {
>getAudioStream
public int getAudioStream() {
return AudioManager.STREAM_RING; //2
}
3.1.构造方法
public RingVolumePreferenceController(Context context, String key) {
super(context, key);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
if (mVibrator != null && !mVibrator.hasVibrator()) {
mVibrator = null;
}
mRingAliasNotif = isRingAliasNotification();
if (mRingAliasNotif) {
mTitleId = R.string.ring_volume_option_title;
mNormalIconId = R.drawable.ic_notifications;
mSilentIconId = R.drawable.ic_notifications_off_24dp;
} else {
mTitleId = R.string.separate_ring_volume_option_title;
mNormalIconId = R.drawable.ic_ring_volume;
mSilentIconId = R.drawable.ic_ring_volume_off;
}
// todo: set a distinct vibrate icon for ring vs notification
mVibrateIconId = R.drawable.ic_volume_ringer_vibrate;
updateRingerMode();
}
>isRingAliasNotification
通知和铃声是否合并统一控制
boolean isRingAliasNotification() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
}
<!-- Flag indicating whether notification and ringtone volumes
are controlled together (aliasing is true) or not. -->
<bool name="config_alias_ring_notif_stream_types">true</bool>
>updateRingerMode
void updateRingerMode() {
final int ringerMode = mHelper.getRingerModeInternal();
if (mRingerMode == ringerMode) return;
mRingerMode = ringerMode;
updatePreferenceIcon();
}
有3种模式,对应不同的图标
>updatePreferenceIcon
private void updatePreferenceIcon() {
if (mPreference != null) {
if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
mPreference.showIcon(mNormalIconId);
} else {
if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE && mVibrator != null) {
mMuteIcon = mVibrateIconId;
} else {
mMuteIcon = mSilentIconId;
}
mPreference.showIcon(mMuteIcon);
}
}
}
> RINGER_MODE
/**
* Ringer mode that will be silent and will not vibrate. (This overrides the
* vibrate setting.)
*/
public static final int RINGER_MODE_SILENT = 0;
/**
* Ringer mode that will be silent and will vibrate. (This will cause the
* phone ringer to always vibrate, but the notification vibrate to only
* vibrate if set.)
*/
public static final int RINGER_MODE_VIBRATE = 1;
/**
* Ringer mode that may be audible and may vibrate. It will be audible if
* the volume before changing out of this mode was audible. It will vibrate
* if the vibrate setting is on.
*/
public static final int RINGER_MODE_NORMAL = 2;
3.2.getAvailabilityStatus
返回是否可用的状态
public int getAvailabilityStatus() {
return Utils.isVoiceCapable(mContext) && !mHelper.isSingleVolume()
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
>isAvailable
基类BasePreferenceController.java里用上述的状态判断此选项是否可用
public final boolean isAvailable() {
if (mIsForWork && mWorkProfileUser == null) {
return false;
}
final int availabilityStatus = getAvailabilityStatus();
return (availabilityStatus == AVAILABLE
|| availabilityStatus == AVAILABLE_UNSEARCHABLE
|| availabilityStatus == DISABLED_DEPENDENT_SETTING);
}
>isVoiceCapable
是否有语音功能,或者说是否是手机
/**
* Returns whether the device is voice-capable (meaning, it is also a phone).
*/
public static boolean isVoiceCapable(Context context) {
final TelephonyManager telephony =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//读取的是配置
return telephony != null && telephony.isVoiceCapable();
}
3.3.RingReceiver
onResume和onPause里注册,取消注册,监听下边2个广播,mode改变更新图标
private class RingReceiver extends BroadcastReceiver {
private boolean mRegistered;
public void register(boolean register) {
if (mRegistered == register) return;
if (register) {
final IntentFilter filter = new IntentFilter();
filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
mContext.registerReceiver(this, filter);
} else {
mContext.unregisterReceiver(this);
}
mRegistered = register;
}
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(H.UPDATE_EFFECTS_SUPPRESSOR);
} else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
mHandler.sendEmptyMessage(H.UPDATE_RINGER_MODE);
}
}
}
>handleMessage
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_EFFECTS_SUPPRESSOR:
updateEffectsSuppressor();
break;
case UPDATE_RINGER_MODE:
updateRingerMode();
break;
}
}
4.NotificationVolumePreferenceController
public class NotificationVolumePreferenceController extends VolumeSeekBarPreferenceController {
private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
>getAudioStream
public int getAudioStream() {
return AudioManager.STREAM_NOTIFICATION; //5
}
4.1.构造方法
public NotificationVolumePreferenceController(Context context, String key) {
super(context, key);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
if (mVibrator != null && !mVibrator.hasVibrator()) {
mVibrator = null;
}
//是否铃声和通知合并使用
mRingNotificationAliased = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
updateRingerMode();
}
>updateRingerMode
更新铃声模式
private void updateRingerMode() {
final int ringerMode = mHelper.getRingerModeInternal();
if (mRingerMode == ringerMode) return;
mRingerMode = ringerMode;
updatePreferenceIconAndSliderState();
}
4.2.getAvailabilityStatus
可用的条件如下:
- config_show_notification_volume配置为true并且非单声音控制
- isVoiceCapable为false或者mRingNotificationAliased为false
public int getAvailabilityStatus() {
// Show separate notification slider if ring/notification are not aliased by AudioManager --
// if they are, notification volume is controlled by RingVolumePreferenceController.
return mContext.getResources().getBoolean(R.bool.config_show_notification_volume)
&& (!mRingNotificationAliased || !Utils.isVoiceCapable(mContext))
&& !mHelper.isSingleVolume()
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
5.VolumeSeekBarPreferenceController
这个是小节3和4的父类
public abstract class VolumeSeekBarPreferenceController extends
AdjustVolumeRestrictedPreferenceController implements LifecycleObserver {
public VolumeSeekBarPreferenceController(Context context, String key) {
super(context, key);
setAudioHelper(new AudioHelper(context));
}
5.1.displayPreference
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
if (isAvailable()) {
mPreference = screen.findPreference(getPreferenceKey());
//mVolumePreferenceCallback是2.5里设置的
mPreference.setCallback(mVolumePreferenceCallback);
//见7.3,
mPreference.setStream(getAudioStream());
mPreference.setMuteIcon(getMuteIcon());
}
}
6.AudioHelper.java
public AudioHelper(Context context) {
mContext = context;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
6.1.isSingleVolume
//是否是单一的音频类型,电视是true,其他的一般都是false
public boolean isSingleVolume() {
return AudioSystem.isSingleVolume(mContext);
}
6.2.get setStreamVolume
获取设置音量,最终实现在小节9里
public int getStreamVolume(int stream) {
return mAudioManager.getStreamVolume(stream);//见9.6
}
public boolean setStreamVolume(int stream, int volume) {
mAudioManager.setStreamVolume(stream, volume, 0);//见9.2
return true;
}
6.3.getMaxVolume getMinVolume
音量的最大最小值设置,获取
public int getMaxVolume(int stream) {
return mAudioManager.getStreamMaxVolume(stream);
}
public int getMinVolume(int stream) {
int minVolume;
try {
minVolume = mAudioManager.getStreamMinVolume(stream);
} catch (IllegalArgumentException e) {
minVolume = mAudioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL);
}
return minVolume;
}
6.4.getRingerModeInternal
public int getRingerModeInternal() {
return mAudioManager.getRingerModeInternal();
}
7.VolumeSeekBarPreference.java
音量调节选项用的都是这个自定义的preference
public class VolumeSeekBarPreference extends SeekBarPreference {
7.1.构造方法
public VolumeSeekBarPreference(Context context) {
super(context);
setLayoutResource(R.layout.preference_volume_slider);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
7.2.onBindViewHolder
绑定的时候会进行初始化操作
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
init();
}
>1.init
protected void init() {
if (mSeekBar == null) return;
final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() {
@Override
public void onSampleStarting(SeekBarVolumizer sbv) {
if (mCallback != null) {
mCallback.onSampleStarting(sbv);
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (mCallback != null) {
mCallback.onStreamValueChanged(mStream, progress);
}
}
@Override
public void onMuted(boolean muted, boolean zenMuted) {
if (mMuted == muted && mZenMuted == zenMuted) return;
mMuted = muted;
mZenMuted = zenMuted;
updateIconView();
}
@Override
public void onStartTrackingTouch(SeekBarVolumizer sbv) {
if (mCallback != null) {
mCallback.onStartTrackingTouch(sbv);
}
mJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
.withView(CUJ_SETTINGS_SLIDER, mSeekBar)
.setTag(getKey()));
}
@Override
public void onStopTrackingTouch(SeekBarVolumizer sbv) {
mJankMonitor.end(CUJ_SETTINGS_SLIDER);
}
};
final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
//封装的工具类,监听seekbar的拖动,通过audioManager修改音量,同时调用callback的对应方法
if (mVolumizer == null) {
mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
}
mVolumizer.start();
mVolumizer.setSeekBar(mSeekBar);
updateIconView();
updateSuppressionText();
if (!isEnabled()) {
mSeekBar.setEnabled(false);
mVolumizer.stop();
}
}
7.3.setStream
根据stream类型设置对应的音量最大最小值以及当前音量
public void setStream(int stream) {
mStream = stream;
setMax(mAudioManager.getStreamMaxVolume(mStream));
// Use getStreamMinVolumeInt for non-public stream type
// eg: AudioManager.STREAM_BLUETOOTH_SCO
setMin(mAudioManager.getStreamMinVolumeInt(mStream));
setProgress(mAudioManager.getStreamVolume(mStream));
}
8.SeekBarVolumizer.java
这个就是工具类,根据传入的音频类型,获取默认值,设置给seekbar,并监听seekbar的拖动,修改对应类型的音量大小
8.1.构造方法
public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) {
this(context, streamType, defaultUri, callback, true /* playSample */);
}
public SeekBarVolumizer(
Context context,
int streamType,
Uri defaultUri,
Callback callback,
boolean playSample) {
mContext = context;
mAudioManager = context.getSystemService(AudioManager.class);
mNotificationManager = context.getSystemService(NotificationManager.class);
mNotificationPolicy = mNotificationManager.getConsolidatedNotificationPolicy();
mAllowAlarms = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_ALARMS) != 0;
mAllowMedia = (mNotificationPolicy.priorityCategories & NotificationManager.Policy
.PRIORITY_CATEGORY_MEDIA) != 0;
mAllowRinger = !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
mNotificationPolicy);
//根据stream类型获取对应的各种值
mStreamType = streamType;
mAffectedByRingerMode = mAudioManager.isStreamAffectedByRingerMode(mStreamType);
mNotificationOrRing = isNotificationOrRing(mStreamType);
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
}
mNotifAliasRing = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
mZenMode = mNotificationManager.getZenMode();
if (hasAudioProductStrategies()) {
mVolumeGroupId = getVolumeGroupIdForLegacyStreamType(mStreamType);
mAttributes = getAudioAttributesForLegacyStreamType(
mStreamType);
}
mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType);
mCallback = callback;
//初始的音量大小,见9.6
mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType);
mLastAudibleStreamVolume = mAudioManager.getLastAudibleStreamVolume(mStreamType);
mMuted = mAudioManager.isStreamMute(mStreamType);
mPlaySample = playSample;
if (mCallback != null) {
mCallback.onMuted(mMuted, isZenMuted());
}
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;
}
8.2.setSeekBar
public void setSeekBar(SeekBar seekBar) {
if (mSeekBar != null) {
mSeekBar.setOnSeekBarChangeListener(null);
}
mSeekBar = seekBar;
mSeekBar.setOnSeekBarChangeListener(null);
mSeekBar.setMax(mMaxStreamVolume);
//更新seekbar的位置
updateSeekBar();
mSeekBar.setOnSeekBarChangeListener(this);
}
8.3.SeekBarChangeListener
public void onStartTrackingTouch(SeekBar seekBar) {
if (mCallback != null) {
mCallback.onStartTrackingTouch(this);
}
}
public void onStopTrackingTouch(SeekBar seekBar) {
postStartSample();
if (mCallback != null) {
mCallback.onStopTrackingTouch(this);
}
}
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (fromTouch) {
postSetVolume(progress);
}
if (mCallback != null) {
mCallback.onProgressChanged(seekBar, progress, fromTouch);
}
}
>1.postSetVolume
修改音量大小,交给handler处理
private void postSetVolume(int progress) {
if (mHandler == null) return;
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
mHandler.removeMessages(MSG_START_SAMPLE);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME),
isDelay() ? SET_STREAM_VOLUME_DELAY_MS : 0);
}
>2.MSG_SET_STREAM_VOLUME
具体逻辑在这里
case MSG_SET_STREAM_VOLUME:
if (mMuted && mLastProgress > 0) {
//之前是静音模式,现在音量大于0
mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_UNMUTE, 0);
} else if (!mMuted && mLastProgress == 0) {
//之前是非静音模式,现在音量为0
mAudioManager.adjustStreamVolume(mStreamType, AudioManager.ADJUST_MUTE, 0);
}
//保存对应类型stream的音量,具体见9.1
mAudioManager.setStreamVolume(mStreamType, mLastProgress,
AudioManager.FLAG_SHOW_UI_WARNINGS);
break;
9.AudioService.java
mAudioManager里边最终调用的是AudioService的方法
9.1.setStreamVolumeWithAttributionInt
这个里边主要是权限检查,
/**
* Internal method for a stream type volume change. Can be used to change the volume on a
* given device only
* @param streamType the stream type whose volume is to be changed
* @param index the volume index
* @param flags options for volume handling
* @param device null when controlling volume for the current routing, otherwise the device
* for which volume is being changed
* @param callingPackage client side-provided package name of caller, not to be trusted
* @param attributionTag client side-provided attribution name, not to be trusted
*/
protected void setStreamVolumeWithAttributionInt(int streamType, int index, int flags,
@Nullable AudioDeviceAttributes device,
String callingPackage, String attributionTag) {
//无障碍功能类型,没有对应的修改权限
if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
+ " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
//语音通话功能,音量为0,没有对应的修改手机状态权限
if ((streamType == AudioManager.STREAM_VOICE_CALL) && (index == 0)
&& (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+ " MODIFY_PHONE_STATE callingPackage=" + callingPackage);
return;
}
//语音助手类型,没有对应的权限
if ((streamType == AudioManager.STREAM_ASSISTANT)
&& (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "Trying to call setStreamVolume() for STREAM_ASSISTANT without"
+ " MODIFY_AUDIO_ROUTING callingPackage=" + callingPackage);
return;
}
//见9.2
setStreamVolume(streamType, index, flags, device,
callingPackage, callingPackage, attributionTag,
Binder.getCallingUid(), callingOrSelfHasAudioSettingsPermission());
}
9.2.setStreamVolume
private void setStreamVolume(int streamType, int index, int flags,
@Nullable AudioDeviceAttributes ada,
String callingPackage, String caller, String attributionTag, int uid,
boolean hasModifyAudioSettings) {
if (mUseFixedVolume) {//补充1,默认为false
return;
}
ensureValidStreamType(streamType);
//数组数据见9.3,就是把一种音频类型指向另外一种,大家用一个
int streamTypeAlias = mStreamVolumeAlias[streamType];
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = (ada == null)
? getDeviceForStream(streamType)
: ada.getInternalType();
int oldIndex;
// skip a2dp absolute volume control request when the device
// is neither an a2dp device nor BLE device
if ((!AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& !AudioSystem.DEVICE_OUT_ALL_BLE_SET.contains(device))
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) {
return;
}
// If we are being called by the system (e.g. hardware keys) check for current user
// so we handle user restrictions correctly.
if (uid == android.os.Process.SYSTEM_UID) {
uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
}
if (!checkNoteAppOp(
STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage, attributionTag)) {
return;
}
if (isAndroidNPlus(callingPackage)
&& wouldToggleZenMode(getNewRingerMode(streamTypeAlias, index, flags))
&& !mNm.isNotificationPolicyAccessGrantedForPackage(callingPackage)) {
throw new SecurityException("Not allowed to change Do Not Disturb state");
}
if (!volumeAdjustmentAllowedByDnd(streamTypeAlias, flags)) {
return;
}
synchronized (mSafeMediaVolumeStateLock) {
// reset any pending volume command
mPendingVolumeCommand = null;
oldIndex = streamState.getIndex(device);
//就是把原本音频类型的音量大小转化为别名音频对应的音量大小,按照音频范围百分比转换
index = rescaleIndex(index * 10, streamType, streamTypeAlias);
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
&& AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
} else if (isAbsoluteVolumeDevice(device)
&& ((flags & AudioManager.FLAG_ABSOLUTE_VOLUME) == 0)) {
AbsoluteVolumeDeviceInfo info = mAbsoluteVolumeDeviceInfoMap.get(device);
dispatchAbsoluteVolumeChanged(streamType, info, index);
}
if (AudioSystem.isLeAudioDeviceType(device)
&& streamType == getBluetoothContextualVolumeStream()
&& (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
//蓝牙
mDeviceBroker.postSetLeAudioVolumeIndex(index,
mStreamStates[streamType].getMaxIndex(), streamType);
}
if (device == AudioSystem.DEVICE_OUT_HEARING_AID
&& streamType == getBluetoothContextualVolumeStream()) {
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
}
flags &= ~AudioManager.FLAG_FIXED_VOLUME;
if (streamTypeAlias == AudioSystem.STREAM_MUSIC && isFixedVolumeDevice(device)) {
flags |= AudioManager.FLAG_FIXED_VOLUME;
// volume is either 0 or max allowed for fixed volume devices
if (index != 0) {
if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
mSafeMediaVolumeDevices.contains(device)) {
index = safeMediaVolumeIndex(device);
} else {
index = streamState.getMaxIndex();
}
}
}
if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
mVolumeController.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
} else {
//这里是修改音量的逻辑,主要就是修改对应的VolumeStreamState对象里的数据
onSetStreamVolume(streamType, index, flags, device, caller, hasModifyAudioSettings);
index = mStreamStates[streamType].getIndex(device);
}
}
synchronized (mHdmiClientLock) {
if (streamTypeAlias == AudioSystem.STREAM_MUSIC
&& (oldIndex != index)) {
maybeSendSystemAudioStatusCommand(false);
}
}
sendVolumeUpdate(streamType, oldIndex, index, flags, device);
}
>1.mUseFixedVolume
是否禁止用户修改音量,默认是false
<!-- Flag indicating that the media framework should not allow changes or mute on any
stream or global volumes. -->
<bool name="config_useFixedVolume">false</bool>
>2.onSetStreamVolume
private void onSetStreamVolume(int streamType, int index, int flags, int device,
String caller, boolean hasModifyAudioSettings) {
final int stream = mStreamVolumeAlias[streamType];
//修改对应的音量大小,最终会通过handler操作,存储在settings
setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
// setting volume on ui sounds stream type also controls silent mode
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(stream == getUiSoundsStreamType())) {
//ringMode的获取见9.4
setRingerMode(getNewRingerMode(stream, index, flags),
TAG + ".onSetStreamVolume", false /*external*/);
}
// setting non-zero volume for a muted stream unmutes the stream and vice versa,
// except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
//index就是音量大小,为0表示静音
mStreamStates[stream].mute(index == 0);
}
}
>3.setStreamVolumeInt
private void setStreamVolumeInt(int streamType,
int index,
int device,
boolean force,
String caller, boolean hasModifyAudioSettings) {
if (isFullVolumeDevice(device)) {
return;
}
VolumeStreamState streamState = mStreamStates[streamType];
if (streamState.setIndex(index, device, caller, hasModifyAudioSettings) || force) {
// Post message to set system volume (it in turn will post a message
// to persist).
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,//这个case里又会调用MSG_PERSIST_VOLUME,这里会存储到settings里
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
9.3.updateStreamVolumeAlias
构造方法里以及其他几处调用,更新各种音频流类型音量的别名
private void updateStreamVolumeAlias(boolean updateVolumes, String caller) {
int dtmfStreamAlias;
final int a11yStreamAlias = sIndependentA11yVolume ?
AudioSystem.STREAM_ACCESSIBILITY : AudioSystem.STREAM_MUSIC;
final int assistantStreamAlias = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useAssistantVolume) ?
AudioSystem.STREAM_ASSISTANT : AudioSystem.STREAM_MUSIC;
if (mIsSingleVolume) {
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_TELEVISION;
dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
} else if (mUseVolumeGroupAliases) {
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NONE;
dtmfStreamAlias = AudioSystem.STREAM_DTMF;
} else {
//正常情况走这里
switch (mPlatformType) {
case AudioSystem.PLATFORM_VOICE:
//带语音功能的走这里
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_VOICE;//数据见补充2
dtmfStreamAlias = AudioSystem.STREAM_RING;
break;
default://不带语音功能的走这里
mStreamVolumeAlias = STREAM_VOLUME_ALIAS_DEFAULT;//数据见补充3
dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
}
if (!mNotifAliasRing) {
//默认不走这里,铃声和通知默认是统一处理的,走到这里说明是分开处理的,那把通知的别名改回自己的
mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] =
AudioSystem.STREAM_NOTIFICATION;
}
}
if (mIsSingleVolume) {
mRingerModeAffectedStreams = 0;
} else {
if (isInCommunication()) {
dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_DTMF);
} else {
mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_DTMF);
}
}
//这里修改下边3种音频类型的别名为dtmfStreamAlias的值
mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
mStreamVolumeAlias[AudioSystem.STREAM_ASSISTANT] = assistantStreamAlias;
//下边是更新相关流状态的逻辑,暂时不看
if (updateVolumes && mStreamStates != null) {
updateDefaultVolumes();
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
mStreamStates[AudioSystem.STREAM_DTMF]
.setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
System.VOLUME_SETTINGS_INT[a11yStreamAlias];
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
mStreamStates[a11yStreamAlias], caller);
}
}
if (sIndependentA11yVolume) {
// restore the a11y values from the settings
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
}
// apply stream mute states according to new value of mRingerModeAffectedStreams
setRingerModeInt(getRingerModeInternal(), false);
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
SENDMSG_QUEUE,
0,
0,
mStreamStates[AudioSystem.STREAM_DTMF], 0);
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
SENDMSG_QUEUE,
0,
0,
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY], 0);
}
}
>1.变量
// indicates whether the system maps all streams to a single stream.
private final boolean mIsSingleVolume;//见10.1
private final boolean mUseFixedVolume;
private final boolean mUseVolumeGroupAliases;
//默认false
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
/**
* indicates whether STREAM_NOTIFICATION is aliased to STREAM_RING
* not final due to test method, see {@link #setNotifAliasRingForTest(boolean)}.
*/
private boolean mNotifAliasRing;
//默认是true
mNotifAliasRing = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_alias_ring_notif_stream_types);
前别贴过,就是铃声和通知是否用一个音量控制
<!-- Flag indicating whether notification and ringtone volumes
are controlled together (aliasing is true) or not. -->
<bool name="config_alias_ring_notif_stream_types">true</bool>
>2.STREAM_VOLUME_ALIAS_VOICE
带语音功能的别名是这个数组,看下注解有解释为啥用别名
/* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
* of another stream: This avoids multiplying the volume settings for hidden
* stream types that follow other stream behavior for volume settings
* NOTE: do not create loops in aliases!
* Some streams alias to different streams according to device category (phone or tablet) or
* use case (in call vs off call...). See updateStreamVolumeAlias() for more details.
* mStreamVolumeAlias contains STREAM_VOLUME_ALIAS_VOICE aliases for a voice capable device
* (phone), STREAM_VOLUME_ALIAS_TELEVISION for a television or set-top box and
* STREAM_VOLUME_ALIAS_DEFAULT for other devices (e.g. tablets).*/
private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
>3.STREAM_VOLUME_ALIAS_DEFAULT
不带语音功能的别名数组
private final int[] STREAM_VOLUME_ALIAS_DEFAULT = new int[] {
AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
AudioSystem.STREAM_RING, // STREAM_SYSTEM
AudioSystem.STREAM_RING, // STREAM_RING
AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
AudioSystem.STREAM_ALARM, // STREAM_ALARM
AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
AudioSystem.STREAM_RING, // STREAM_DTMF
AudioSystem.STREAM_MUSIC, // STREAM_TTS
AudioSystem.STREAM_MUSIC, // STREAM_ACCESSIBILITY
AudioSystem.STREAM_MUSIC // STREAM_ASSISTANT
};
>4.MIN_STREAM_VOLUME
protected static int[] MIN_STREAM_VOLUME = new int[] {
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
0, // STREAM_MUSIC
1, // STREAM_ALARM
0, // STREAM_NOTIFICATION
0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0, // STREAM_TTS
1, // STREAM_ACCESSIBILITY
0 // STREAM_ASSISTANT
};
>5.MAX_STREAM_VOLUME
protected static int[] MAX_STREAM_VOLUME = new int[] {
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING // configured by config_audio_ring_vol_steps
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION // configured by config_audio_notif_vol_steps
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
15, // STREAM_ACCESSIBILITY
15 // STREAM_ASSISTANT
};
9.4.getNewRingerMode
private int getNewRingerMode(int stream, int index, int flags) {
// setRingerMode does nothing if the device is single volume,so the value would be unchanged
if (mIsSingleVolume) {
return getRingerModeExternal();
}
// setting volume on ui sounds stream type also controls silent mode
if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
(stream == getUiSoundsStreamType())) {
//铃声模式
int newRingerMode;
if (index == 0) {
//音量是0,先判断是否可以震动,可以的话就是震动模式,不可以继续判断是否
//音量减少可以进入安静模式,是的话返回silent模式,其他的返回normal模式。
newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
: mVolumePolicy.volumeDownToEnterSilent ? AudioManager.RINGER_MODE_SILENT
: AudioManager.RINGER_MODE_NORMAL;
} else {
//音量非0,normal模式
newRingerMode = AudioManager.RINGER_MODE_NORMAL;
}
return newRingerMode;
}
//不是ring类型的,返回ring的当前类型
return getRingerModeExternal();
}
9.5.createStreamStates
private void createStreamStates() {
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
//对应的第一个参数名字用的是别名对应的音频名字
streams[i] =
//构造方法里会读取对应的音量,见9.7
new VolumeStreamState(System.VOLUME_SETTINGS_INT[mStreamVolumeAlias[i]], i);
}
checkAllFixedVolumeDevices();
checkAllAliasStreamVolumes();
checkMuteAffectedStreams();
updateDefaultVolumes();
}
>1.checkAllAliasStreamVolumes
private void checkAllAliasStreamVolumes() {
synchronized (mSettingsLock) {
synchronized (VolumeStreamState.class) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
//把数据执行别名对应的数据
mStreamStates[streamType]
.setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
// apply stream volume
if (!mStreamStates[streamType].mIsMuted) {
//更新state里的数据
mStreamStates[streamType].applyAllVolumes();
}
}
}
}
}
9.6.getStreamVolume
public int getStreamVolume(int streamType) {
ensureValidStreamType(streamType);
//获取的也是对应别名的
int device = getDeviceForStream(streamType);
synchronized (VolumeStreamState.class) {
int index = mStreamStates[streamType].getIndex(device);
// by convention getStreamVolume() returns 0 when a stream is muted.
if (mStreamStates[streamType].mIsMuted) {
index = 0;
}
if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
isFixedVolumeDevice(device)) {
index = mStreamStates[streamType].getMaxIndex();
}
return (index + 5) / 10;
}
}
9.7.VolumeStreamState
小节9.5会调用
>1.构造方法
//数据在这个集合里,存储对应类型的index也就是音量大小值,自定义了put方法,保存数据是native方法
private final SparseIntArray mIndexMap = new SparseIntArray(8) {
//..
private VolumeStreamState(String settingName, int streamType) {
mVolumeIndexSettingName = settingName;
mStreamType = streamType;
//数组见9.3.4
mIndexMin = MIN_STREAM_VOLUME[streamType] * 10;
mIndexMinNoPerm = mIndexMin; // may be overwritten later in updateNoPermMinIndex()
//数组见9.3.5
mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
//初始化对应类型的音频,native方法
final int status = AudioSystem.initStreamVolume(
streamType, mIndexMin / 10, mIndexMax / 10);
if (status != AudioSystem.AUDIO_STATUS_OK) {
//
}
//读取保存的值
readSettings();
mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
mStreamDevicesChanged = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
mStreamDevicesChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
}
>2.readSettings
synchronized (VolumeStreamState.class) {
//循环所有的输出装置集合
for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
// retrieve current volume for device
// if no volume stored for current stream and device, use default volume if default
// device, continue otherwise
//如果是默认的输出装置,那默认值用默认的配置里的,否则默认值是-1
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioSystem.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
int index;
if (!hasValidSettingsName()) {
index = defaultIndex;
} else {
//获取key值,读取settings里存储的值
String name = getSettingNameForDevice(device);
index = mSettings.getSystemIntForUser(
mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
}
if (index == -1) {
continue;
}
//更新map数据
mIndexMap.put(device, getValidIndex(10 * index,
true /*hasModifyAudioSettings*/));
}
}
>3.getSettingNameForDevice
举例:铃声对应的mVolumeIndexSettingName是volume_ring,后边拼上对应的device name
- volume_ring_0x40000000 ,默认的输出装置
- volume_ring_earpiece ,耳机输出装置
public @Nullable String getSettingNameForDevice(int device) {
if (!hasValidSettingsName()) {
//mVolumeIndexSettingName为null,构造方法里传递的,正常不为null
return null;
}
//device转换成对应的string
final String suffix = AudioSystem.getOutputDeviceName(device);
if (suffix.isEmpty()) {
return mVolumeIndexSettingName是
}
//拼接在一起
return mVolumeIndexSettingName + "_" + suffix;
}
9.8.获取最小最大音量
数据来源见9.7构造方法
public int getStreamMaxVolume(int streamType) {
ensureValidStreamType(streamType);
return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
}
public int getStreamMinVolume(int streamType) {
ensureValidStreamType(streamType);
final boolean isPrivileged =
Binder.getCallingUid() == Process.SYSTEM_UID
|| callingHasAudioSettingsPermission()
|| (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
== PackageManager.PERMISSION_GRANTED);
return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
}
10.AudioSystem.java
10.1.isSingleVolume
电视是一个音量控制的,或者配置里设置为强制一个音量控制
public static boolean isSingleVolume(Context context) {
boolean forceSingleVolume = context.getResources().getBoolean(
com.android.internal.R.bool.config_single_volume);
return getPlatformType(context) == PLATFORM_TELEVISION || forceSingleVolume;
}
10.2.getPlatformType
3种类型
public static int getPlatformType(Context context) {
if (((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE))
.isVoiceCapable()) {
//是否有语音功能,也是可配置的
return PLATFORM_VOICE;
} else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
return PLATFORM_TELEVISION;
} else {
return PLATFORM_DEFAULT;
}
}
10.3.数据
>1.DEFAULT_STREAM_VOLUME
默认输出装置的默认音量值
public static int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
5, // STREAM_RING // configured in AudioService by config_audio_notif_vol_default
5, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION // configured in AudioService by config_audio_ring_vol_default
7, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
5, // STREAM_DTMF
5, // STREAM_TTS
5, // STREAM_ACCESSIBILITY
5, // STREAM_ASSISTANT
};
>2.STREAM_NAMES
public static final String[] STREAM_NAMES = new String[] {
"STREAM_VOICE_CALL",
"STREAM_SYSTEM",
"STREAM_RING",
"STREAM_MUSIC",
"STREAM_ALARM",
"STREAM_NOTIFICATION",
"STREAM_BLUETOOTH_SCO",
"STREAM_SYSTEM_ENFORCED",
"STREAM_DTMF",
"STREAM_TTS",
"STREAM_ACCESSIBILITY",
"STREAM_ASSISTANT"
};
>3.常量
对应补充2里的索引
public static final int STREAM_DEFAULT = -1;
/** @hide Used to identify the volume of audio streams for phone calls */
public static final int STREAM_VOICE_CALL = 0;
/** @hide Used to identify the volume of audio streams for system sounds */
public static final int STREAM_SYSTEM = 1;
/** @hide Used to identify the volume of audio streams for the phone ring and message alerts */
public static final int STREAM_RING = 2;
/** @hide Used to identify the volume of audio streams for music playback */
public static final int STREAM_MUSIC = 3;
/** @hide Used to identify the volume of audio streams for alarms */
public static final int STREAM_ALARM = 4;
/** @hide Used to identify the volume of audio streams for notifications */
public static final int STREAM_NOTIFICATION = 5;
/** @hide
* Used to identify the volume of audio streams for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
/** @hide Used to identify the volume of audio streams for enforced system sounds in certain
* countries (e.g camera in Japan) */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static final int STREAM_SYSTEM_ENFORCED = 7;
/** @hide Used to identify the volume of audio streams for DTMF tones */
public static final int STREAM_DTMF = 8;
/** @hide Used to identify the volume of audio streams exclusively transmitted through the
* speaker (TTS) of the device */
public static final int STREAM_TTS = 9;
/** @hide Used to identify the volume of audio streams for accessibility prompts */
public static final int STREAM_ACCESSIBILITY = 10;
/** @hide Used to identify the volume of audio streams for virtual assistant */
public static final int STREAM_ASSISTANT = 11;
10.4.getNumStreamTypes()
数据参考10.3.2
// Expose only the getter method publicly so we can change it in the future
private static final int NUM_STREAM_TYPES = 12;
public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; }
11.Settings.java
11.1.数据
>1.VOLUME_SETTINGS_INT
数据个数和10.3.2是一样的,不过有几个是空字符串(他们的音量设置不是永久的)
/**
* The mapping of stream type (integer) to its setting.
* Unlike the VOLUME_SETTINGS array, this one contains as many entries as
* AudioSystem.NUM_STREAM_TYPES, and has empty strings for stream types whose volumes
* are never persisted.
*/
public static final String[] VOLUME_SETTINGS_INT = {
VOLUME_VOICE, VOLUME_SYSTEM, VOLUME_RING, VOLUME_MUSIC,
VOLUME_ALARM, VOLUME_NOTIFICATION, VOLUME_BLUETOOTH_SCO,
"" /*STREAM_SYSTEM_ENFORCED, no setting for this stream*/,
"" /*STREAM_DTMF, no setting for this stream*/,
"" /*STREAM_TTS, no setting for this stream*/,
VOLUME_ACCESSIBILITY, VOLUME_ASSISTANT
};
12.总结
- 支持语音通话的显示的是ring¬ification volume ,不支持的显示的是notifaction volume
- ring和notification的streamType一个是2一个是5,不过最终用的都是2,具体见9.3的别名数组
- 音量大小的读取是存储在settings里的,至于key值见9.7里key值的组合