Android产品下拉框一直只有亮度条没有音量控制条。 为了方便控制音量,普遍都是底部导航栏添加音量加减按钮,在Android10以后,大家普遍用上了手势导航,去掉底部导航栏。 目前需要再下拉框中可以直接控制音量。
文章目录
- 前言
- 需求及效果
- 基础必备
- 修改文件:
- 修改说明
- 去掉长按亮度条不隐藏QSPanel
- 新增音量进度条到QS面板,放到亮度进度条下方
- 几个相关类引入
- 简要对部分类的简单分析
- BrightnessSlider
- BrightnessSlider
- QSPanelController
- 创建mBrightnessSlider 变量的创建:
- setBrightnessView
- 实现方案
- 布局
- 构造控制器并绑定View
- 业务
- StatusBar
- StatusBarPhoneModule
- QSPanel QSPanelController
- 其它相关类
- 资源
前言
在Android12平台,QS 面板上,亮度控制条下面添加音量条,方便控制音量。
需求及效果
Android12 版本
- RK和MTK平台在下拉框QS面板中,亮度条的下方新增音量条控制器 亮度条长按不隐藏QS面板
- 亮度条长按不隐藏QS面板
基础必备
SystemUI 相关知识,务必做到基本流程了解、概念、架构、布局,方便实现基础功能和理解业务修改。
参考资料:截屏功能添加中的SystemUI基础描述
#修改说明
详细说明如下,参考修改文件和新增文件即可完成功能,实现需求
修改文件:
vendor/mediatek/proprietary/packages/apps/SystemUI/AndroidManifest.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/classifier/Classifier.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/qs/QSAnimator.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/qs/QSPanel.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/qs/QSPanelController.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
`
## 新增文件:
```java
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable-hdpi/icon_volume_test.png
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable-mdpi/icon_volume_test.png
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable-xhdpi/icon_volume_test.png
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable-xxhdpi/icon_volume_test.png
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable-xxxhdpi/icon_volume_test.png
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/ic_volume.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/volume_progress_drawable.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/drawable/volume_progress_full_drawable.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/res/layout/quick_settings_volume_dialog.xml
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/settings/volume/
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/util/ApplicationContextProvider.kt
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/util/ContextProvider.java
vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/util/SoundUtils.kt
修改说明
修改说明详细文档,方便理解
去掉长按亮度条不隐藏QSPanel
com.android.systemui.statusbar.policy.BrightnessMirrorController 亮度条镜控制器
com.android.systemui.settings.brightness.BrightnessSlider 类 SeekBar.OnSeekBarChangeListener回调方法中
屏蔽mirrorController 的相关回调控制
具体代码如下:
private final SeekBar.OnSeekBarChangeListener mSeekListener =
new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mListener != null) {
mListener.onChanged(mTracking, progress, false);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mTracking = true;
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), false);
}
/*if (mMirrorController != null) {
mMirrorController.showMirror();
mMirrorController.setLocationAndSize(mView);
}*/
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mTracking = false;
if (mListener != null) {
mListener.onChanged(mTracking, getValue(), true);
}
/*if (mMirrorController != null) {
mMirrorController.hideMirror();
}*/
}
};
新增音量进度条到QS面板,放到亮度进度条下方
实现方案,参考源码中亮度条实现的方式实现
几个相关类引入
com.android.systemui.settings.brightness 包下
BrightnessController.java :对外的控制器 BrightnessController implements ToggleSlider.Listener
BrightnessSlider.java : 本质上也是一个View,带了ViewController控制器,extends ViewController<BrightnessSliderView> implements ToggleSlider
ToggleSlider.java : 定义进度条控制器的接口,如:进度条变化回调 onChanged、setMax、getMax、getValue、setValue
BrightnessDialog.java :显示音量的Activity,里面加载的是Dialog,源码暂未使用
BrightnessSliderView.java :SeekBar的根布局文件,包裹ToggleSeekBar 的View,本质是一个FrameLayout 布局
ToggleSeekBar.java :SeekBar
简要对部分类的简单分析
BrightnessSlider
中的Factory类,来构造BrightnessSlider, 这里面加载了布局 quick_settings_brightness_dialog,
在上面已经描述 这个BrightnessSlider 本质就是一个带控制器ViewController的view
public static class Factory {
private final FalsingManager mFalsingManager;
@Inject
public Factory(FalsingManager falsingManager) {
mFalsingManager = falsingManager;
}
/**
* Creates the view hierarchy and controller
*
* @param context a {@link Context} to inflate the hierarchy
* @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated
* hierarchy will not be attached
*/
public BrightnessSlider create(Context context, @Nullable ViewGroup viewRoot) {
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
return new BrightnessSlider(root, mFalsingManager);
}
/** Get the layout to inflate based on what slider to use */
private int getLayout() {
return R.layout.quick_settings_brightness_dialog;
}
}
BrightnessSlider
类的Factory类,来构造BrightnessController
/** Factory for creating a {@link BrightnessController}. */
public static class Factory {
private final Context mContext;
private final BroadcastDispatcher mBroadcastDispatcher;
@Inject
public Factory(Context context, BroadcastDispatcher broadcastDispatcher) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
}
/** Create a {@link BrightnessController} */
public BrightnessController create(ToggleSlider toggleSlider) {
return new BrightnessController(mContext, toggleSlider, mBroadcastDispatcher);
}
}
重点关注构造方法中传递了一个ToggleSlider参数,如果传递ToggleSlider 实现类,那么就实现了控制器和view 的绑定。
控制器中可以通过传递过来的view 来控制view 的各种状态和设置内容。 追踪一下这块创建和添加view 地方:
BrightnessController-> createcreate(ToggleSlider toggleSlider)
QSPanelController
调用地方:QSPanelController 构造方法中:*
mBrightnessSliderFactory = brightnessSliderFactory;
//通过factory里的create方法生成
mBrightnessSlider = mBrightnessSliderFactory.create(getContext(), mView);
//mView就是上边的QSPanel,可以看到,亮度条是动态添加到容器里的
mView.setBrightnessView(mBrightnessSlider.getRootView());
//通过factory里的create方法生成
mBrightnessController = brightnessControllerFactory.create(mBrightnessSlider);
创建mBrightnessSlider 变量的创建:
mBrightnessSlider = mBrightnessSliderFactory.create(getContext(), mView);
-> BrightnessSlider 的create 方法
/**
* Creates the view hierarchy and controller
*
* @param context a {@link Context} to inflate the hierarchy
* @param viewRoot the {@link ViewGroup} that will contain the hierarchy. The inflated
* hierarchy will not be attached
*/
public BrightnessSlider create(Context context, @Nullable ViewGroup viewRoot) {
int layout = getLayout();
BrightnessSliderView root = (BrightnessSliderView) LayoutInflater.from(context)
.inflate(layout, viewRoot, false);
return new BrightnessSlider(root, mFalsingManager);
}
return 返回的不就是创建的它的视线的子类吗?BrightnessSliderView
这样就间接实现了 对外Brightness 控制器BrightnessController 和 view 对应BrightnessSliderView 的绑定。
setBrightnessView
关注另外一个点:
mView.setBrightnessView(mBrightnessSlider.getRootView());
QSPanelController 里面的mView 肯定是QSPanel, setBrightnessView 又是做什么的呢? 且看代码
/**
* Add brightness view above the tile layout.
*
* Used to add the brightness slider after construction.
*/
public void setBrightnessView(@NonNull View view) {
if (mBrightnessView != null) {
removeView(mBrightnessView);
mMovableContentStartIndex--;
}
addView(view, 0);
mBrightnessView = view;
setBrightnessViewMargin();
mMovableContentStartIndex++;
}
就是在QS面板里面添加View,这个地方就是QS 、QQS 相关的核心思想,根布局下面的子布局,都是通过add 添加进去的。
实现方案
实现方案和思想,完全参考亮度进度条的实现方案,故必须对SystemUI有所了解,且对亮度条控制亮度流程、思想比较熟悉,照抄照搬!
几个类的定义,实现volume 控制条,创建几个volume 调节相关的类,
com/android/systemui/settings/volume/
VolumeController.java
VolumeSlider.java
VolumeSliderView.java
和对应的布局文件quick_settings_volume_dialog
布局
quick_settings_volume_dialog 完整版本如下:其中SeekBar 完全延用brightness中的ToggleSeekBar
<com.android.systemui.settings.volume.VolumeSliderView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/brightness_slider"
android:layout_width="match_parent"
android:layout_height="@dimen/brightness_mirror_height"
android:layout_gravity="center"
android:contentDescription="@string/accessibility_brightness"
android:importantForAccessibility="no" >
<com.android.systemui.settings.brightness.ToggleSeekBar
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:minHeight="48dp"
android:thumb="@null"
android:background="@null"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:progressDrawable="@drawable/volume_progress_drawable"
android:splitTrack="false"
/>
</com.android.systemui.settings.volume.VolumeSliderView>
构造控制器并绑定View
QSPanelController 类中,构建View并构建管理view控制器 Control,同时绑定View
mVolumeSliderFactory = volumeSliderFactory;
mVolumeSlider = mVolumeSliderFactory.create(getContext(), mView);
mView.setVolumeView(mVolumeSlider.getRootView());
mVolumeController = volumeControllerFactory.create(mVolumeSlider);
业务
StatusBar
构造方法中,构建 VolumeSlider.Factory mVolumeSliderFactory,通过注解构造方法实现,其实就是加载View,布局就加载出来了
StatusBarPhoneModule
StatusBarPhoneModule.java 中,生成的StatusBar的, 也是在这里,加载传递VolumeSlider 给StatusBar 的,具体实现方式完全通过Dragger注解来实现。
/**
* Provides our instance of StatusBar which is considered optional.
*/
@Provides
@SysUISingleton
static StatusBar provideStatusBar(
....
VolumeSlider.Factory volumeSliderFactory,
....
){
return new StatusBar(
...
volumeSliderFactory,
...
);
};
QSPanel QSPanelController
QSPanel QSPanelController 去掉相关的BrightnessMirrorController[镜像控制条,需求一种已经去掉这个类 或者 暂时无用就屏蔽掉]
同时分析,亮度条和进度条是怎么添加到了QSPanel 中去的
- QSPanel 中添加brightnessSeekView 和 volumSeekView 并设置margin
public void setVolumeView(@NonNull View view) {
Log.d(TAG,"setVolumeView");
if (mVolumeView != null) {
removeView(mVolumeView);
mMovableContentStartIndex--;
}
addView(view, 1);
mVolumeView = view;
setVolumeViewMargin();
mMovableContentStartIndex++;
//view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE);
}
private void setVolumeViewMargin() {
if (mVolumeView != null) {
MarginLayoutParams lp = (MarginLayoutParams) mVolumeView.getLayoutParams();
lp.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qs_brightness_margin_top);
lp.bottomMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qs_brightness_margin_bottom);
mVolumeView.setLayoutParams(lp);
}
}
- QSPanelController
QSPanelController 类中,构建View并构建管理view控制器 Control
mVolumeSliderFactory = volumeSliderFactory;
mVolumeSlider = mVolumeSliderFactory.create(getContext(), mView);
mView.setVolumeView(mVolumeSlider.getRootView());
mVolumeController = volumeControllerFactory.create(mVolumeSlider);
其它相关类
com.android.systemui.util 目录下几个类:
SoundUtils.kt :自定义音量控制工具,将15级别音量调整为100级别,方便展示更加细腻化
ContextProvider.java ApplicationContextProvider.kt :和ApplicationContextProvider 一起,提供全局的Context支持,在AndroidManifest.xml中配置
<!-- Add a Provider to get Application in Project. -->
<provider
android:name="com.android.systemui.util.ApplicationContextProvider"
android:authorities="com.android.systemui.contextprovider"
android:exported="false"
android:grantUriPermissions="true">
</provider>
资源
RK Android12 SystemUI SystemUI源码方便调试
资源实现源码待RK平台集成后,晚些释放。