Skip to content

Commit 2d669b5

Browse files
improve VolumeStepsIncrease
* settings activity * hooking AudioService
1 parent 74c9b8f commit 2d669b5

File tree

7 files changed

+205
-34
lines changed

7 files changed

+205
-34
lines changed

VolumeStepsIncrease/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ android {
1313
}
1414

1515
dependencies {
16-
implementation(libs.androidx.preference)
16+
implementation(libs.androidx.preference.ktx)
1717
}
Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,52 @@
11
package com.programminghoch10.VolumeStepsIncrease
22

3+
import com.programminghoch10.VolumeStepsIncrease.StockValues.MAX_STREAM_VOLUME
4+
import com.programminghoch10.VolumeStepsIncrease.StockValues.MIN_STREAM_VOLUME
5+
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_ALARM
6+
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_MUSIC
7+
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_SYSTEM
8+
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_VOICE_CALL
9+
310
object Common {
4-
val streams = arrayOf(
5-
"ro.config.vc_call_vol_steps",
6-
"ro.config.media_vol_steps",
7-
"ro.config.alarm_vol_steps",
8-
"ro.config.system_vol_steps",
11+
val SHARED_PREFERENCES_NAME = "streams"
12+
13+
val STREAMS = StockValues::class.java.declaredFields.filter { it.name.startsWith("STREAM_") }.associate { it.name to it.getInt(null) }
14+
15+
val STREAM_NAMES = STREAMS.keys
16+
17+
val systemPropertyToStream = mapOf(
18+
"ro.config.vc_call_vol_steps" to STREAM_VOICE_CALL,
19+
"ro.config.media_vol_steps" to STREAM_MUSIC,
20+
"ro.config.alarm_vol_steps" to STREAM_ALARM,
21+
"ro.config.system_vol_steps" to STREAM_SYSTEM,
922
)
1023

11-
val defaultStreamValues = mapOf<String, Int>(
12-
// default volumes from AudioService
13-
"ro.config.vc_call_vol_steps" to 5,
14-
"ro.config.media_vol_steps" to 15,
15-
"ro.config.alarm_vol_steps" to 7,
16-
"ro.config.system_vol_steps" to 7,
17-
).mapValues { it.value * 2 }
24+
val moduleDefaultVolumeSteps = mapOf(
25+
STREAM_MUSIC to MAX_STREAM_VOLUME[STREAM_MUSIC] * 2,
26+
STREAM_VOICE_CALL to MAX_STREAM_VOLUME[STREAM_VOICE_CALL] * 2,
27+
)
28+
29+
fun getSystemMaxVolumeSteps(stream: Int): Int {
30+
return MAX_STREAM_VOLUME[stream]
31+
}
32+
33+
fun getModuleDefaultVolumeSteps(stream: Int): Int {
34+
return moduleDefaultVolumeSteps[stream] ?: getSystemMaxVolumeSteps(stream)
35+
}
36+
37+
fun getModuleMaxVolumeSteps(stream: Int): Int {
38+
return getSystemMaxVolumeSteps(stream) * 3
39+
}
40+
41+
fun getSystemMinVolumeSteps(stream: Int): Int {
42+
return MIN_STREAM_VOLUME[stream]
43+
}
44+
45+
fun getModuleMinVolumeSteps(stream: Int): Int {
46+
return getSystemMinVolumeSteps((stream))
47+
}
1848

19-
fun getMaxVolumeSteps(): Int {
20-
return defaultStreamValues.values.maxOf { it } * 3
49+
fun getPreferenceKey(stream: Int): String {
50+
return "STREAM_${stream}"
2151
}
2252
}

VolumeStepsIncrease/src/main/java/com/programminghoch10/VolumeStepsIncrease/Hook.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package com.programminghoch10.VolumeStepsIncrease
22

33
import android.util.Log
4-
import com.programminghoch10.VolumeStepsIncrease.Common.streams
4+
import com.programminghoch10.VolumeStepsIncrease.Common.SHARED_PREFERENCES_NAME
5+
import com.programminghoch10.VolumeStepsIncrease.Common.STREAMS
6+
import com.programminghoch10.VolumeStepsIncrease.Common.getPreferenceKey
7+
import com.programminghoch10.VolumeStepsIncrease.Common.systemPropertyToStream
58
import de.robv.android.xposed.IXposedHookLoadPackage
69
import de.robv.android.xposed.XSharedPreferences
10+
import de.robv.android.xposed.XposedBridge
711
import de.robv.android.xposed.XposedHelpers
812
import de.robv.android.xposed.callbacks.XC_LoadPackage
913
import de.robv.android.xposed.XC_MethodHook as MethodHook
@@ -19,9 +23,9 @@ class Hook : IXposedHookLoadPackage {
1923
val key = param.args[0] as String
2024
param.args[1] as Int
2125
val result = param.result as Int
22-
if (!streams.contains(key)) return
23-
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, "streams")
24-
if (!preferences.contains(key)) return
26+
val streamInt = systemPropertyToStream[key] ?: return
27+
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
28+
if (!preferences.contains(getPreferenceKey(streamInt))) return
2529
param.result = preferences.getInt(key, result)
2630
Log.d("Logger", "beforeHookedMethod: replace $key with ${param.result}")
2731
}
@@ -33,5 +37,16 @@ class Hook : IXposedHookLoadPackage {
3337
if (key == "audio.safemedia.bypass") param.result = true
3438
}
3539
})
40+
41+
val AudioServiceClass = XposedHelpers.findClass("com.android.server.audio.AudioService", lpparam.classLoader)
42+
XposedBridge.hookAllConstructors(AudioServiceClass, object : MethodHook() {
43+
override fun afterHookedMethod(param: MethodHookParam) {
44+
val MAX_STREAM_VOLUME = XposedHelpers.getObjectField(param.thisObject, "MAX_STREAM_VOLUME") as Array<Int>
45+
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
46+
STREAMS.filter { preferences.contains(getPreferenceKey(it.value)) }
47+
.map { it.value to preferences.getInt(getPreferenceKey(it.value), MAX_STREAM_VOLUME[it.value]) }
48+
.forEach { MAX_STREAM_VOLUME[it.first] = it.second }
49+
}
50+
})
3651
}
3752
}
Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
package com.programminghoch10.VolumeStepsIncrease
22

