Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ A collection of small Xposed Modules.
| [ResetAllNotificationChannels](ResetAllNotificationChannels) | [@binarynoise](https://github.com/binarynoise) | Reset all Notification Channels: vibrations, ringtones, importance etc. | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=resetAllNotificationChannels) [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/de.binarynoise.resetAllNotificationChannels) |
| [RotationControl](RotationControl) | [@programminghoch10](https://github.com/programminghoch10) | Force rotation for selected packages | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=RotationControl) [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.programminghoch10.RotationControl) |
| [UpsideWifi](UpsideWifi) | [@programminghoch10](https://github.com/programminghoch10) | Turn the WiFi icon upside down | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=UpsideWifi) |
| [VolumeStepsIncrease](VolumeStepsIncrease) | [@programminghoch10](https://github.com/programminghoch10) | Increase the amount of volume steps | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=VolumeStepsIncrease) |
<!--@formatter:on-->

## License
Expand Down
3 changes: 3 additions & 0 deletions VolumeStepsIncrease/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# VolumeStepsIncrease

Increase the amount of volume steps.
17 changes: 17 additions & 0 deletions VolumeStepsIncrease/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
plugins {
alias(libs.plugins.buildlogic.android.application)
alias(libs.plugins.buildlogic.kotlin.android)
}

android {
namespace = "com.programminghoch10.VolumeStepsIncrease"

defaultConfig {
minSdk = 23
targetSdk = 35
}
}

dependencies {
implementation(libs.androidx.preference.ktx)
}
37 changes: 37 additions & 0 deletions VolumeStepsIncrease/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">

<application android:label="@string/app_name">
<activity
android:name=".SettingsActivity"
android:exported="true"
android:label="@string/title_activity_settings"
android:theme="@style/AppTheme"
android:excludeFromRecents="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
</intent-filter>
</activity>

<meta-data
android:name="xposedmodule"
android:value="true"
/>
<meta-data
android:name="xposeddescription"
android:value="@string/description"
/>
<meta-data
android:name="xposedminversion"
android:value="93"
/>
<meta-data
android:name="xposedscope"
android:resource="@array/scope"
/>
</application>

</manifest>
1 change: 1 addition & 0 deletions VolumeStepsIncrease/src/main/assets/xposed_init
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.programminghoch10.VolumeStepsIncrease.Hook
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.programminghoch10.VolumeStepsIncrease

import com.programminghoch10.VolumeStepsIncrease.StockValues.MAX_STREAM_VOLUME
import com.programminghoch10.VolumeStepsIncrease.StockValues.MIN_STREAM_VOLUME
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_ALARM
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_MUSIC
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_SYSTEM
import com.programminghoch10.VolumeStepsIncrease.StockValues.STREAM_VOICE_CALL

object Common {
val SHARED_PREFERENCES_NAME = "streams"

val STREAMS = StockValues::class.java.declaredFields.filter { it.name.startsWith("STREAM_") }.associate { it.name to it.getInt(null) }

val systemPropertyToStream = mapOf(
"ro.config.vc_call_vol_steps" to STREAM_VOICE_CALL,
"ro.config.media_vol_steps" to STREAM_MUSIC,
"ro.config.alarm_vol_steps" to STREAM_ALARM,
"ro.config.system_vol_steps" to STREAM_SYSTEM,
)

val moduleDefaultVolumeSteps = mapOf(
STREAM_MUSIC to MAX_STREAM_VOLUME[STREAM_MUSIC] * 2,
STREAM_VOICE_CALL to MAX_STREAM_VOLUME[STREAM_VOICE_CALL] * 2,
)

fun getSystemMaxVolumeSteps(stream: Int): Int {
return MAX_STREAM_VOLUME[stream]
}

fun getModuleDefaultVolumeSteps(stream: Int): Int {
return moduleDefaultVolumeSteps[stream] ?: getSystemMaxVolumeSteps(stream)
}

fun getModuleMaxVolumeSteps(stream: Int): Int {
return getSystemMaxVolumeSteps(stream) * 3
}

fun getSystemMinVolumeSteps(stream: Int): Int {
return MIN_STREAM_VOLUME[stream]
}

fun getModuleMinVolumeSteps(stream: Int): Int {
return getSystemMinVolumeSteps((stream))
}

fun getPreferenceKey(stream: Int): String {
return "STREAM_${stream}"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.programminghoch10.VolumeStepsIncrease

import android.util.Log
import com.programminghoch10.VolumeStepsIncrease.Common.SHARED_PREFERENCES_NAME
import com.programminghoch10.VolumeStepsIncrease.Common.STREAMS
import com.programminghoch10.VolumeStepsIncrease.Common.getPreferenceKey
import com.programminghoch10.VolumeStepsIncrease.Common.systemPropertyToStream
import de.robv.android.xposed.IXposedHookLoadPackage
import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage
import de.robv.android.xposed.XC_MethodHook as MethodHook

class Hook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
if (lpparam.packageName == BuildConfig.APPLICATION_ID) return

val SystemPropertiesClass = XposedHelpers.findClass("android.os.SystemProperties", lpparam.classLoader)

XposedHelpers.findAndHookMethod(SystemPropertiesClass, "getInt", String::class.java, Int::class.java, object : MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
val key = param.args[0] as String
param.args[1] as Int
val result = param.result as Int
val streamInt = systemPropertyToStream[key] ?: return
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
if (!preferences.contains(getPreferenceKey(streamInt))) return
param.result = preferences.getInt(key, result)
Log.d("Logger", "beforeHookedMethod: replace $key with ${param.result}")
}
})

XposedHelpers.findAndHookMethod(SystemPropertiesClass, "getBoolean", String::class.java, Boolean::class.java, object : MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam) {
val key = param.args[0] as String
if (key == "audio.safemedia.bypass") param.result = true
}
})

val AudioServiceClass = XposedHelpers.findClass("com.android.server.audio.AudioService", lpparam.classLoader)
XposedBridge.hookAllConstructors(AudioServiceClass, object : MethodHook() {
override fun afterHookedMethod(param: MethodHookParam) {
val MAX_STREAM_VOLUME = XposedHelpers.getObjectField(param.thisObject, "MAX_STREAM_VOLUME") as Array<Int>
val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
STREAMS.filter { preferences.contains(getPreferenceKey(it.value)) }
.map { it.value to preferences.getInt(getPreferenceKey(it.value), MAX_STREAM_VOLUME[it.value]) }
.forEach { MAX_STREAM_VOLUME[it.first] = it.second }
}
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.programminghoch10.VolumeStepsIncrease

import kotlin.math.round
import android.annotation.SuppressLint
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceGroup
import androidx.preference.SeekBarPreference
import androidx.preference.children
import com.programminghoch10.VolumeStepsIncrease.Common.SHARED_PREFERENCES_NAME
import com.programminghoch10.VolumeStepsIncrease.Common.STREAMS
import com.programminghoch10.VolumeStepsIncrease.Common.getModuleDefaultVolumeSteps
import com.programminghoch10.VolumeStepsIncrease.Common.getModuleMaxVolumeSteps
import com.programminghoch10.VolumeStepsIncrease.Common.getModuleMinVolumeSteps
import com.programminghoch10.VolumeStepsIncrease.Common.getPreferenceKey
import com.programminghoch10.VolumeStepsIncrease.Common.getSystemMaxVolumeSteps

class SettingsActivity : FragmentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.settings_activity)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction().replace(R.id.settings, SettingsFragment()).commit()
}
actionBar?.setDisplayHomeAsUpEnabled(true)
}

override fun onNavigateUp(): Boolean {
finish()
return super.onNavigateUp()
}

class SettingsFragment : PreferenceFragmentCompat() {
@SuppressLint("WorldReadableFiles")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE

for (stream in STREAMS) {
val preference = SeekBarPreference(requireContext())
preference.key = getPreferenceKey(stream.value)
preference.title = stream.key.replace("STREAM_", "")
preference.min = getModuleMinVolumeSteps(stream.value)
preference.max = getModuleMaxVolumeSteps(stream.value)
preference.setDefaultValue(getModuleDefaultVolumeSteps(stream.value))
preference.showSeekBarValue = true
preference.updatesContinuously = true
preference.setOnPreferenceChangeListener { preference, newValue ->
val factor = (newValue as Int).toDouble() / getSystemMaxVolumeSteps(stream.value)
preference.summary = arrayOf(
"factor=${factor.round(1)}x ",
//"systemMin=${getSystemMinVolumeSteps(stream.value)} ",
"systemMax=${getSystemMaxVolumeSteps(stream.value)} ",
).joinToString(" ")
true
}
preferenceScreen.addPreference(preference)
preference.onPreferenceChangeListener?.onPreferenceChange(preference, preference.value)
}
preferenceScreen.setIconSpaceReservedRecursive(false)
}

fun Preference.setIconSpaceReservedRecursive(iconSpaceReserved: Boolean) {
this.isIconSpaceReserved = iconSpaceReserved
if (this is PreferenceGroup) children.forEach { it.setIconSpaceReservedRecursive(iconSpaceReserved) }
}

fun Double.round(decimals: Int = 0): Double {
var multiplier = 1.0
repeat(decimals) { multiplier *= 10 }
return round(this * multiplier) / multiplier
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.programminghoch10.VolumeStepsIncrease

import android.media.AudioManager
import androidx.annotation.Keep

@Keep
object StockValues {

// from com.android.server.media.AudioService

/** Maximum volume index values for audio streams */
var MAX_STREAM_VOLUME: IntArray = intArrayOf(
5, // STREAM_VOICE_CALL
7, // STREAM_SYSTEM
7, // STREAM_RING
15, // STREAM_MUSIC
7, // STREAM_ALARM
7, // STREAM_NOTIFICATION
15, // STREAM_BLUETOOTH_SCO
7, // STREAM_SYSTEM_ENFORCED
15, // STREAM_DTMF
15, // STREAM_TTS
15, // STREAM_ACCESSIBILITY
15, // STREAM_ASSISTANT
)

/** Minimum volume index values for audio streams */
var MIN_STREAM_VOLUME: IntArray = intArrayOf(
1, // STREAM_VOICE_CALL
0, // STREAM_SYSTEM
0, // STREAM_RING
0, // STREAM_MUSIC
1, // STREAM_ALARM
0, // STREAM_NOTIFICATION
0, // STREAM_BLUETOOTH_SCO
0, // STREAM_SYSTEM_ENFORCED
0, // STREAM_DTMF
0, // STREAM_TTS
1, // STREAM_ACCESSIBILITY
0, // STREAM_ASSISTANT
)

// from android.media.AudioSystem

const val STREAM_VOICE_CALL: Int = AudioManager.STREAM_VOICE_CALL

/** @hide Used to identify the volume of audio streams for system sounds
*/
const val STREAM_SYSTEM: Int = AudioManager.STREAM_SYSTEM

/** @hide Used to identify the volume of audio streams for the phone ring and message alerts
*/
const val STREAM_RING: Int = AudioManager.STREAM_RING

/** @hide Used to identify the volume of audio streams for music playback
*/
const val STREAM_MUSIC: Int = AudioManager.STREAM_MUSIC

/** @hide Used to identify the volume of audio streams for alarms
*/
const val STREAM_ALARM: Int = AudioManager.STREAM_ALARM

/** @hide Used to identify the volume of audio streams for notifications
*/
const val STREAM_NOTIFICATION: Int = AudioManager.STREAM_NOTIFICATION

/** @hide
* Used to identify the volume of audio streams for phone calls when connected on bluetooth
*/
@Deprecated("use {@link #STREAM_VOICE_CALL} instead ")
const val STREAM_BLUETOOTH_SCO: Int = 6

/** @hide Used to identify the volume of audio streams for enforced system sounds in certain
* countries (e.g camera in Japan)
*/
const val STREAM_SYSTEM_ENFORCED: Int = 7

/** @hide Used to identify the volume of audio streams for DTMF tones
*/
const val STREAM_DTMF: Int = AudioManager.STREAM_DTMF

/** @hide Used to identify the volume of audio streams exclusively transmitted through the
* speaker (TTS) of the device
*/
const val STREAM_TTS: Int = 9

/** @hide Used to identify the volume of audio streams for accessibility prompts
*/
const val STREAM_ACCESSIBILITY: Int = AudioManager.STREAM_ACCESSIBILITY

/** @hide Used to identify the volume of audio streams for virtual assistant
*/
const val STREAM_ASSISTANT: Int = 11
}
14 changes: 14 additions & 0 deletions VolumeStepsIncrease/src/main/res/layout/settings_activity.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
style="@style/AppTheme.Edge2EdgeFix"
>

<FrameLayout
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
4 changes: 4 additions & 0 deletions VolumeStepsIncrease/src/main/res/values-v21/themes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault.Settings" />
</resources>
6 changes: 6 additions & 0 deletions VolumeStepsIncrease/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="scope">
<item>android</item>
</string-array>
</resources>
7 changes: 7 additions & 0 deletions VolumeStepsIncrease/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">VolumeStepsIncrease</string>
<string name="title_activity_settings">Volume Steps Configuration</string>
<string name="description">Increase the amount of volume steps.</string>
<string name="restart_required">A restart is required to apply the new values!</string>
</resources>
8 changes: 8 additions & 0 deletions VolumeStepsIncrease/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="@android:style/Theme.DeviceDefault" />

<style name="AppTheme.Edge2EdgeFix">
<item name="android:fitsSystemWindows">true</item>
</style>
</resources>
4 changes: 4 additions & 0 deletions VolumeStepsIncrease/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference app:summary="@string/restart_required" />

</PreferenceScreen>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Increase the amount of volume steps.
Loading
Loading