Fix OOM of metro clock
Test: use ahat to see heap dump, atest ClockEventControllerTest Bug: 287689742 Change-Id: I0bffb664bbe99ea67d5150846cc7bbd52230068e
This commit is contained in:
@@ -26,6 +26,7 @@ import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.View.OnAttachStateChangeListener
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -88,6 +89,15 @@ constructor(
|
||||
) {
|
||||
var clock: ClockController? = null
|
||||
set(value) {
|
||||
smallClockOnAttachStateChangeListener?.let {
|
||||
field?.smallClock?.view?.removeOnAttachStateChangeListener(it)
|
||||
smallClockFrame?.viewTreeObserver
|
||||
?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
|
||||
}
|
||||
largeClockOnAttachStateChangeListener?.let {
|
||||
field?.largeClock?.view?.removeOnAttachStateChangeListener(it)
|
||||
}
|
||||
|
||||
field = value
|
||||
if (value != null) {
|
||||
smallLogBuffer?.log(TAG, DEBUG, {}, { "New Clock" })
|
||||
@@ -130,44 +140,62 @@ constructor(
|
||||
}
|
||||
value.events.onWeatherDataChanged(it)
|
||||
}
|
||||
value.smallClock.view.addOnAttachStateChangeListener(
|
||||
|
||||
smallClockOnAttachStateChangeListener =
|
||||
object : OnAttachStateChangeListener {
|
||||
var pastVisibility: Int? = null
|
||||
override fun onViewAttachedToWindow(view: View?) {
|
||||
value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
|
||||
if (view != null) {
|
||||
val smallClockFrame = view.parent as FrameLayout
|
||||
pastVisibility = smallClockFrame.visibility
|
||||
smallClockFrame.viewTreeObserver.addOnGlobalLayoutListener(
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
val currentVisibility = smallClockFrame.visibility
|
||||
if (pastVisibility != currentVisibility) {
|
||||
pastVisibility = currentVisibility
|
||||
// when small clock visible, recalculate bounds and sample
|
||||
if (currentVisibility == View.VISIBLE) {
|
||||
smallRegionSampler?.stopRegionSampler()
|
||||
smallRegionSampler?.startRegionSampler()
|
||||
smallClockFrame = view.parent as FrameLayout
|
||||
smallClockFrame?.let {frame ->
|
||||
pastVisibility = frame.visibility
|
||||
onGlobalLayoutListener = OnGlobalLayoutListener {
|
||||
val currentVisibility = frame.visibility
|
||||
if (pastVisibility != currentVisibility) {
|
||||
pastVisibility = currentVisibility
|
||||
// when small clock visible,
|
||||
// recalculate bounds and sample
|
||||
if (currentVisibility == View.VISIBLE) {
|
||||
smallRegionSampler?.stopRegionSampler()
|
||||
smallRegionSampler?.startRegionSampler()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
frame.viewTreeObserver
|
||||
.addOnGlobalLayoutListener(onGlobalLayoutListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(p0: View?) {
|
||||
smallClockFrame?.viewTreeObserver
|
||||
?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
|
||||
}
|
||||
})
|
||||
value.largeClock.view.addOnAttachStateChangeListener(
|
||||
}
|
||||
value.smallClock.view
|
||||
.addOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
|
||||
|
||||
largeClockOnAttachStateChangeListener =
|
||||
object : OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(p0: View?) {
|
||||
value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context))
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(p0: View?) {
|
||||
}
|
||||
})
|
||||
}
|
||||
value.largeClock.view
|
||||
.addOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
var smallClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null
|
||||
@VisibleForTesting
|
||||
var largeClockOnAttachStateChangeListener: OnAttachStateChangeListener? = null
|
||||
private var smallClockFrame: FrameLayout? = null
|
||||
private var onGlobalLayoutListener: OnGlobalLayoutListener? = null
|
||||
|
||||
private var isDozing = false
|
||||
private set
|
||||
|
||||
@@ -307,7 +335,6 @@ constructor(
|
||||
return
|
||||
}
|
||||
isRegistered = true
|
||||
|
||||
broadcastDispatcher.registerReceiver(
|
||||
localeBroadcastReceiver,
|
||||
IntentFilter(Intent.ACTION_LOCALE_CHANGED)
|
||||
@@ -346,6 +373,12 @@ constructor(
|
||||
largeRegionSampler?.stopRegionSampler()
|
||||
smallTimeListener?.stop()
|
||||
largeTimeListener?.stop()
|
||||
clock?.smallClock?.view
|
||||
?.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
|
||||
smallClockFrame?.viewTreeObserver
|
||||
?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
|
||||
clock?.largeClock?.view
|
||||
?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
|
||||
}
|
||||
|
||||
private fun updateTimeListeners() {
|
||||
|
||||
@@ -18,7 +18,8 @@ package com.android.keyguard
|
||||
import android.content.BroadcastReceiver
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.FrameLayout
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
|
||||
@@ -83,7 +84,13 @@ class ClockEventControllerTest : SysuiTestCase() {
|
||||
@Mock private lateinit var bgExecutor: Executor
|
||||
@Mock private lateinit var featureFlags: FeatureFlags
|
||||
@Mock private lateinit var smallClockController: ClockFaceController
|
||||
@Mock private lateinit var smallClockView: View
|
||||
@Mock private lateinit var smallClockViewTreeObserver: ViewTreeObserver
|
||||
@Mock private lateinit var smallClockFrame: FrameLayout
|
||||
@Mock private lateinit var smallClockFrameViewTreeObserver: ViewTreeObserver
|
||||
@Mock private lateinit var largeClockController: ClockFaceController
|
||||
@Mock private lateinit var largeClockView: View
|
||||
@Mock private lateinit var largeClockViewTreeObserver: ViewTreeObserver
|
||||
@Mock private lateinit var smallClockEvents: ClockFaceEvents
|
||||
@Mock private lateinit var largeClockEvents: ClockFaceEvents
|
||||
@Mock private lateinit var parentView: View
|
||||
@@ -99,8 +106,12 @@ class ClockEventControllerTest : SysuiTestCase() {
|
||||
fun setUp() {
|
||||
whenever(clock.smallClock).thenReturn(smallClockController)
|
||||
whenever(clock.largeClock).thenReturn(largeClockController)
|
||||
whenever(smallClockController.view).thenReturn(TextView(context))
|
||||
whenever(largeClockController.view).thenReturn(TextView(context))
|
||||
whenever(smallClockController.view).thenReturn(smallClockView)
|
||||
whenever(smallClockView.parent).thenReturn(smallClockFrame)
|
||||
whenever(smallClockView.viewTreeObserver).thenReturn(smallClockViewTreeObserver)
|
||||
whenever(smallClockFrame.viewTreeObserver).thenReturn(smallClockFrameViewTreeObserver)
|
||||
whenever(largeClockController.view).thenReturn(largeClockView)
|
||||
whenever(largeClockView.viewTreeObserver).thenReturn(largeClockViewTreeObserver)
|
||||
whenever(smallClockController.events).thenReturn(smallClockEvents)
|
||||
whenever(largeClockController.events).thenReturn(largeClockEvents)
|
||||
whenever(clock.events).thenReturn(events)
|
||||
@@ -302,8 +313,38 @@ class ClockEventControllerTest : SysuiTestCase() {
|
||||
verify(configurationController).removeCallback(any())
|
||||
verify(batteryController).removeCallback(any())
|
||||
verify(keyguardUpdateMonitor).removeCallback(any())
|
||||
verify(smallClockController.view)
|
||||
.removeOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
|
||||
verify(largeClockController.view)
|
||||
.removeOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerOnAttachStateChangeListener_validate() = runBlocking(IMMEDIATE) {
|
||||
verify(smallClockController.view)
|
||||
.addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
|
||||
verify(largeClockController.view)
|
||||
.addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerAndRemoveOnGlobalLayoutListener_correctly() = runBlocking(IMMEDIATE) {
|
||||
underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
|
||||
verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
|
||||
underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow(smallClockView)
|
||||
verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun registerOnGlobalLayoutListener_RemoveOnAttachStateChangeListener_correctly() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
underTest.smallClockOnAttachStateChangeListener!!
|
||||
.onViewAttachedToWindow(smallClockView)
|
||||
verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
|
||||
underTest.unregisterListeners()
|
||||
verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user