Android动态设置系统音量最大值

产品需求

  • 通过设定最大音量限制大屏声音输出,设置完后需要立即生效且需要记忆;
  • 以10%为递增状态,设置最大音量后,无论最大音量调节至多少百分比,音量条始终显示为100%(比如最大音量设置为80%,即侧边音量条依然可以显示充满,但是音量只有原来的80%)。

从技术角度上理解为:Framework层需要提供一个动态设置系统音量最大值的开放接口供应用层调用,应用apk通过设置最大音量百分比后系统声音效果需要立即生效,同时原生音量条的最大值范围也要变化。

需要涉及3个地方的修改:

  • 添加Framework层接口方法
  • 应用层apk调用新增的接口
  • SystemUI音量条监听最大值变化

Framework层接口

1、以Android 13为例,如果修改系统默认的STREAM_MUSIC音量值,通常是修改AudioService.java下MAX_STREAM_VOLUME数组对应的值15。但我们需要动态修改这个值且不需要重启服务立即生效。

/** Maximum volume index values for audio streams */
protected static int[] MAX_STREAM_VOLUME = new int[] {5,  // STREAM_VOICE_CALL7,  // STREAM_SYSTEM7,  // STREAM_RING15, // STREAM_MUSIC7,  // STREAM_ALARM7,  // STREAM_NOTIFICATION......
};

2、代码实现
/frameworks/base/media/java/android/media/AudioManager.java
/frameworks/base/media/java/android/media/IAudioService.aidl
/frameworks/base/services/core/java/com/android/server/audio/AudioService.java

IAudioService.aidl主要是声明一个新的系统服务方法,然后通过AudioManager.java来调用服务的具体实现

public void setStreamMaxVolume(int streamType, int maxVolume) {final IAudioService service = getService();try {service.setStreamMaxVolume(streamType, maxVolume);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

AudioService.java具体实现setStreamMaxVolume方法

//动态变更STREAM_MUSIC最大音量值接口
public void setStreamMaxVolume(int streamType, int maxVolume) {ensureValidStreamType(streamType);if (streamType == AudioSystem.STREAM_MUSIC) {if (maxVolume <= 15 && maxVolume >= 0) {int preMaxVolume = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC];//更新MAX_STREAM_VOLUME数组值同时更新mIndexMaxMAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxVolume;mStreamStates[streamType].updateMaxIndex(maxVolume);//发送一个MAX_VOLUME_CHANGED_ACTION广播用于通知最大音量值变化,该系统广播需要声明在frameworks/base/core/res/AndroidManifest.xml里Intent intent = new Intent("com.ist.media.MAX_VOLUME_CHANGED_ACTION");intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);intent.putExtra("preMaxVolume", preMaxVolume);intent.putExtra("maxVolume", maxVolume);sendBroadcastToAll(intent);}}
}//关键:该方法加在内部class VolumeStreamState中,VolumeStreamState保存了运行时的音量信息,这里有个流音量index的换算关系,更新MAX_STREAM_VOLUME数组的同时也需要更新mIndexMax       
//mIndexMax = MAX_STREAM_VOLUME[streamType] * 10;
public void updateMaxIndex(int index) {mIndexMax = index * 10;
}

设置后需要有记忆性,即重启后AudioService服务构造初始化时设置最大音量值

	int maxMusicVolume = SystemProperties.getInt("ro.config.media_vol_steps", -1);if (maxMusicVolume != -1) {MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxMusicVolume;} else {//add by XXX 初始化STREAM_MUSIC最大音量值String ptsStr = mSettings.getGlobalString(mContentResolver, "persist.sys.hht.max_volume_percent");//上层通过该Settings值来保存音量设置的百分比if (TextUtils.isEmpty(ptsStr)) {ptsStr = "10";}int maxPercent = Integer.parseInt(ptsStr);maxMusicVolume = Math.round((float) maxPercent / 10 * 15);MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = maxMusicVolume;Log.i(TAG, "init maxMusicVolume = " + maxMusicVolume);//end by XXX
}

应用层apk调用

 /*** 设置最大音量百分比* @param percent 0~10,10%递进*/public void setMaxVolumePercent(int percent){Log.i(TAG, "setMaxVolumePercent() percent = "+ percent);//保存最大音量百分比设置Settings.Global.putString(context.getContentResolver(), "persist.sys.hht.max_volume_percent", String.valueOf(percent));//最大音量值设置后需要立即生效if (mAudioManager != null) {int currentMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//获取系统最大音量值int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);//当前音量值int maxMusicVolume = Math.round((float) percent / 10 * 15);//调用新增的系统接口方法mAudioManager.setStreamMaxVolume(AudioManager.STREAM_MUSIC, maxMusicVolume);Log.i(TAG, "setStreamMaxVolume = " + maxMusicVolume);if (currentVolume > maxMusicVolume) { //若当前音量值大于最大音量值则取较小值mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxMusicVolume, 0);}}}

SystemUI音量条监听最大值变化

/frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java

final String action = intent.getAction();
boolean changed = false;
if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);final int oldLevel = intent.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream+ " level=" + level + " oldLevel=" + oldLevel);changed = updateStreamLevelW(stream, level);
}
......
//add by XXX 侧边音量条最大值即时更新
else if (action.equals("com.ist.media.MAX_VOLUME_CHANGED_ACTION")) {Log.d(TAG, "onReceive com.ist.media.MAX_VOLUME_CHANGED_ACTION");final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);final int preMaxVolume = intent.getIntExtra("preMaxVolume", -1);final int maxVolume = intent.getIntExtra("maxVolume", -1);Log.i(TAG, "onReceive preMaxVolume = " + preMaxVolume + ", maxVolume = " + maxVolume);if (preMaxVolume != maxVolume) {changed = true;getState();}
}
//end by XXX
if (changed) {mCallbacks.onStateChanged(mState);
}

