Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f28d265a authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Fix a bug that prevented notification reinflate on user switch." into tm-qpr-dev

parents 2b13efde 85713436
Loading
Loading
Loading
Loading
+53 −7
Original line number Diff line number Diff line
@@ -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

/**
@@ -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
@@ -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
@@ -74,7 +111,7 @@ class ViewConfigCoordinator @Inject internal constructor(
        onDensityOrFontScaleChanged()
    }

    override fun onUserChanged(userId: Int) {
    private fun applyChangesOnUserSwitched() {
        if (mReinflateNotificationsOnUserSwitched) {
            updateNotificationsOnDensityOrFontScaleChanged()
            mReinflateNotificationsOnUserSwitched = false
@@ -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()
        }
    }

@@ -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
+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)
    }
}