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

news2024/10/5 8:30:13

产品需求

  • 通过设定最大音量限制大屏声音输出,设置完后需要立即生效且需要记忆;
  • 以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_CALL
    7,  // STREAM_SYSTEM
    7,  // STREAM_RING
    15, // STREAM_MUSIC
    7,  // STREAM_ALARM
    7,  // 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数组值同时更新mIndexMax
				MAX_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: false
   Muted Internally: false
   Min: 0
   Max: 15
   streamVolume:8
   Current: 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.coloradmin.cn/o/1900963.html

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

相关文章

vofa+:一款超级好用的可视化串口调试软件

目录 一、软件配置 1、先配置好usart1串口 2、重定向printf: 3&#xff0c;勾选魔术棒中的LIB 二、vofa的使用 1、RawData模式 2、FireWater 一、软件配置 1、先配置好usart1串口 2、重定向printf: 在 stm32f4xx_hal.c中添加&#xff1a; #include <stdio.h> e…

加密货币大利好!9月降息概率突破70%!美国可能大幅降息或多次降息?

根据最新消息&#xff0c;美国9月降息的概率已经突破70%&#xff0c;这对加密货币市场来说是个利好消息。与此同时&#xff0c;美国经济表现疲软&#xff0c;可能会陷入衰退&#xff0c;联邦储备系统(Fed)接下来会不会果断采取大幅降息措施备受关注。 美国劳工统计局7月5日公布…

VBA初学:零件成本统计之一(任务汇总)

经过前期一年多对金蝶K3生产任务流程和操作的改造和优化&#xff0c;现在总算可以将零件加工各个环节的成本进行归集了。 原本想写存储过程&#xff0c;通过直接SQL报表做到K3中去的&#xff0c;但财务原本就是用EXCEL&#xff0c;可以方便调整和保存&#xff0c;加上还有一部分…

Java对象比对工具

背景 前段时间的任务中&#xff0c;遇到了需要识别两个对象不同属性的场景&#xff0c;如果使用传统的一个个属性比对equals方法&#xff0c;会存在大量的重复工作&#xff0c;而且为对象新增了属性后&#xff0c;比对方法也需要同步修改&#xff0c;不方便维护&#xff0c;于是…

76 4G模组 境外拨号入网注意

1 引言 最近朋友把国内的设备拿到新加坡了&#xff0c;然后发现原本国内可以使用的设备无法在异国他乡联网&#xff0c;所以就叫我来看看&#xff0c;发现是附网返回状态、入网APN发生了改变导致的。另外&#xff0c;如果在境外使用国产4G模组拨号入网&#xff0c;也需要关注4G…

Nginx实战:nginx性能压测(ab)

在nginx的生产实践中,不管是服务上线,还是性能优化,都会遇到需要对nginx的性能压测,本文介绍一个简单的压测工具:ab命令 ab(Apache Bench)是一个常用的HTTP压力测试工具,可以用来测试Nginx的性能和压力。ab命令可以指定并发请求数、请求数、请求类型等参数,并输出测试…

MySQL第三次作业--DML语句(INSERT)

目录 一、在数据库中创建一个表student&#xff0c;用于存储学生信息 二、向student表中添加一条新记录&#xff0c;记录中id字段的值为1&#xff0c;name字段的值为"monkey"&#xff0c;grade字段的值为98.5 三、向student表中添加多条新记录&#xff1a; 2,&qu…

G1.【C语言】EasyX初步了解

1.介绍 EasyX 是针对 C/C 的图形库&#xff0c;可以帮助使用C/C语言的程序员快速上手图形和游戏编程。 2.安装 EasyX Graphics Library for CEasyX Graphics Library 是针对 Visual C 的绘图库&#xff0c;支持 VC6.0 ~ VC2019&#xff0c;简单易用&#xff0c;学习成本极低…

使用WinSCP工具连接Windows电脑与Ubuntu虚拟机实现文件共享传输

一。环境配置 1.首先你的Windows电脑上安装了VMware虚拟机&#xff0c;虚拟机装有Ubuntu系统&#xff1b; 2.在你的windows电脑安装了WinSCP工具&#xff1b; 3.打开WinSCP工具默认是这样 二。设置WinSCP连接 打开WinSCP&#xff0c;点击新标签页&#xff0c;进入到如下图的…

编码与加密

