Test Cards grid rendering lifecycle

This commit is contained in:
ajp_anton
2026-08-29 23:21:05 +00:00
parent 179dc3a1db
commit 626776886a
4 changed files with 110 additions and 2 deletions
@@ -0,0 +1,67 @@
package se.ajpanton.notificationsmaster.module
import android.content.Intent
import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import se.ajpanton.notificationsmaster.visibility.CardsGridSettings
@RunWith(AndroidJUnit4::class)
class OneUiCardsGridRendererTest {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
@Test fun hidesStockOnlyAfterRenderingAndRestoresIt() {
val activity = instrumentation.startActivitySync(
Intent(instrumentation.targetContext, GridTestActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
)
val renderer = OneUiCardsGridRenderer()
lateinit var root: ViewGroup
lateinit var source: FrameLayout
instrumentation.runOnMainSync {
root = activity.window.decorView as ViewGroup
source = FrameLayout(activity)
root.addView(source, ViewGroup.LayoutParams(300, 80))
renderer.updateSources(source, listOf(OneUiCardsGridRenderer.Source(
"test-key",
view = null,
Icon.createWithResource(activity, android.R.drawable.ic_dialog_info),
)))
renderer.refresh(source, active = true, emptySet(), CONFIG)
}
instrumentation.waitForIdleSync()
instrumentation.runOnMainSync {
assertEquals(0f, source.alpha)
val host = root.findViewWithTag<ViewGroup>("notifications-master:cards-grid")
assertEquals(View.VISIBLE, host.visibility)
assertEquals(1, host.childCount)
renderer.refresh(source, active = false, emptySet(), CONFIG)
}
instrumentation.waitForIdleSync()
instrumentation.runOnMainSync {
assertEquals(1f, source.alpha)
renderer.release(source)
assertNull(root.findViewWithTag<View>("notifications-master:cards-grid"))
root.removeView(source)
activity.finish()
}
}
private companion object {
val CONFIG = OneUiCardsGridRenderer.Config(
CardsGridSettings(1, 5, true, true, 60),
showPills = false,
hideShelf = false,
)
}
}
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name="se.ajpanton.notificationsmaster.module.GridTestActivity"
android:exported="false" />
</application>
</manifest>
@@ -0,0 +1,12 @@
package se.ajpanton.notificationsmaster.module
import android.app.Activity
import android.os.Bundle
import android.widget.FrameLayout
class GridTestActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(FrameLayout(this))
}
}
@@ -46,6 +46,17 @@ internal class OneUiCardsGridRenderer {
}
}
fun release(container: ViewGroup) {
val state = states.remove(container) ?: return
restoreNative(state)
state.attachListener?.let(container::removeOnAttachStateChangeListener)
state.layoutListener?.let { state.root?.removeOnLayoutChangeListener(it) }
state.host?.let { host ->
clearHost(host)
(host.parent as? ViewGroup)?.removeView(host)
}
}
private fun render(container: ViewGroup, state: State, active: Boolean) {
if (!active || !container.isAttachedToWindow) {
failOpen(state)
@@ -139,13 +150,14 @@ internal class OneUiCardsGridRenderer {
}
private fun notificationIcon(root: View, source: Source, size: Int): ImageView? {
val drawable = findDrawable(source.view)
var drawable = findDrawable(source.view)
?: runCatching {
val context = source.packageName?.let { root.context.createPackageContext(it, 0) }
?: root.context
source.smallIcon?.loadDrawable(context)
}.getOrNull()
?: return null
source.tintColor?.let { color -> drawable = drawable.mutate().apply { setTint(color) } }
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
val bounds = Rect(drawable.bounds)
try {
@@ -271,13 +283,21 @@ internal class OneUiCardsGridRenderer {
private fun dp(view: View, value: Int) = (value * view.resources.displayMetrics.density).toInt()
private fun state(container: ViewGroup) = states.getOrPut(container, ::State)
private fun state(container: ViewGroup) = states.getOrPut(container) {
State().also { state ->
state.attachListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(view: View) = Unit
override fun onViewDetachedFromWindow(view: View) = release(container)
}.also(container::addOnAttachStateChangeListener)
}
}
data class Source(
val key: String,
val view: View?,
val smallIcon: android.graphics.drawable.Icon?,
val packageName: String? = null,
val tintColor: Int? = null,
)
data class Config(
@@ -292,6 +312,7 @@ internal class OneUiCardsGridRenderer {
var host: FrameLayout? = null
var root: ViewGroup? = null
var layoutListener: View.OnLayoutChangeListener? = null
var attachListener: View.OnAttachStateChangeListener? = null
var active = false
var nativeSurface: View? = null
var nativeAlpha = 1f