Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +53 −7 Original line number Diff line number Diff line Loading @@ -16,15 +16,18 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.util.Log import com.android.internal.widget.MessagingGroup import com.android.internal.widget.MessagingMessage import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Compile import javax.inject.Inject /** Loading @@ -38,24 +41,52 @@ class ViewConfigCoordinator @Inject internal constructor( private val mLockscreenUserManager: NotificationLockscreenUserManager, private val mGutsManager: NotificationGutsManager, private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor ) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener { ) : Coordinator, ConfigurationController.ConfigurationListener { private var mIsSwitchingUser = false private var mReinflateNotificationsOnUserSwitched = false private var mDispatchUiModeChangeOnUserSwitched = false private var mPipeline: NotifPipeline? = null private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() { override fun onUserSwitching(userId: Int) { log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" } mIsSwitchingUser = true } override fun onUserSwitchComplete(userId: Int) { log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" } mIsSwitchingUser = false applyChangesOnUserSwitched() } } private val mUserChangedListener = object : UserChangedListener { override fun onUserChanged(userId: Int) { log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" } applyChangesOnUserSwitched() } } override fun attach(pipeline: NotifPipeline) { mPipeline = pipeline if (pipeline.isNewPipelineEnabled) { mLockscreenUserManager.addUserChangedListener(this) mLockscreenUserManager.addUserChangedListener(mUserChangedListener) mConfigurationController.addCallback(this) mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback) } } override fun onDensityOrFontScaleChanged() { log { val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser "ViewConfigCoordinator.onDensityOrFontScaleChanged()" + " isSwitchingUser=$mIsSwitchingUser" + " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser" } MessagingMessage.dropCache() MessagingGroup.dropCache() if (!mKeyguardUpdateMonitor.isSwitchingUser) { if (!mIsSwitchingUser) { updateNotificationsOnDensityOrFontScaleChanged() } else { mReinflateNotificationsOnUserSwitched = true Loading @@ -63,7 +94,13 @@ class ViewConfigCoordinator @Inject internal constructor( } override fun onUiModeChanged() { if (!mKeyguardUpdateMonitor.isSwitchingUser) { log { val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser "ViewConfigCoordinator.onUiModeChanged()" + " isSwitchingUser=$mIsSwitchingUser" + " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser" } if (!mIsSwitchingUser) { updateNotificationsOnUiModeChanged() } else { mDispatchUiModeChangeOnUserSwitched = true Loading @@ -74,7 +111,7 @@ class ViewConfigCoordinator @Inject internal constructor( onDensityOrFontScaleChanged() } override fun onUserChanged(userId: Int) { private fun applyChangesOnUserSwitched() { if (mReinflateNotificationsOnUserSwitched) { updateNotificationsOnDensityOrFontScaleChanged() mReinflateNotificationsOnUserSwitched = false Loading @@ -86,9 +123,9 @@ class ViewConfigCoordinator @Inject internal constructor( } private fun updateNotificationsOnUiModeChanged() { log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" } mPipeline?.allNotifs?.forEach { entry -> val row = entry.row row?.onUiModeChanged() entry.row?.onUiModeChanged() } } Loading @@ -101,4 +138,13 @@ class ViewConfigCoordinator @Inject internal constructor( } } } private inline fun log(message: () -> String) { if (DEBUG) Log.d(TAG, message()) } companion object { private const val TAG = "ViewConfigCoordinator" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG) } } No newline at end of file packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt 0 → 100644 +198 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.collection.coordinator import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper class ViewConfigCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: ViewConfigCoordinator // Captured private lateinit var userChangedListener: UserChangedListener private lateinit var configurationListener: ConfigurationController.ConfigurationListener private lateinit var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback // Mocks private val entry: NotificationEntry = mock() private val row: ExpandableNotificationRow = mock() private val pipeline: NotifPipeline = mock() private val configurationController: ConfigurationController = mock() private val lockscreenUserManager: NotificationLockscreenUserManager = mock() private val gutsManager: NotificationGutsManager = mock() private val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock() @Before fun setUp() { whenever(pipeline.isNewPipelineEnabled).thenReturn(true) whenever(pipeline.allNotifs).thenReturn(listOf(entry)) whenever(entry.row).thenReturn(row) coordinator = ViewConfigCoordinator( configurationController, lockscreenUserManager, gutsManager, keyguardUpdateMonitor) coordinator.attach(pipeline) userChangedListener = withArgCaptor { verify(lockscreenUserManager).addUserChangedListener(capture()) } configurationListener = withArgCaptor { verify(configurationController).addCallback(capture()) } keyguardUpdateMonitorCallback = withArgCaptor { verify(keyguardUpdateMonitor).registerCallback(capture()) } } @Test fun uiModeChangePropagatesToRow() { configurationListener.onUiModeChanged() verify(entry).row verify(row).onUiModeChanged() verifyNoMoreInteractions(entry, row) } @Test fun themeChangePropagatesToEntry() { configurationListener.onThemeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) } @Test fun densityChangePropagatesToEntry() { configurationListener.onDensityOrFontScaleChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) } @Test fun switchingUserDefersChangesWithUserChangedEventAfter() { // GIVEN switching users keyguardUpdateMonitorCallback.onUserSwitching(10) // WHEN configuration changes happen configurationListener.onUiModeChanged() configurationListener.onDensityOrFontScaleChanged() configurationListener.onThemeChanged() // VERIFY no changes are propagated verifyNoMoreInteractions(entry, row) // WHEN user switch completes keyguardUpdateMonitorCallback.onUserSwitchComplete(10) // VERIFY the changes propagate verify(entry).row verify(row).onUiModeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) clearInvocations(entry, row) // WHEN user change happens after the switching window userChangedListener.onUserChanged(10) // VERIFY user change itself does not re-trigger updates verifyNoMoreInteractions(entry, row) } @Test fun switchingUserDefersChangesWithUserChangedEventDuring() { // GIVEN switching users keyguardUpdateMonitorCallback.onUserSwitching(10) // WHEN configuration changes happen configurationListener.onUiModeChanged() configurationListener.onDensityOrFontScaleChanged() configurationListener.onThemeChanged() // VERIFY no changes are propagated verifyNoMoreInteractions(entry, row) // WHEN user change happens during the switching window userChangedListener.onUserChanged(10) // VERIFY the changes propagate verify(entry).row verify(row).onUiModeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) clearInvocations(entry, row) // WHEN user switch completes keyguardUpdateMonitorCallback.onUserSwitchComplete(10) // VERIFY the switching window closing does not re-propagate verifyNoMoreInteractions(entry, row) } @Test fun switchingUserDefersChangesWithUserChangedEventBefore() { // WHEN user change happens before configuration changes or switching window userChangedListener.onUserChanged(10) // VERIFY no changes happen verifyNoMoreInteractions(entry, row) // WHEN switching users then configuration changes happen keyguardUpdateMonitorCallback.onUserSwitching(10) configurationListener.onUiModeChanged() configurationListener.onDensityOrFontScaleChanged() configurationListener.onThemeChanged() // VERIFY no changes happen verifyNoMoreInteractions(entry, row) // WHEN user switch completes keyguardUpdateMonitorCallback.onUserSwitchComplete(10) // VERIFY the changes propagate verify(entry).row verify(row).onUiModeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) clearInvocations(entry, row) } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +53 −7 Original line number Diff line number Diff line Loading @@ -16,15 +16,18 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.util.Log import com.android.internal.widget.MessagingGroup import com.android.internal.widget.MessagingMessage import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.Compile import javax.inject.Inject /** Loading @@ -38,24 +41,52 @@ class ViewConfigCoordinator @Inject internal constructor( private val mLockscreenUserManager: NotificationLockscreenUserManager, private val mGutsManager: NotificationGutsManager, private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor ) : Coordinator, UserChangedListener, ConfigurationController.ConfigurationListener { ) : Coordinator, ConfigurationController.ConfigurationListener { private var mIsSwitchingUser = false private var mReinflateNotificationsOnUserSwitched = false private var mDispatchUiModeChangeOnUserSwitched = false private var mPipeline: NotifPipeline? = null private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() { override fun onUserSwitching(userId: Int) { log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" } mIsSwitchingUser = true } override fun onUserSwitchComplete(userId: Int) { log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" } mIsSwitchingUser = false applyChangesOnUserSwitched() } } private val mUserChangedListener = object : UserChangedListener { override fun onUserChanged(userId: Int) { log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" } applyChangesOnUserSwitched() } } override fun attach(pipeline: NotifPipeline) { mPipeline = pipeline if (pipeline.isNewPipelineEnabled) { mLockscreenUserManager.addUserChangedListener(this) mLockscreenUserManager.addUserChangedListener(mUserChangedListener) mConfigurationController.addCallback(this) mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback) } } override fun onDensityOrFontScaleChanged() { log { val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser "ViewConfigCoordinator.onDensityOrFontScaleChanged()" + " isSwitchingUser=$mIsSwitchingUser" + " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser" } MessagingMessage.dropCache() MessagingGroup.dropCache() if (!mKeyguardUpdateMonitor.isSwitchingUser) { if (!mIsSwitchingUser) { updateNotificationsOnDensityOrFontScaleChanged() } else { mReinflateNotificationsOnUserSwitched = true Loading @@ -63,7 +94,13 @@ class ViewConfigCoordinator @Inject internal constructor( } override fun onUiModeChanged() { if (!mKeyguardUpdateMonitor.isSwitchingUser) { log { val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser "ViewConfigCoordinator.onUiModeChanged()" + " isSwitchingUser=$mIsSwitchingUser" + " keyguardUpdateMonitor.isSwitchingUser=$keyguardIsSwitchingUser" } if (!mIsSwitchingUser) { updateNotificationsOnUiModeChanged() } else { mDispatchUiModeChangeOnUserSwitched = true Loading @@ -74,7 +111,7 @@ class ViewConfigCoordinator @Inject internal constructor( onDensityOrFontScaleChanged() } override fun onUserChanged(userId: Int) { private fun applyChangesOnUserSwitched() { if (mReinflateNotificationsOnUserSwitched) { updateNotificationsOnDensityOrFontScaleChanged() mReinflateNotificationsOnUserSwitched = false Loading @@ -86,9 +123,9 @@ class ViewConfigCoordinator @Inject internal constructor( } private fun updateNotificationsOnUiModeChanged() { log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" } mPipeline?.allNotifs?.forEach { entry -> val row = entry.row row?.onUiModeChanged() entry.row?.onUiModeChanged() } } Loading @@ -101,4 +138,13 @@ class ViewConfigCoordinator @Inject internal constructor( } } } private inline fun log(message: () -> String) { if (DEBUG) Log.d(TAG, message()) } companion object { private const val TAG = "ViewConfigCoordinator" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG) } } No newline at end of file
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt 0 → 100644 +198 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar.notification.collection.coordinator import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationGutsManager import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper class ViewConfigCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: ViewConfigCoordinator // Captured private lateinit var userChangedListener: UserChangedListener private lateinit var configurationListener: ConfigurationController.ConfigurationListener private lateinit var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback // Mocks private val entry: NotificationEntry = mock() private val row: ExpandableNotificationRow = mock() private val pipeline: NotifPipeline = mock() private val configurationController: ConfigurationController = mock() private val lockscreenUserManager: NotificationLockscreenUserManager = mock() private val gutsManager: NotificationGutsManager = mock() private val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock() @Before fun setUp() { whenever(pipeline.isNewPipelineEnabled).thenReturn(true) whenever(pipeline.allNotifs).thenReturn(listOf(entry)) whenever(entry.row).thenReturn(row) coordinator = ViewConfigCoordinator( configurationController, lockscreenUserManager, gutsManager, keyguardUpdateMonitor) coordinator.attach(pipeline) userChangedListener = withArgCaptor { verify(lockscreenUserManager).addUserChangedListener(capture()) } configurationListener = withArgCaptor { verify(configurationController).addCallback(capture()) } keyguardUpdateMonitorCallback = withArgCaptor { verify(keyguardUpdateMonitor).registerCallback(capture()) } } @Test fun uiModeChangePropagatesToRow() { configurationListener.onUiModeChanged() verify(entry).row verify(row).onUiModeChanged() verifyNoMoreInteractions(entry, row) } @Test fun themeChangePropagatesToEntry() { configurationListener.onThemeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) } @Test fun densityChangePropagatesToEntry() { configurationListener.onDensityOrFontScaleChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) } @Test fun switchingUserDefersChangesWithUserChangedEventAfter() { // GIVEN switching users keyguardUpdateMonitorCallback.onUserSwitching(10) // WHEN configuration changes happen configurationListener.onUiModeChanged() configurationListener.onDensityOrFontScaleChanged() configurationListener.onThemeChanged() // VERIFY no changes are propagated verifyNoMoreInteractions(entry, row) // WHEN user switch completes keyguardUpdateMonitorCallback.onUserSwitchComplete(10) // VERIFY the changes propagate verify(entry).row verify(row).onUiModeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) clearInvocations(entry, row) // WHEN user change happens after the switching window userChangedListener.onUserChanged(10) // VERIFY user change itself does not re-trigger updates verifyNoMoreInteractions(entry, row) } @Test fun switchingUserDefersChangesWithUserChangedEventDuring() { // GIVEN switching users keyguardUpdateMonitorCallback.onUserSwitching(10) // WHEN configuration changes happen configurationListener.onUiModeChanged() configurationListener.onDensityOrFontScaleChanged() configurationListener.onThemeChanged() // VERIFY no changes are propagated verifyNoMoreInteractions(entry, row) // WHEN user change happens during the switching window userChangedListener.onUserChanged(10) // VERIFY the changes propagate verify(entry).row verify(row).onUiModeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) clearInvocations(entry, row) // WHEN user switch completes keyguardUpdateMonitorCallback.onUserSwitchComplete(10) // VERIFY the switching window closing does not re-propagate verifyNoMoreInteractions(entry, row) } @Test fun switchingUserDefersChangesWithUserChangedEventBefore() { // WHEN user change happens before configuration changes or switching window userChangedListener.onUserChanged(10) // VERIFY no changes happen verifyNoMoreInteractions(entry, row) // WHEN switching users then configuration changes happen keyguardUpdateMonitorCallback.onUserSwitching(10) configurationListener.onUiModeChanged() configurationListener.onDensityOrFontScaleChanged() configurationListener.onThemeChanged() // VERIFY no changes happen verifyNoMoreInteractions(entry, row) // WHEN user switch completes keyguardUpdateMonitorCallback.onUserSwitchComplete(10) // VERIFY the changes propagate verify(entry).row verify(row).onUiModeChanged() verify(entry).onDensityOrFontScaleChanged() verify(entry).areGutsExposed() verifyNoMoreInteractions(entry, row) clearInvocations(entry, row) } }