Skip to content

Commit 893af74

Browse files
fix FreeNotifications for older android versions
1 parent b1f0207 commit 893af74

File tree

2 files changed

+89
-29
lines changed

2 files changed

+89
-29
lines changed

FreeNotifications/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ android {
88

99
defaultConfig {
1010
minSdk = 26
11-
targetSdk = 33
11+
targetSdk = 36
1212
}
1313
}
1414

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package de.binarynoise.freeNotifications
22

33
import android.app.NotificationChannel
4+
import android.app.NotificationChannelGroup
5+
import android.os.Build
46
import de.binarynoise.logger.Logger.log
57
import de.binarynoise.reflection.findDeclaredField
68
import de.robv.android.xposed.IXposedHookLoadPackage
@@ -14,53 +16,111 @@ import de.robv.android.xposed.XC_MethodHook as MethodHook
1416
class Hook : IXposedHookLoadPackage {
1517

1618
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
17-
// AOSP: mBlockableSystem / setBlockable / isBlockable
1819

19-
val cls = NotificationChannel::class.java
20+
fun hookVariable(
21+
clazz: Class<*>,
22+
variableName: String,
23+
setMethodName: String,
24+
getMethodName: String,
25+
value: Boolean,
26+
) {
27+
tryAndLog("${clazz.simpleName} after constructor $variableName") {
28+
val mVariable = clazz.findDeclaredField(variableName)
29+
XposedBridge.hookAllConstructors(clazz, object : MethodHook() {
30+
override fun afterHookedMethod(param: MethodHookParam) {
31+
mVariable.set(param.thisObject, value)
32+
}
33+
})
34+
}
35+
tryAndLog("${clazz.simpleName} $getMethodName") {
36+
XposedHelpers.findAndHookMethod(clazz, getMethodName, returnConstant(value))
37+
}
38+
tryAndLog("${clazz.simpleName} $setMethodName") {
39+
XposedHelpers.findAndHookMethod(clazz, setMethodName, Boolean::class.java, DO_NOTHING)
40+
}
41+
}
2042

21-
val mBlockableSystem = cls.findDeclaredField("mBlockableSystem")
43+
fun String.isCharUpperCase(index: Int): Boolean {
44+
return this[index] == this[index].uppercaseChar()
45+
}
2246

23-
tryAndLog("hook NotificationChannel constructors") {
24-
XposedBridge.hookAllConstructors(cls, object : MethodHook() {
25-
override fun afterHookedMethod(param: MethodHookParam) {
26-
mBlockableSystem.set(param.thisObject, true)
27-
}
28-
})
47+
/**
48+
* Remove prefix only if it is used in a CamelCase sentence,
49+
* only if the letter following the prefix is uppercase.
50+
*/
51+
fun String.removeCamelCasePrefix(prefix: String): String {
52+
if (!this.isCharUpperCase(prefix.length + 1)) return this.removePrefix(prefix)
53+
return this
2954
}
30-
tryAndLog("hook setBlockable") {
31-
XposedHelpers.findAndHookMethod(cls, "setBlockable", Boolean::class.java, DO_NOTHING)
55+
56+
fun sanitizeVariableName(name: String): String {
57+
return name.removeCamelCasePrefix("m")
58+
.removeCamelCasePrefix("set")
59+
.removeCamelCasePrefix("is")
60+
.removeCamelCasePrefix("get")
61+
.replaceFirstChar { it.uppercaseChar() }
3262
}
33-
tryAndLog("hook isBlockable") {
34-
XposedHelpers.findAndHookMethod(cls, "isBlockable", returnConstant(true))
63+
64+
fun hookVariable(clazz: Class<*>, variableName: String, functionName: String, value: Boolean) {
65+
val sanitizedVariableName = sanitizeVariableName(variableName)
66+
val sanitizedFunctionName = sanitizeVariableName(functionName)
67+
return hookVariable(
68+
clazz,
69+
"m$sanitizedVariableName",
70+
"set$sanitizedFunctionName",
71+
"is$sanitizedFunctionName",
72+
value,
73+
)
3574
}
3675

37-
// AOSP: mImportanceLockedDefaultApp / setImportanceLockedByCriticalDeviceFunction / isImportanceLockedByCriticalDeviceFunction
76+
fun hookVariable(clazz: Class<*>, name: String, value: Boolean) {
77+
return hookVariable(clazz, name, name, value)
78+
}
3879

39-
val mImportanceLockedDefaultApp = cls.findDeclaredField("mImportanceLockedDefaultApp")
80+
// AOSP
81+
hookVariable(
82+
NotificationChannel::class.java,
83+
"mBlockableSystem",
84+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) "setBlockable" else "setBlockableSystem",
85+
true,
86+
)
4087

41-
tryAndLog("hook constructors") {
42-
XposedBridge.hookAllConstructors(cls, object : MethodHook() {
43-
override fun afterHookedMethod(param: MethodHookParam) {
44-
mImportanceLockedDefaultApp.set(param.thisObject, false)
45-
}
46-
})
88+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
89+
// AOSP
90+
hookVariable(
91+
NotificationChannel::class.java,
92+
"mImportanceLockedDefaultApp",
93+
"setImportanceLockedByCriticalDeviceFunction",
94+
false,
95+
)
4796
}
48-
tryAndLog("hook setImportanceLockedByCriticalDeviceFunction") {
49-
XposedHelpers.findAndHookMethod(cls, "setImportanceLockedByCriticalDeviceFunction", Boolean::class.java, DO_NOTHING)
97+
98+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
99+
// AOSP
100+
hookVariable(
101+
NotificationChannel::class.java,
102+
"mImportanceLockedByOEM",
103+
false,
104+
)
50105
}
51-
tryAndLog("hook isImportanceLockedByCriticalDeviceFunction") {
52-
XposedHelpers.findAndHookMethod(cls, "isImportanceLockedByCriticalDeviceFunction", returnConstant(false))
106+
107+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
108+
// AOSP
109+
hookVariable(
110+
NotificationChannelGroup::class.java,
111+
"mBlocked",
112+
false,
113+
)
53114
}
54115
}
55116
}
56117

57118
private inline fun tryAndLog(message: String, block: () -> Unit) {
58-
log(message)
59119
return try {
60120
block()
61-
log("done!")
121+
log("hook $message successful!")
62122
} catch (t: Throwable) {
63-
log("failed!")
123+
log("hook $message failed!", t)
64124
XposedBridge.log(t)
65125
}
66126
}

0 commit comments

Comments
 (0)