3+
import kotlin.math.round
34
import android.annotation.SuppressLint
45
import android.os.Bundle
56
import androidx.fragment.app.FragmentActivity
7+
import androidx.preference.Preference
68
import androidx.preference.PreferenceFragmentCompat
9+
import androidx.preference.PreferenceGroup
710
import androidx.preference.SeekBarPreference
8-
import com.programminghoch10.VolumeStepsIncrease.Common.defaultStreamValues
9-
import com.programminghoch10.VolumeStepsIncrease.Common.getMaxVolumeSteps
10-
import com.programminghoch10.VolumeStepsIncrease.Common.streams
11+
import androidx.preference.children
12+
import com.programminghoch10.VolumeStepsIncrease.Common.SHARED_PREFERENCES_NAME
13+
import com.programminghoch10.VolumeStepsIncrease.Common.STREAMS
14+
import com.programminghoch10.VolumeStepsIncrease.Common.STREAM_NAMES
15+
import com.programminghoch10.VolumeStepsIncrease.Common.getModuleDefaultVolumeSteps
16+
import com.programminghoch10.VolumeStepsIncrease.Common.getModuleMaxVolumeSteps
17+
import com.programminghoch10.VolumeStepsIncrease.Common.getModuleMinVolumeSteps
18+
import com.programminghoch10.VolumeStepsIncrease.Common.getPreferenceKey
19+
import com.programminghoch10.VolumeStepsIncrease.Common.getSystemMaxVolumeSteps
1120