编码与加密在爬虫中经常涉及&#xff0c;常见的编码有base64, unicode, urlencode&#xff0c;常见的加密有MD5, SHA1, HMAC, DES, AES, RSA。 下面逐一介绍&#xff1a; 一&#xff0c;编码 1.1 常规编码 常规编码约定了字符集中字符与一定长度二进制的映射关系&#xff0…

leetcode刷题(51-60)

算法是码农的基本功&#xff0c;也是各个大厂必考察的重点&#xff0c;让我们一起坚持写题吧。 遇事不决&#xff0c;可问春风&#xff0c;春风不语&#xff0c;即是本心。 我们在我们能力范围内&#xff0c;做好我们该做的事&#xff0c;然后相信一切都事最好的安排就可以啦…

BES 平台 SDK之ANC 参数调整

前言: 最近项目开发进入到DV 阶段,客户临时提了一个需求,希望在ota升级的时候,保留ANC 参数下的total_gain 值,ota只更新滤波器相关参数。total_gain 继续使用产线校准好的值。 一:ANC 参数 1.首先需要找到代码对应ANC 加载的函数: best1502x_ibrt_anc_…

TeXstudio对已加载宏包的命令标记为暗红色未知命令

宏包已正常加载&#xff0c;编译也正常&#xff0c;但却将某些命令标记为暗红色。 具体的原因可参考 https://sourceforge.net/p/texstudio/wiki/Frequently%20Asked%20Questions/#how-does-txs-know-about-valid-commandshttps://sourceforge.net/p/texstudio/wiki/Frequent…

Vue 3集成krpano 全景图展示

Vue 3集成krpano 全景图展示 星光云全景系统源码 VR全景体验地址 星光云全景VR系统 将全景krpano静态资源文件vtour放入vue项目中 导入vue之前需要自己制作一个全景图 需要借助官方工具进行制作 工具下载地址&#xff1a;krpano工具下载地址 注意事项&#xff1a;vuecli…

(软件06)串口屏的应用,让你的产品显得高级一点(下篇)

本文目录 学习前言 单片机代码实现 学习前言 目前市面上我记得好像有IIC的屏幕、SPI的屏幕、并口屏幕、还有就是今天我们介绍的这个串口屏了&#xff0c;串口屏&#xff0c;就是用串口进行通讯的&#xff0c;上篇我们已经介绍了屏幕供应商提供的上位机软件进行配置好了&#…

两年经验前端带你重学前端框架必会的ajax+node.js+webpack+git等技术的个人学习心得、作业及bug记录 Day1

黑马程序员前端AJAX入门到实战全套教程&#xff0c;包含学前端框架必会的&#xff08;ajaxnode.jswebpackgit&#xff09;&#xff0c;一套全覆盖 Day1 你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​…

前端八股文 对$nextTick的理解

$nexttick是什么? 获取更新后的dom内容 为什么会有$nexttick ? vue的异步更新策略 (这也是vue的优化之一 要不然一修改数据就更新dom 会造成大量的dom更新 浪费性能) 这是因为 message &#xff08;data&#xff09;数据在发现变化的时候&#xff0c;vue 并不会立刻去更…

.mkp勒索病毒:深度解析与防范

引言&#xff1a; 在数字化时代&#xff0c;网络安全问题日益严峻&#xff0c;其中勒索病毒作为一种极具破坏性的恶意软件&#xff0c;严重威胁着个人用户和企业机构的数据安全。在众多勒索病毒家族中&#xff0c;.mkp勒索病毒以其强大的加密能力和广泛的传播方式&#xff0c;成…

54、一维和二维自组织映射(matlab)

1、一维和二维自组织映射原理 一维和二维自组织映射&#xff08;Self-Organizing Maps, SOM&#xff09;是一种无监督的机器学习算法&#xff0c;通过学习输入数据的拓扑结构&#xff0c;将高维输入数据映射到低维的网格结构中&#xff0c;使得相似的输入数据点在映射空间中也…

异步调用 - 初识

目录 1、引入 2、同步调用 2.1、例子&#xff1a;支付功能 2.2、同步调用的好处 2.3、同步调用的缺点 3、异步调用 3.1、异步调用的方式 3.2、异步调用的优势 3.3、异步调用的缺点 3.4、什么场景下使用异步调用 3.5、MQ技术选型 1、引入 为什么想要异步通信呢&…