Stabilize OneUI Cards mode transitions
This commit is contained in:
@@ -46,6 +46,17 @@ internal class OneUiCardsGridRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
fun deactivate(container: ViewGroup) {
|
||||
states[container]?.let { state ->
|
||||
state.active = false
|
||||
failOpen(state)
|
||||
}
|
||||
}
|
||||
|
||||
fun deactivateAll() {
|
||||
states.keys.toList().forEach(::deactivate)
|
||||
}
|
||||
|
||||
fun release(container: ViewGroup) {
|
||||
val state = states.remove(container) ?: return
|
||||
restoreNative(state)
|
||||
|
||||
+5
@@ -84,6 +84,10 @@ internal class OneUiLockscreenCardsBackend(
|
||||
}.forEach { method ->
|
||||
method.isAccessible = true
|
||||
framework.hook(method).intercept { chain ->
|
||||
val leavingLockscreen = method.name == "setState" && chain.args.firstOrNull() != KEYGUARD
|
||||
val enteringDoze = method.name == "setDozeAmountInternal" &&
|
||||
((chain.args.firstOrNull() as? Number)?.toFloat() ?: 0f) > 0f
|
||||
if (leavingLockscreen || enteringDoze) gridRenderer.deactivateAll()
|
||||
val result = chain.proceed()
|
||||
updateState(chain.thisObject)
|
||||
result
|
||||
@@ -206,6 +210,7 @@ internal class OneUiLockscreenCardsBackend(
|
||||
statusBarStateKnown = true
|
||||
}
|
||||
(findField(controller.javaClass, "mIsDozing")?.get(controller) as? Boolean)?.let { dozing = it }
|
||||
if (dozing || statusBarState != KEYGUARD) gridRenderer.deactivateAll()
|
||||
applyTrackedStacks()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package se.ajpanton.notificationsmaster.module
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.provider.Settings
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.Log
|
||||
@@ -19,6 +20,8 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
||||
private val managers = Collections.synchronizedMap(WeakHashMap<Any, State>())
|
||||
private val cardsContainers = Collections.newSetFromMap(WeakHashMap<ViewGroup, Boolean>())
|
||||
private val cardsDozing = Collections.synchronizedMap(WeakHashMap<ViewGroup, Boolean>())
|
||||
private val cardsReadyAt = Collections.synchronizedMap(WeakHashMap<ViewGroup, Long>())
|
||||
private val cardsLocations = Collections.synchronizedMap(WeakHashMap<ViewGroup, Pair<Int, Int>>())
|
||||
private val cardsRenderer = OneUiCardsGridRenderer()
|
||||
private val mainHandler = Handler(Looper.getMainLooper())
|
||||
private val refreshing = ThreadLocal<Boolean>()
|
||||
@@ -26,7 +29,7 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
||||
private lateinit var updateVisible: Method
|
||||
@Volatile private var blockedObserved = false
|
||||
@Volatile private var visibleNotifications = emptyList<StatusBarNotification>()
|
||||
@Volatile private var cardsRefreshPending = false
|
||||
private var cardsRefreshTask: Runnable? = null
|
||||
|
||||
override fun install(classLoader: ClassLoader): Boolean = runCatching {
|
||||
val manager = Class.forName(MANAGER, false, classLoader)
|
||||
@@ -102,11 +105,21 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
||||
}.forEach { method ->
|
||||
method.isAccessible = true
|
||||
framework.hook(method).intercept { chain ->
|
||||
val container = chain.thisObject as? ViewGroup
|
||||
val dozing = chain.args.firstOrNull() == true
|
||||
container?.let {
|
||||
trackCardsContainer(it)
|
||||
cardsDozing[it] = false
|
||||
cardsRenderer.deactivate(it)
|
||||
}
|
||||
val result = chain.proceed()
|
||||
(chain.thisObject as? ViewGroup)?.let { view ->
|
||||
trackCardsContainer(view)
|
||||
cardsDozing[view] = chain.args.firstOrNull() == true
|
||||
refreshCardsGrid()
|
||||
container?.let { view ->
|
||||
cardsDozing[view] = dozing
|
||||
if (dozing) settleCardsGrid(view, DOZE_SETTLE_DELAY) else {
|
||||
cardsReadyAt.remove(view)
|
||||
cardsLocations.remove(view)
|
||||
refreshCardsGrid()
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
@@ -132,54 +145,93 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
||||
if (!cardsContainers.add(container)) return
|
||||
}
|
||||
container.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(view: View) = refreshCardsGrid()
|
||||
override fun onViewAttachedToWindow(view: View) {
|
||||
if (cardsDozing[container] == true) settleCardsGrid(container) else refreshCardsGrid()
|
||||
}
|
||||
override fun onViewDetachedFromWindow(view: View) {
|
||||
cardsDozing[container] = false
|
||||
cardsReadyAt.remove(container)
|
||||
cardsLocations.remove(container)
|
||||
cardsRenderer.release(container)
|
||||
}
|
||||
})
|
||||
container.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> refreshCardsGrid() }
|
||||
container.addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
|
||||
if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
|
||||
if (cardsDozing[container] == true) settleCardsGrid(container) else refreshCardsGrid()
|
||||
}
|
||||
}
|
||||
refreshCardsGrid()
|
||||
}
|
||||
|
||||
private fun settleCardsGrid(container: ViewGroup, delay: Long = LAYOUT_SETTLE_DELAY) {
|
||||
cardsRenderer.deactivate(container)
|
||||
cardsReadyAt[container] = SystemClock.uptimeMillis() + delay
|
||||
refreshCardsGrid()
|
||||
}
|
||||
|
||||
private fun refreshCardsGrid() {
|
||||
if (cardsRefreshPending) return
|
||||
cardsRefreshPending = true
|
||||
mainHandler.post {
|
||||
cardsRefreshPending = false
|
||||
val notifications = visibleNotifications.distinctBy(StatusBarNotification::getKey)
|
||||
val config = OneUiCardsGridRenderer.Config(
|
||||
ProcessVisibilityPolicyCache.aodCards,
|
||||
showPills = false,
|
||||
hideShelf = false,
|
||||
)
|
||||
synchronized(cardsContainers) { cardsContainers.toList() }.forEach { container ->
|
||||
val tint = runCatching {
|
||||
container.javaClass.getMethod("getIconColor").invoke(container) as Int
|
||||
}.getOrNull()
|
||||
val sources = notifications.map { sbn ->
|
||||
val icon = sbn.notification.smallIcon
|
||||
OneUiCardsGridRenderer.Source(
|
||||
sbn.key,
|
||||
view = null,
|
||||
icon,
|
||||
sbn.packageName,
|
||||
tint.takeUnless {
|
||||
icon.type == Icon.TYPE_BITMAP || icon.type == Icon.TYPE_ADAPTIVE_BITMAP
|
||||
},
|
||||
)
|
||||
}
|
||||
cardsRenderer.updateSources(container, sources)
|
||||
cardsRenderer.refresh(
|
||||
container,
|
||||
container.isAttachedToWindow && cardsDozing[container] == true && isCardsMode(container),
|
||||
emptySet(),
|
||||
config,
|
||||
cardsRefreshTask?.let(mainHandler::removeCallbacks)
|
||||
val now = SystemClock.uptimeMillis()
|
||||
val delay = synchronized(cardsReadyAt) {
|
||||
cardsReadyAt.entries
|
||||
.filter { cardsDozing[it.key] == true }
|
||||
.maxOfOrNull { (it.value - now).coerceAtLeast(0) } ?: 0
|
||||
}
|
||||
cardsRefreshTask = Runnable {
|
||||
cardsRefreshTask = null
|
||||
renderCardsGrid()
|
||||
}.also { mainHandler.postDelayed(it, delay) }
|
||||
}
|
||||
|
||||
private fun renderCardsGrid() {
|
||||
val moving = synchronized(cardsContainers) { cardsContainers.toList() }.filter { container ->
|
||||
if (cardsDozing[container] != true) return@filter false
|
||||
val location = screenLocation(container)
|
||||
val previous = cardsLocations.put(container, location)
|
||||
previous == null || previous != location
|
||||
}
|
||||
if (moving.isNotEmpty()) {
|
||||
moving.forEach { settleCardsGrid(it) }
|
||||
return
|
||||
}
|
||||
val notifications = visibleNotifications.distinctBy(StatusBarNotification::getKey)
|
||||
val config = OneUiCardsGridRenderer.Config(
|
||||
ProcessVisibilityPolicyCache.aodCards,
|
||||
showPills = false,
|
||||
hideShelf = false,
|
||||
)
|
||||
synchronized(cardsContainers) { cardsContainers.toList() }.forEach { container ->
|
||||
val tint = runCatching {
|
||||
container.javaClass.getMethod("getIconColor").invoke(container) as Int
|
||||
}.getOrNull()
|
||||
val sources = notifications.map { sbn ->
|
||||
val icon = sbn.notification.smallIcon
|
||||
OneUiCardsGridRenderer.Source(
|
||||
sbn.key,
|
||||
view = null,
|
||||
icon,
|
||||
sbn.packageName,
|
||||
tint.takeUnless {
|
||||
icon.type == Icon.TYPE_BITMAP || icon.type == Icon.TYPE_ADAPTIVE_BITMAP
|
||||
},
|
||||
)
|
||||
}
|
||||
cardsRenderer.updateSources(container, sources)
|
||||
cardsRenderer.refresh(
|
||||
container,
|
||||
container.isAttachedToWindow && cardsDozing[container] == true && isCardsMode(container),
|
||||
emptySet(),
|
||||
config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun screenLocation(view: View): Pair<Int, Int> {
|
||||
val location = IntArray(2)
|
||||
view.getLocationOnScreen(location)
|
||||
return location[0] to location[1]
|
||||
}
|
||||
|
||||
private fun isCardsMode(view: View) = runCatching {
|
||||
Settings.System.getInt(view.context.contentResolver, NOTIFICATION_STYLE) == CARDS
|
||||
}.getOrDefault(false)
|
||||
@@ -239,6 +291,8 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
||||
private companion object {
|
||||
const val TAG = "NotificationsMaster"
|
||||
const val CARDS = 0
|
||||
const val LAYOUT_SETTLE_DELAY = 120L
|
||||
const val DOZE_SETTLE_DELAY = 220L
|
||||
const val NOTIFICATION_STYLE = "lockscreen_minimizing_notification"
|
||||
const val MANAGER = "com.samsung.android.uniform.plugins.notification.AODNotificationManager"
|
||||
const val AOD_ICON_CONTAINER =
|
||||
|
||||
Reference in New Issue
Block a user