1221
class SettingsActivity : FragmentActivity() {
1322

@@ -26,21 +35,43 @@ class SettingsActivity : FragmentActivity() {
2635
@SuppressLint("WorldReadableFiles")
2736
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
2837
setPreferencesFromResource(R.xml.root_preferences, rootKey)
29-
preferenceManager.sharedPreferencesName = "streams"
38+
preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
3039
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
3140

32-
for (stream in streams) {
33-
val defaultValue = defaultStreamValues[stream]!!
41+
for (stream in STREAM_NAMES) {
42+
val streamInt = STREAMS[stream]!!
3443
val preference = SeekBarPreference(requireContext())
35-
preference.key = stream
36-
preference.title = stream
37-
preference.min = 1
38-
preference.max = getMaxVolumeSteps()
39-
preference.setDefaultValue(defaultValue)
44+
preference.key = getPreferenceKey(streamInt)
45+
preference.title = stream.replace("STREAM_", "")
46+
preference.min = getModuleMinVolumeSteps(streamInt)
47+
preference.max = getModuleMaxVolumeSteps(streamInt)
48+
preference.setDefaultValue(getModuleDefaultVolumeSteps(streamInt))
4049
preference.showSeekBarValue = true
41-
//preference.summary = "default = ${defaultValue / 2}"
50+
preference.updatesContinuously = true
51+
preference.setOnPreferenceChangeListener { preference, newValue ->
52+
val factor = (newValue as Int).toDouble() / getSystemMaxVolumeSteps(streamInt)
53+
preference.summary = arrayOf(
54+
"factor=${factor.round(1)}x ",
55+
//"systemMin=${getSystemMinVolumeSteps(streamInt)} ",
56+
"systemMax=${getSystemMaxVolumeSteps(streamInt)} ",
57+
).joinToString(" ")
58+
true
59+
}
4260
preferenceScreen.addPreference(preference)
61+
preference.onPreferenceChangeListener?.onPreferenceChange(preference, preference.value)
4362
}
63+
preferenceScreen.setIconSpaceReservedRecursive(false)
64+
}
65+
66+
fun Preference.setIconSpaceReservedRecursive(iconSpaceReserved: Boolean) {
67+
this.isIconSpaceReserved = iconSpaceReserved
68+
if (this is PreferenceGroup) children.forEach { it.setIconSpaceReservedRecursive(iconSpaceReserved) }
69+
}
70+
71+
fun Double.round(decimals: Int = 0): Double {
72+
var multiplier = 1.0
73+
repeat(decimals) { multiplier *= 10 }
74+
return round(this * multiplier) / multiplier
4475
}
4576
}
4677
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package com.programminghoch10.VolumeStepsIncrease
2+
3+
import android.media.AudioManager
4+
import androidx.annotation.Keep
5+
6+
@Keep
7+
object StockValues {
8+
9+
// from com.android.server.media.AudioService
10+
11+
/** Maximum volume index values for audio streams */
12+
var MAX_STREAM_VOLUME: IntArray = intArrayOf(
13+
5, // STREAM_VOICE_CALL
14+
7, // STREAM_SYSTEM
15+
7, // STREAM_RING
16+
15, // STREAM_MUSIC
17+
7, // STREAM_ALARM
18+
7, // STREAM_NOTIFICATION
19+
15, // STREAM_BLUETOOTH_SCO
20+
7, // STREAM_SYSTEM_ENFORCED
21+
15, // STREAM_DTMF
22+
15, // STREAM_TTS
23+
15, // STREAM_ACCESSIBILITY
24+
15, // STREAM_ASSISTANT
25+
)
26+
27+
/** Minimum volume index values for audio streams */
28+
var MIN_STREAM_VOLUME: IntArray = intArrayOf(
29+
1, // STREAM_VOICE_CALL
30+
0, // STREAM_SYSTEM
31+
0, // STREAM_RING
32+
0, // STREAM_MUSIC
33+
1, // STREAM_ALARM
34+
0, // STREAM_NOTIFICATION
35+
0, // STREAM_BLUETOOTH_SCO
36+
0, // STREAM_SYSTEM_ENFORCED
37+
0, // STREAM_DTMF
38+
0, // STREAM_TTS
39+
1, // STREAM_ACCESSIBILITY
40+
0, // STREAM_ASSISTANT
41+
)
42+
43+
// from android.media.AudioSystem
44+
45+
const val STREAM_VOICE_CALL: Int = AudioManager.STREAM_VOICE_CALL
46+
47+
/** @hide Used to identify the volume of audio streams for system sounds
48+
*/
49+
const val STREAM_SYSTEM: Int = AudioManager.STREAM_SYSTEM
50+
51+
/** @hide Used to identify the volume of audio streams for the phone ring and message alerts
52+
*/
53+
const val STREAM_RING: Int = AudioManager.STREAM_RING
54+
55+
/** @hide Used to identify the volume of audio streams for music playback
56+
*/
57+
const val STREAM_MUSIC: Int = AudioManager.STREAM_MUSIC
58+
59+
/** @hide Used to identify the volume of audio streams for alarms
60+
*/
61+
const val STREAM_ALARM: Int = AudioManager.STREAM_ALARM
62+
63+
/** @hide Used to identify the volume of audio streams for notifications
64+
*/
65+
const val STREAM_NOTIFICATION: Int = AudioManager.STREAM_NOTIFICATION
66+
67+
/** @hide
68+
* Used to identify the volume of audio streams for phone calls when connected on bluetooth
69+
*/
70+
@Deprecated("use {@link #STREAM_VOICE_CALL} instead ")
71+
const val STREAM_BLUETOOTH_SCO: Int = 6
72+
73+
/** @hide Used to identify the volume of audio streams for enforced system sounds in certain
74+
* countries (e.g camera in Japan)
75+
*/
76+
const val STREAM_SYSTEM_ENFORCED: Int = 7
77+
78+
/** @hide Used to identify the volume of audio streams for DTMF tones
79+
*/
80+
const val STREAM_DTMF: Int = AudioManager.STREAM_DTMF
81+
82+
/** @hide Used to identify the volume of audio streams exclusively transmitted through the
83+
* speaker (TTS) of the device
84+
*/
85+
const val STREAM_TTS: Int = 9
86+
87+
/** @hide Used to identify the volume of audio streams for accessibility prompts
88+
*/
89+
const val STREAM_ACCESSIBILITY: Int = AudioManager.STREAM_ACCESSIBILITY
90+
91+
/** @hide Used to identify the volume of audio streams for virtual assistant
92+
*/
93+
const val STREAM_ASSISTANT: Int = 11
94+
}

VolumeStepsIncrease/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
<string name="app_name">VolumeStepsIncrease</string>
44
<string name="title_activity_settings">Volume Steps Configuration</string>
55
<string name="description">Increase the amount of volume steps.</string>
6+
<string name="restart_required">A restart is required to apply the new values!</string>
67
</resources>
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<PreferenceScreen
2-
xmlns:app="http://schemas.android.com/apk/res-auto">
3-
1+
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
2+
<Preference app:summary="@string/restart_required" />
3+
44
</PreferenceScreen>

0 commit comments

Comments
 (0)