fix: remove OneUI unlocked icon cap

This commit is contained in:
ajp_anton
2026-08-30 23:31:24 +00:00
parent 2bf35884dc
commit fe99f789ed
@@ -4,6 +4,8 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.service.notification.StatusBarNotification import android.service.notification.StatusBarNotification
import android.util.Log import android.util.Log
import android.view.View
import android.view.ViewGroup
import io.github.libxposed.api.XposedInterface import io.github.libxposed.api.XposedInterface
import se.ajpanton.notificationsmaster.visibility.NotificationSurface import se.ajpanton.notificationsmaster.visibility.NotificationSurface
import java.lang.reflect.Method import java.lang.reflect.Method
@@ -14,7 +16,9 @@ import java.util.function.Function
/** Filters only OneUI's native unlocked status-bar container. */ /** Filters only OneUI's native unlocked status-bar container. */
internal class OneUiUnlockedIconBackend(private val framework: XposedInterface) : VisibilitySurfaceBackend { internal class OneUiUnlockedIconBackend(private val framework: XposedInterface) : VisibilitySurfaceBackend {
private val controllers = Collections.newSetFromMap(WeakHashMap<Any, Boolean>()) private val controllers = Collections.newSetFromMap(WeakHashMap<Any, Boolean>())
private val iconContainers = Collections.newSetFromMap(WeakHashMap<View, Boolean>())
private lateinit var updateStatusBarIcons: Method private lateinit var updateStatusBarIcons: Method
private lateinit var maxStaticIcons: java.lang.reflect.Field
@Volatile private var blockedObserved = false @Volatile private var blockedObserved = false
override fun install(classLoader: ClassLoader): Boolean = runCatching { override fun install(classLoader: ClassLoader): Boolean = runCatching {
@@ -25,6 +29,10 @@ internal class OneUiUnlockedIconBackend(private val framework: XposedInterface)
require(it.type == container) require(it.type == container)
it.isAccessible = true it.isAccessible = true
} }
maxStaticIcons = container.getDeclaredField("mMaxStaticIcons").also {
require(it.type == Int::class.javaPrimitiveType)
it.isAccessible = true
}
val sbn = entry.getDeclaredField("mSbn").also { val sbn = entry.getDeclaredField("mSbn").also {
require(it.type == StatusBarNotification::class.java) require(it.type == StatusBarNotification::class.java)
it.isAccessible = true it.isAccessible = true
@@ -50,10 +58,13 @@ internal class OneUiUnlockedIconBackend(private val framework: XposedInterface)
updateStatusBarIcons.isAccessible = true updateStatusBarIcons.isAccessible = true
framework.hook(updateStatusBarIcons).intercept { chain -> framework.hook(updateStatusBarIcons).intercept { chain ->
synchronized(controllers) { controllers += chain.thisObject } synchronized(controllers) { controllers += chain.thisObject }
(notificationIcons.get(chain.thisObject) as? View)?.let(::manage)
chain.proceed() chain.proceed()
} }
framework.hook(update).intercept { chain -> framework.hook(update).intercept { chain ->
if (chain.args[1] !== notificationIcons.get(chain.thisObject)) return@intercept chain.proceed() val icons = notificationIcons.get(chain.thisObject)
if (chain.args[1] !== icons) return@intercept chain.proceed()
(icons as? View)?.let(::manage)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val original = chain.args[0] as? Function<Any, Any?> ?: return@intercept chain.proceed() val original = chain.args[0] as? Function<Any, Any?> ?: return@intercept chain.proceed()
val wrapped = Function<Any, Any?> { item -> val wrapped = Function<Any, Any?> { item ->
@@ -77,6 +88,28 @@ internal class OneUiUnlockedIconBackend(private val framework: XposedInterface)
args[0] = wrapped args[0] = wrapped
chain.proceed(args) chain.proceed(args)
} }
container.declaredMethods.filter { it.name == "calculateIconXTranslations" }.forEach { method ->
method.isAccessible = true
framework.hook(method).intercept { chain ->
(chain.thisObject as? View)?.takeIf(::isManaged)?.let(::applyLimit)
chain.proceed()
}
}
container.declaredMethods.filter { it.name == "shouldForceOverflow" }.forEach { method ->
method.isAccessible = true
framework.hook(method).intercept { chain ->
val view = chain.thisObject as? View
if (view == null || !isManaged(view) || chain.args.size < 4) {
return@intercept chain.proceed()
}
val limit = unlockedLimit()
applyLimit(view)
val index = chain.args[0] as? Int ?: return@intercept chain.proceed()
val speedBump = chain.args[1] as? Int ?: -1
val appear = chain.args[2] as? Float ?: 0f
(speedBump != -1 && index >= speedBump && appear > 0f) || index >= limit
}
}
}.onSuccess { }.onSuccess {
Log.i(TAG, "Installed OneUI unlocked notification-icon backend") Log.i(TAG, "Installed OneUI unlocked notification-icon backend")
}.onFailure { }.onFailure {
@@ -94,10 +127,41 @@ internal class OneUiUnlockedIconBackend(private val framework: XposedInterface)
} }
} }
private fun manage(view: View) {
synchronized(iconContainers) { iconContainers += view }
applyLimit(view)
widenParent(view)
}
private fun isManaged(view: View) = synchronized(iconContainers) { view in iconContainers }
private fun applyLimit(view: View) {
maxStaticIcons.setInt(view, unlockedLimit())
}
private fun unlockedLimit() = ProcessVisibilityPolicyCache.unlockedIconLimit ?: UNLIMITED_ICONS
private fun widenParent(view: View) {
var parent = view.parent as? View
repeat(3) {
val current = parent ?: return
if (current.javaClass.name.contains("AlphaOptimized")) {
current.layoutParams?.takeIf {
it.width == ViewGroup.LayoutParams.WRAP_CONTENT || it.width == 0
}?.let {
it.width = ViewGroup.LayoutParams.MATCH_PARENT
current.layoutParams = it
}
}
parent = current.parent as? View
}
}
private companion object { private companion object {
const val TAG = "NotificationsMaster" const val TAG = "NotificationsMaster"
const val CONTROLLER = "com.android.systemui.statusbar.phone.LegacyNotificationIconAreaControllerImpl" const val CONTROLLER = "com.android.systemui.statusbar.phone.LegacyNotificationIconAreaControllerImpl"
const val ICON_CONTAINER = "com.android.systemui.statusbar.phone.NotificationIconContainer" const val ICON_CONTAINER = "com.android.systemui.statusbar.phone.NotificationIconContainer"
const val NOTIFICATION_ENTRY = "com.android.systemui.statusbar.notification.collection.NotificationEntry" const val NOTIFICATION_ENTRY = "com.android.systemui.statusbar.notification.collection.NotificationEntry"
const val UNLIMITED_ICONS = 1000
} }
} }