您现在的位置是:首页 > 文章详情

Android6.0 源码修改之Settings音量调节界面增加通话音量调节

日期:2019-04-23点击:494

Android6.0 源码修改之Settings音量调节界面增加通话音量调节
前言
今天客户提了个需求,因为我们的设备在正常情况下无法调节通话音量,只有在打电话过程中,按物理音量加减键才能出现调节通话音量seekBar,很不方便,于是乎需求就来了。需要优化两个地方

1、在正常情况下,按物理音量加减键都显示 通话音量调节seekBar,可方便快速调节通话音量
2、在Settings中提示音界面点击设置进入,增加通话音量调节seekBar
在这里插入图片描述
在这里插入图片描述

修改前

在这里插入图片描述
在这里插入图片描述

修改后

实现
第一个功能
先来完成第一个功能,还是通过Hierarchy View查看布局结构,查找到布局文件id为volume_dialog,通过在源码中搜索找到位于SystemUI中,volume_dialog.xml
源码位置 frameworksbasepackagesSystemUIreslayoutvolume_dialog.xml

对应的java类为 frameworksbasepackagesSystemUIsrccomandroidsystemuivolumeVolumeDialog.java

修改代码

addRow(AudioManager.STREAM_VOICE_CALL,

 R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, true);

原来的第四个参数为false,修改为true即可显示通话音量seekBar

为了便于说明,我们跟进addRow()中查看

private void addRow(int stream, int iconRes, int iconMuteRes, boolean important) {

final VolumeRow row = initRow(stream, iconRes, iconMuteRes, important); if (!mRows.isEmpty()) { final View v = new View(mContext); v.setId(android.R.id.background); final int h = mContext.getResources() .getDimensionPixelSize(R.dimen.volume_slider_interspacing); final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, h); mDialogContentView.addView(v, mDialogContentView.getChildCount() - 1, lp); row.space = v; } ...

}
传递的参数对应important,从字面意思理解重要对应显示,继续查看initRow都做了什么

private VolumeRow initRow(final int stream, int iconRes, int iconMuteRes, boolean important) {

final VolumeRow row = new VolumeRow(); row.stream = stream; row.iconRes = iconRes; row.iconMuteRes = iconMuteRes; row.important = important; row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null); row.view.setTag(row); row.header = (TextView) row.view.findViewById(R.id.volume_row_header); mSpTexts.add(row.header); row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider); row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row)); // forward events above the slider into the slider row.view.setOnTouchListener(new OnTouchListener() { private final Rect mSliderHitRect = new Rect(); private boolean mDragging; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { row.slider.getHitRect(mSliderHitRect); if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN && event.getY() < mSliderHitRect.top) { mDragging = true; } if (mDragging) { event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top); row.slider.dispatchTouchEvent(event); if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) { mDragging = false; } return true; } return false; } }); row.icon = (ImageButton) row.view.findViewById(R.id.volume_row_icon); row.icon.setImageResource(iconRes); row.icon.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, row.stream, row.iconState); mController.setActiveStream(row.stream); if (row.stream == AudioManager.STREAM_RING) { final boolean hasVibrator = mController.hasVibrator(); if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) { if (hasVibrator) { mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false); } else { final boolean wasZero = row.ss.level == 0; mController.setStreamVolume(stream, wasZero ? row.lastAudibleLevel : 0); } } else { mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false); if (row.ss.level == 0) { mController.setStreamVolume(stream, 1); } } } else { final boolean vmute = row.ss.level == 0; mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0); } row.userAttempt = 0; // reset the grace period, slider should update immediately } }); row.settingsButton = (ImageButton) row.view.findViewById(R.id.volume_settings_button); row.settingsButton.setOnClickListener(mClickSettings); return row;

}
从上面可看出,将一些变量都保存到了VolumeRow中,设置了icon的点击事件,将当前对应的音量类型设置为最低(禁音), 设置seekBar的改变事件。通过过滤日志,查找到控制音量类型的显示和隐藏的代码块updateRowsH()

private boolean isVisibleH(VolumeRow row, boolean isActive) {

return mExpanded && row.view.getVisibility() == View.VISIBLE || (mExpanded && (row.important || isActive)) || !mExpanded && isActive;

}

private void updateRowsH() {

if (D.BUG) Log.d(TAG, "updateRowsH"); final VolumeRow activeRow = getActiveRow(); updateFooterH(); updateExpandButtonH(); if (!mShowing) { trimObsoleteH(); } // apply changes to all rows for (VolumeRow row : mRows) { final boolean isActive = row == activeRow; final boolean visible = isVisibleH(row, isActive); Log.e(TAG, "row==" + row.stream + " isActive=="+isActive + " visible="+visible); Util.setVisOrGone(row.view, visible); Util.setVisOrGone(row.space, visible && mExpanded); final int expandButtonRes = mExpanded ? R.drawable.ic_volume_settings : 0; if (expandButtonRes != row.cachedExpandButtonRes) { row.cachedExpandButtonRes = expandButtonRes; if (expandButtonRes == 0) { row.settingsButton.setImageDrawable(null); } else { row.settingsButton.setImageResource(expandButtonRes); } } Util.setVisOrInvis(row.settingsButton, false); updateVolumeRowHeaderVisibleH(row); row.header.setAlpha(mExpanded && isActive ? 1 : 0.5f); updateVolumeRowSliderTintH(row, isActive); }

}
遍历已经添加的音量类型集合mRows,依次判断是否处于活动状态,再和开始设置的important属性比较。mExpanded是否展开,默认只显示铃声音量控制,点击下拉的按钮,才完全显示其它的音量控制