dumpsys audio调试

通过adb shell dumpsys audio命令查看音频系统的状态信息。
Stream volumes (device: index):查看各类型流的音量值。其中Muted为是否静音,Min为最小值,Max为最大值,Current为各输出设备的当前音量,Devices为当前输出设备。
调试时,我们关注STREAM_MUSIC的Max值有没有按预期的变化即可。

- STREAM_MUSIC:Muted: falseMuted Internally: falseMin: 0Max: 15streamVolume:8Current: 2 (speaker): 8, 400 (hdmi): 8, 80000 (spdif): 8, 100000 (fm_transmitter): 8, 4000000 (usb_headset): 2, 10000000 (echo_canceller): 8, 40000000 (default): 3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/867295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Threejs环境、透视相机、坐标系、光源

文章目录 如何引入threejsnpm方式script方式script module方式 基本流程与坐标摄像机Geometry(几何体)和Material(材质)光源 如何引入threejs 对于很多刚刚上手threejs的朋友&#xff0c;可能第一步引入threejs就出问题了&#xff0c; 明明已经导入了&#xff0c;就是这样问题…

【搭建Nacos服务】centos7 docker从0搭建Nacos服务

前言 本次搭建基于阿里云服务器系统为&#xff08;CentOS7 Linux&#xff09;、Nacos&#xff08;2.0.3&#xff09;、Docker version 26.1.4 本次搭建基于一个新的云服务器 安装java yum install -y java-1.8.0-openjdk.x86_64安装驱动以及gcc等前置需要的命令 yum install …

【nvm管理nodejs版本,切换node指定版本】

nvm管理nodejs版本 nvm管理nodejs版本主要功能使用 nvm nvm管理nodejs版本 nvm&#xff08;Node Version Manager&#xff09;顾名思义node版本管理器&#xff0c;无须去node管网下载很多node安装程序;用于管理多个 Node.js 版本的工具。它允许你在同一台机器上同时安装和管理…

Appium启动APP时报错Security exception: Permission Denial

报错内容Security exception: Permission Denial: starting Intent 直接通过am命令尝试也是同样的报错 查阅资料了解到&#xff1a;android:exported | App quality | Android Developers exported属性默认false&#xff0c;所以android:exported"false"修改为t…

Eureka服务降级策略配置指南:确保微服务架构的弹性

在微服务架构中&#xff0c;服务之间的依赖关系非常普遍。当一个服务不可用时&#xff0c;可能会导致整个系统的故障。服务降级是一种提高系统可用性的策略&#xff0c;它允许服务在某些条件不满足时提供降级的响应。Eureka作为Netflix开源的服务发现框架&#xff0c;可以与Hys…

c与c++ 常用的字符与字符串处理的接口介绍:

在 C 和 C 中&#xff0c;字符与字符串处理是非常基础和常见的操作。以下是一些常用的接口和函数&#xff1a; C 字符串处理接口 字符处理&#xff1a; char 类型&#xff1a;C 中用于表示单个字符。 字符串是以 null 结尾的字符数组&#xff0c;通常使用 char[] 或 char* 表…

基于java+springboot+vue实现的图书商城管理系统(文末源码+Lw)283

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本图书商城管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…

rpm包下载

内网无法下载、选择外网的一台机器下载rpm包 下载后上传rpm包 1、创建下载目录 mkdir /data/asap/test 2、下载能留存包的工具 sudo yum install yum-utils -y 报错就是环境问题没下载成功&#xff0c;我换了个环境正常的机器就可以了 3、下载rpm包到指定目录/data/asa…

