diff options
3 files changed, 100 insertions, 78 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt index 7bca86e2e8fb..12be32c54b22 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/ConfigurationState.kt @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.policy.onThemeChanged import com.android.systemui.util.kotlin.emitOnStart import com.android.systemui.util.view.bindLatest import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge @@ -95,7 +97,8 @@ constructor( * call [onInflate] on the resulting view each time. Disposes of the [DisposableHandle] returned by * [onInflate] when done. * - * This never completes unless cancelled, it just suspends and waits for updates. + * This never completes unless cancelled, it just suspends and waits for updates. It runs on a + * background thread using [backgroundDispatcher]. * * For parameters [resource], [root] and [attachToRoot], see [LayoutInflater.inflate]. * @@ -105,7 +108,7 @@ constructor( * ``` * parentView.repeatWhenAttached { * configurationState - * .reinflateOnChange( + * .reinflateAndBindLatest( * R.layout.my_layout, * parentView, * attachToRoot = false, @@ -124,7 +127,10 @@ suspend fun <T : View> ConfigurationState.reinflateAndBindLatest( @LayoutRes resource: Int, root: ViewGroup?, attachToRoot: Boolean, + backgroundDispatcher: CoroutineDispatcher, onInflate: (T) -> DisposableHandle?, ) { - inflateLayout<T>(resource, root, attachToRoot).bindLatest(onInflate) + inflateLayout<T>(resource, root, attachToRoot) + .flowOn(backgroundDispatcher) + .bindLatest(onInflate) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index 4554085c35c0..fdda1205c84c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -24,6 +24,7 @@ import com.android.internal.logging.nano.MetricsProto import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.common.ui.reinflateAndBindLatest import com.android.systemui.common.ui.view.setImportantForAccessibilityYesNo +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R @@ -40,6 +41,7 @@ import com.android.systemui.statusbar.notification.stack.ui.viewmodel.Notificati import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.policy.ConfigurationController import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch @@ -48,12 +50,13 @@ class NotificationListViewBinder @Inject constructor( private val viewModel: NotificationListViewModel, - private val metricsLogger: MetricsLogger, + @Background private val backgroundDispatcher: CoroutineDispatcher, private val configuration: ConfigurationState, private val configurationController: ConfigurationController, private val falsingManager: FalsingManager, private val iconAreaController: NotificationIconAreaController, private val iconViewBindingFailureTracker: StatusBarIconViewBindingFailureTracker, + private val metricsLogger: MetricsLogger, private val shelfIconViewStore: ShelfNotificationIconViewStore, ) { @@ -101,6 +104,7 @@ constructor( R.layout.status_bar_notification_footer, parentView, attachToRoot = false, + backgroundDispatcher, ) { footerView: FooterView -> traceSection("bind FooterView") { val disposableHandle = diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt index 034b8022305a..112cec25784c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/common/ui/ConfigurationStateTest.kt @@ -30,6 +30,8 @@ import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -44,102 +46,112 @@ class ConfigurationStateTest : SysuiTestCase() { private val configurationController: ConfigurationController = mock() private val layoutInflater = TestLayoutInflater() + private val backgroundDispatcher = StandardTestDispatcher() + private val testScope = TestScope(backgroundDispatcher) val underTest = ConfigurationState(configurationController, context, layoutInflater) @Test - fun reinflateAndBindLatest_inflatesWithoutEmission() = runTest { - var callbackCount = 0 - backgroundScope.launch { - underTest.reinflateAndBindLatest<View>( - resource = 0, - root = null, - attachToRoot = false, - ) { - callbackCount++ - null + fun reinflateAndBindLatest_inflatesWithoutEmission() = + testScope.runTest { + var callbackCount = 0 + backgroundScope.launch { + underTest.reinflateAndBindLatest<View>( + resource = 0, + root = null, + attachToRoot = false, + backgroundDispatcher, + ) { + callbackCount++ + null + } } - } - // Inflates without an emission - runCurrent() - assertThat(layoutInflater.inflationCount).isEqualTo(1) - assertThat(callbackCount).isEqualTo(1) - } + // Inflates without an emission + runCurrent() + assertThat(layoutInflater.inflationCount).isEqualTo(1) + assertThat(callbackCount).isEqualTo(1) + } @Test - fun reinflateAndBindLatest_reinflatesOnThemeChanged() = runTest { - var callbackCount = 0 - backgroundScope.launch { - underTest.reinflateAndBindLatest<View>( - resource = 0, - root = null, - attachToRoot = false, - ) { - callbackCount++ - null + fun reinflateAndBindLatest_reinflatesOnThemeChanged() = + testScope.runTest { + var callbackCount = 0 + backgroundScope.launch { + underTest.reinflateAndBindLatest<View>( + resource = 0, + root = null, + attachToRoot = false, + backgroundDispatcher, + ) { + callbackCount++ + null + } } - } - runCurrent() + runCurrent() - val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany { - verify(configurationController, atLeastOnce()).addCallback(capture()) - } + val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany { + verify(configurationController, atLeastOnce()).addCallback(capture()) + } - listOf(1, 2, 3).forEach { count -> - assertThat(layoutInflater.inflationCount).isEqualTo(count) - assertThat(callbackCount).isEqualTo(count) - configListeners.forEach { it.onThemeChanged() } - runCurrent() + listOf(1, 2, 3).forEach { count -> + assertThat(layoutInflater.inflationCount).isEqualTo(count) + assertThat(callbackCount).isEqualTo(count) + configListeners.forEach { it.onThemeChanged() } + runCurrent() + } } - } @Test - fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() = runTest { - var callbackCount = 0 - backgroundScope.launch { - underTest.reinflateAndBindLatest<View>( - resource = 0, - root = null, - attachToRoot = false, - ) { - callbackCount++ - null + fun reinflateAndBindLatest_reinflatesOnDensityOrFontScaleChanged() = + testScope.runTest { + var callbackCount = 0 + backgroundScope.launch { + underTest.reinflateAndBindLatest<View>( + resource = 0, + root = null, + attachToRoot = false, + backgroundDispatcher, + ) { + callbackCount++ + null + } } - } - runCurrent() + runCurrent() - val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany { - verify(configurationController, atLeastOnce()).addCallback(capture()) - } + val configListeners: List<ConfigurationController.ConfigurationListener> = captureMany { + verify(configurationController, atLeastOnce()).addCallback(capture()) + } - listOf(1, 2, 3).forEach { count -> - assertThat(layoutInflater.inflationCount).isEqualTo(count) - assertThat(callbackCount).isEqualTo(count) - configListeners.forEach { it.onDensityOrFontScaleChanged() } - runCurrent() + listOf(1, 2, 3).forEach { count -> + assertThat(layoutInflater.inflationCount).isEqualTo(count) + assertThat(callbackCount).isEqualTo(count) + configListeners.forEach { it.onDensityOrFontScaleChanged() } + runCurrent() + } } - } @Test - fun testReinflateAndBindLatest_disposesOnCancel() = runTest { - var callbackCount = 0 - var disposed = false - val job = launch { - underTest.reinflateAndBindLatest<View>( - resource = 0, - root = null, - attachToRoot = false, - ) { - callbackCount++ - DisposableHandle { disposed = true } + fun testReinflateAndBindLatest_disposesOnCancel() = + testScope.runTest { + var callbackCount = 0 + var disposed = false + val job = launch { + underTest.reinflateAndBindLatest<View>( + resource = 0, + root = null, + attachToRoot = false, + backgroundDispatcher, + ) { + callbackCount++ + DisposableHandle { disposed = true } + } } - } - runCurrent() - job.cancelAndJoin() - assertThat(disposed).isTrue() - } + runCurrent() + job.cancelAndJoin() + assertThat(disposed).isTrue() + } inner class TestLayoutInflater : LayoutInflater(context) { |