11package de.binarynoise.freeNotifications
22
33import android.app.NotificationChannel
4+ import android.app.NotificationChannelGroup
5+ import android.os.Build
46import de.binarynoise.logger.Logger.log
57import de.binarynoise.reflection.findDeclaredField
68import de.robv.android.xposed.IXposedHookLoadPackage
@@ -14,53 +16,111 @@ import de.robv.android.xposed.XC_MethodHook as MethodHook
1416class 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
57118private 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