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) {
|
fun release(container: ViewGroup) {
|
||||||
val state = states.remove(container) ?: return
|
val state = states.remove(container) ?: return
|
||||||
restoreNative(state)
|
restoreNative(state)
|
||||||
|
|||||||
+5
@@ -84,6 +84,10 @@ internal class OneUiLockscreenCardsBackend(
|
|||||||
}.forEach { method ->
|
}.forEach { method ->
|
||||||
method.isAccessible = true
|
method.isAccessible = true
|
||||||
framework.hook(method).intercept { chain ->
|
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()
|
val result = chain.proceed()
|
||||||
updateState(chain.thisObject)
|
updateState(chain.thisObject)
|
||||||
result
|
result
|
||||||
@@ -206,6 +210,7 @@ internal class OneUiLockscreenCardsBackend(
|
|||||||
statusBarStateKnown = true
|
statusBarStateKnown = true
|
||||||
}
|
}
|
||||||
(findField(controller.javaClass, "mIsDozing")?.get(controller) as? Boolean)?.let { dozing = it }
|
(findField(controller.javaClass, "mIsDozing")?.get(controller) as? Boolean)?.let { dozing = it }
|
||||||
|
if (dozing || statusBarState != KEYGUARD) gridRenderer.deactivateAll()
|
||||||
applyTrackedStacks()
|
applyTrackedStacks()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package se.ajpanton.notificationsmaster.module
|
|||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import android.os.SystemClock
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.service.notification.StatusBarNotification
|
import android.service.notification.StatusBarNotification
|
||||||
import android.util.Log
|
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 managers = Collections.synchronizedMap(WeakHashMap<Any, State>())
|
||||||
private val cardsContainers = Collections.newSetFromMap(WeakHashMap<ViewGroup, Boolean>())
|
private val cardsContainers = Collections.newSetFromMap(WeakHashMap<ViewGroup, Boolean>())
|
||||||
private val cardsDozing = Collections.synchronizedMap(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 cardsRenderer = OneUiCardsGridRenderer()
|
||||||
private val mainHandler = Handler(Looper.getMainLooper())
|
private val mainHandler = Handler(Looper.getMainLooper())
|
||||||
private val refreshing = ThreadLocal<Boolean>()
|
private val refreshing = ThreadLocal<Boolean>()
|
||||||
@@ -26,7 +29,7 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
|||||||
private lateinit var updateVisible: Method
|
private lateinit var updateVisible: Method
|
||||||
@Volatile private var blockedObserved = false
|
@Volatile private var blockedObserved = false
|
||||||
@Volatile private var visibleNotifications = emptyList<StatusBarNotification>()
|
@Volatile private var visibleNotifications = emptyList<StatusBarNotification>()
|
||||||
@Volatile private var cardsRefreshPending = false
|
private var cardsRefreshTask: Runnable? = null
|
||||||
|
|
||||||
override fun install(classLoader: ClassLoader): Boolean = runCatching {
|
override fun install(classLoader: ClassLoader): Boolean = runCatching {
|
||||||
val manager = Class.forName(MANAGER, false, classLoader)
|
val manager = Class.forName(MANAGER, false, classLoader)
|
||||||
@@ -102,12 +105,22 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
|||||||
}.forEach { method ->
|
}.forEach { method ->
|
||||||
method.isAccessible = true
|
method.isAccessible = true
|
||||||
framework.hook(method).intercept { chain ->
|
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()
|
val result = chain.proceed()
|
||||||
(chain.thisObject as? ViewGroup)?.let { view ->
|
container?.let { view ->
|
||||||
trackCardsContainer(view)
|
cardsDozing[view] = dozing
|
||||||
cardsDozing[view] = chain.args.firstOrNull() == true
|
if (dozing) settleCardsGrid(view, DOZE_SETTLE_DELAY) else {
|
||||||
|
cardsReadyAt.remove(view)
|
||||||
|
cardsLocations.remove(view)
|
||||||
refreshCardsGrid()
|
refreshCardsGrid()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,21 +145,55 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
|||||||
if (!cardsContainers.add(container)) return
|
if (!cardsContainers.add(container)) return
|
||||||
}
|
}
|
||||||
container.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
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) {
|
override fun onViewDetachedFromWindow(view: View) {
|
||||||
cardsDozing[container] = false
|
cardsDozing[container] = false
|
||||||
|
cardsReadyAt.remove(container)
|
||||||
|
cardsLocations.remove(container)
|
||||||
cardsRenderer.release(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()
|
refreshCardsGrid()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshCardsGrid() {
|
private fun refreshCardsGrid() {
|
||||||
if (cardsRefreshPending) return
|
cardsRefreshTask?.let(mainHandler::removeCallbacks)
|
||||||
cardsRefreshPending = true
|
val now = SystemClock.uptimeMillis()
|
||||||
mainHandler.post {
|
val delay = synchronized(cardsReadyAt) {
|
||||||
cardsRefreshPending = false
|
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 notifications = visibleNotifications.distinctBy(StatusBarNotification::getKey)
|
||||||
val config = OneUiCardsGridRenderer.Config(
|
val config = OneUiCardsGridRenderer.Config(
|
||||||
ProcessVisibilityPolicyCache.aodCards,
|
ProcessVisibilityPolicyCache.aodCards,
|
||||||
@@ -178,6 +225,11 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
private fun isCardsMode(view: View) = runCatching {
|
||||||
@@ -239,6 +291,8 @@ internal class SamsungAodBackend(private val framework: XposedInterface) : Visib
|
|||||||
private companion object {
|
private companion object {
|
||||||
const val TAG = "NotificationsMaster"
|
const val TAG = "NotificationsMaster"
|
||||||
const val CARDS = 0
|
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 NOTIFICATION_STYLE = "lockscreen_minimizing_notification"
|
||||||
const val MANAGER = "com.samsung.android.uniform.plugins.notification.AODNotificationManager"
|
const val MANAGER = "com.samsung.android.uniform.plugins.notification.AODNotificationManager"
|
||||||
const val AOD_ICON_CONTAINER =
|
const val AOD_ICON_CONTAINER =
|
||||||
|
|||||||
Reference in New Issue
Block a user