测试人员如何管理项目与风险预警

在平时工作过程中&#xff0c;你有没有因项目延时&#xff0c;需求频繁变更&#xff0c;开发提测质量不高&#xff0c;以及漏测的情况下&#xff0c;背了不少锅的情况呢&#xff1f; 作为测试人员&#xff0c;我们应该如何发挥积极主动性&#xff0c;进行项目管理&#xff0c;有…

PHP框架中环境变量的管理和最佳实践

在现代Web开发中&#xff0c;环境变量的使用是确保应用程序在不同环境&#xff08;如开发、测试、生产&#xff09;中安全、灵活运行的关键。PHP框架提供了多种方式来管理和使用环境变量&#xff0c;本文将详细探讨这些方法&#xff0c;并提供一些最佳实践。 环境变量的重要性…

理解async和await

async 和 await 是 JavaScript 中用于编写异步代码的两个关键字。它们让异步代码的编写更加简洁、易于理解和维护&#xff0c;类似于同步代码的写法。 async async 用于声明一个函数是异步的。这意味着这个函数在执行时不会阻塞 JavaScript 的主线程&#xff0c;允许其他代码…

【学术会议征稿】2024年第十届机械制造技术与工程材料国际学术会议(ICMTEM 2024)

2024年第十届机械制造技术与工程材料国际学术会议&#xff08;ICMTEM 2024&#xff09; 2024 10th International Forum on Manufacturing Technology and Engineering Materials 第十届机械制造技术与工程材料国际学术会议&#xff08;ICMTEM 2024&#xff09;将于2024年10月…

影刀_扩展屏幕运行总结

首先&#xff0c;先看一个例子“&#xff1a; 滑块拼图验证免费版 1、影刀在扩展屏幕&#xff0c;谷歌在主屏幕。成功 2、影刀主屏幕&#xff0c;谷歌在扩展屏幕&#xff0c;失败。 3、都在扩展屏幕&#xff0c;失败。 4、都在主屏幕上&#xff0c;成功。从上面的例子可以看出…

本地事务、全局事务、分布式事务

事务处理 事务处理几乎在每一个信息系统中都会涉及&#xff0c;它存在的意义是为了保证系统中所有数据都是符合期望的&#xff0c;且相互关联的数据之间不会产生矛盾&#xff0c;即数据状态的一致性&#xff08;Consistency&#xff09;。 按照数据库的经典理论&#xff0c;要…

(七)[重制]C++命名空间与标准模板库(STL)

​ 引言 在专栏C教程的第六篇C中的结构体与联合体中&#xff0c;介绍了C中的结构体和联合体&#xff0c;包括它们的定义、初始化、内存布局和对齐&#xff0c;以及作为函数参数和返回值的应用。在专栏C教程的第七篇中&#xff0c;我们将深入了解C中的命名空间&#xff08;nam…

linux 基础命令、gcc的基础用法

1、ls——>列出目录下的内容 语法&#xff1a;ls [-a -l -h] [Linux路径] &#xff08;1&#xff09;-a -l -h 是可选的选项 &#xff08;2&#xff09;Linux路径是此命令的可选参数 ①当不使用选项和参数&#xff0c;直接使用 ls 命令本体&#xff0c;表示&#xff1a;…

滑动窗口练习4-将x减到0的最小操作数

题目链接&#xff1a;**. - 力扣&#xff08;LeetCode&#xff09;**&#xff08;字节跳动&#xff09; 题目描述&#xff1a; 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的…

短剧app开发如何对接广告联盟?

短剧app对接广告联盟是一个综合性的过程&#xff0c;涉及多个关键步骤。以下是一个大致的指南&#xff1a; 了解广告联盟&#xff1a; 对市场上的广告联盟进行全面了解&#xff0c;包括它们的类型、支持的广告格式&#xff08;如横幅广告、插屏广告、视频广告等&#xff09;、…

kubernetes集群部署:环境准备及master节点部署(二)

主机名IPv4地址IPv6地址角色安装组件操作系统k8s130-node190192.168.XX.190240a:XX::190masterkubeadm、kubelet、containerdAnolis OS 8.94.19.91-28.1.an8.x86_64k8s130-node191192.168.XX.191240a:XX::191nodekubeadm、kubelet、cri-oAnolis OS 8.94.19.91-28.1.an8.x86_64k…

Java基础-接口与实现

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 Java 接口 什么是接口&#xff1f; 声明接口 实现接口 继承接口 接口的多继承 标记接口 Java 接口 …