mExpanded && row.view.getVisibility() == View.VISIBLE || (mExpanded && (row.important || isActive)) || !mExpanded && isActive

true && false || (true && (true || false)) || false && true --->true

好了,至此分析完毕,重新mmm push SystemUI.apk 查看效果

第二个功能
源码位置

Settingsres_extxmledit_profile_prefs.xml
SettingssrccommediatekaudioprofileEditprofile.java
SettingssrccommediatekaudioprofileVolumeSeekBarPreference.java

在edit_profile_prefs.xml中仿照原来的Alarm volume和Ring volume,新增加一个Call volume


 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="call_volume" android:icon="@drawable/ic_volume_voice" android:title="@string/call_volume_option_title" />

对应的drawable文件时从SystemUI中拷贝过来的,ic_volume_voice.xml

android:height="24.0dp"
android:viewportHeight="48.0"
android:viewportWidth="48.0"
android:width="24.0dp" >

android:fillColor="#ff727272" android:pathData="M13.25,21.59c2.88,5.66 7.51,10.29 13.18,13.17l4.4,-4.41c0.55,-0.55 1.34,-0.71 2.03,-0.49C35.1,30.6 37.51,31.0 40.0,31.0c1.11,0.0 2.0,0.89 2.0,2.0l0.0,7.0c0.0,1.11 -0.89,2.0 -2.0,2.0C21.22,42.0 6.0,26.78 6.0,8.0c0.0,-1.1 0.9,-2.0 2.0,-2.0l7.0,0.0c1.11,0.0 2.0,0.89 2.0,2.0 0.0,2.4 0.4,4.9 1.14,7.1 0.2,0.6 0.06,1.48 -0.49,2.03l-4.4,4.42z" /> 


接下来对应到 Editprofile.java 文件中,可以看到 KEY_ALARM_VOLUME 对应的preference初始化,依旧照葫芦画瓢,添加 KEY_CALL_VOLUME

private void initVolume(PreferenceScreen parent) {

initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC); initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM); initVolumePreference(KEY_CALL_VOLUME, AudioManager.STREAM_VOICE_CALL); if (mVoiceCapable) { mVolume = initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING); parent.removePreference(parent.findPreference(KEY_NOTIFICATION_VOLUME)); } else { mVolume = initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION); parent.removePreference(parent.findPreference(KEY_RING_VOLUME)); }

}
重新编译,push替换后发现,UI倒是出来了,但是无法滑动,事情果然没那么简单,继续查看 initVolumePreference()

private VolumeSeekBarPreference initVolumePreference(String key, int stream) {

Log.d("@M_" + TAG, "Init volume preference, key = " + key + ",stream = " + stream); final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key); volumePref.setStream(stream); volumePref.setCallback(mVolumeCallback); volumePref.setProfile(mKey); return volumePref;

}
保存了当前的音量调节类型,设置seekBar回调事件,接下来看看回调处理了什么

private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback {

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); } } 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(); } } public void ringtoneChanged() { if (mCurrent != null) { mCurrent.ringtoneChanged(); } else { mVolume.getSeekBar().ringtoneChanged(); } }

};
当我们点击或者是滑动seekBar时,会根据当前设置的音量大小播放一段短暂的默认铃音,当铃音未播放完成时,再次点击将不进行播放。继续跟进 VolumeSeekBarPreference.java 中

@Override
protected void onBindView(View view) {

super.onBindView(view); if (mStream == 0) { Log.w(TAG, "No stream found, not binding volumizer "); return; } getPreferenceManager().registerOnActivityStopListener(this); final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); if (seekBar == mSeekBar) { return; } mSeekBar = seekBar; final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() { @Override public void onSampleStarting(SeekBarVolumizer sbv) { if (mCallback != null) { mCallback.onSampleStarting(sbv); } } }; final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null; if (mVolumizer == null) { mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc, mKey); } //mVolumizer.setProfile(mKey); mVolumizer.setSeekBar(mSeekBar);

}
mStream == 0, 直接return,会不会和这有关系呢,来看下各个音量调节类型对应的int值

/* Used to identify the volume of audio streams for phone calls /
public static final int STREAM_VOICE_CALL = 0;
/* Used to identify the volume of audio streams for the phone ring and message alerts /
public static final int STREAM_RING = 2;
/* Used to identify the volume of audio streams for music playback /
public static final int STREAM_MUSIC = 3;
/* Used to identify the volume of audio streams for alarms /
public static final int STREAM_ALARM = 4;
我们新增的 STREAM_VOICE_CALL对应的mStream正好为0,直接给return掉了,所以修改为 mStream < 0 即可,重新编译 push 发现成功了。

具体的音量调节逻辑在 packagesappsSettingssrccommediatekaudioprofileSeekBarVolumizer.java 中,感兴趣的可继续深究,肯定离不开调用 .setStreamVolume()方法,大概看了一眼,主要是onProgressChanged()回调,通过postSetVolume(progress)方法,发送MSG_SET_STREAM_VOLUME消息,最终调用saveVolume()
原文地址https://www.cnblogs.com/cczheng-666/p/10757358.html

原文链接:https://yq.aliyun.com/articles/699611
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章