Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository import com.android.systemui.util.FakeEventLog import com.android.systemui.util.settings.SystemSettings import com.android.systemui.util.settings.fakeGlobalSettings Loading Loading @@ -142,6 +143,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected val settingsInteractor: NotificationSettingsInteractor = mock() protected val packageManager: PackageManager = mock() protected val notificationManager: NotificationManager = mock() protected val deviceProvisioningRepository = kosmos.fakeDeviceProvisioningRepository protected val logger: VisualInterruptionDecisionLogger = mock() protected abstract val provider: VisualInterruptionDecisionProvider Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt +3 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings Loading Loading @@ -64,6 +65,7 @@ object VisualInterruptionDecisionProviderTestUtil { context: Context, notificationManager: NotificationManager, settingsInteractor: NotificationSettingsInteractor, deviceProvisioningInteractor: DeviceProvisioningInteractor, ): VisualInterruptionDecisionProvider { return if (VisualInterruptionRefactor.isEnabled) { VisualInterruptionDecisionProviderImpl( Loading @@ -89,6 +91,7 @@ object VisualInterruptionDecisionProviderTestUtil { context, notificationManager, settingsInteractor, deviceProvisioningInteractor, ) } else { NotificationInterruptStateProviderWrapper( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt +98 −2 Original line number Diff line number Diff line Loading @@ -15,14 +15,19 @@ */ package com.android.systemui.statusbar.policy.data.repository import android.content.Context import android.content.applicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.util.mockito.whenever import com.android.systemui.testKosmos import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import java.time.Instant import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before Loading @@ -31,11 +36,14 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock @Mock lateinit var deviceProvisionedController: DeviceProvisionedController lateinit var underTest: DeviceProvisioningRepositoryImpl Loading @@ -45,6 +53,9 @@ class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) underTest = DeviceProvisioningRepositoryImpl( kosmos.applicationContext, testScope.backgroundScope, systemClock, deviceProvisionedController, ) } Loading Loading @@ -75,4 +86,89 @@ class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { .onDeviceProvisionedChanged() assertThat(deviceProvisioned).isFalse() } @Test fun getProvisionedTimestamp_provisionedEarlierWithoutTracking_isUnknown() = testScope.runTest { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) underTest.start() val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo(DeviceProvisioningRepository.ProvisionedTimestamp.Unknown) } @Test fun getProvisionedTimestamp_provisionedEarlierWithTracking_isProvisioningInstant() = testScope.runTest { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) kosmos.applicationContext .getSharedPreferences(context.packageName, Context.MODE_PRIVATE) .edit() .putLong(DeviceProvisioningRepositoryImpl.PREF_DEVICE_PROVISIONED_TIMESTAMP, 42L) .apply() underTest.start() val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo( DeviceProvisioningRepository.ProvisionedTimestamp.AtInstant( Instant.ofEpochMilli(42L) ) ) } @Test fun getProvisionedTimestamp_notProvisioned_isNotProvisioned() = testScope.runTest { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) underTest.start() val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo(DeviceProvisioningRepository.ProvisionedTimestamp.NotProvisioned) } @Test fun getProvisionedTimestamp_provisionedWhileTracking_isProvisioningInstant() = testScope.runTest { // Start not provisioned -> tracking systemClock.setCurrentTimeMillis(1L) whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) underTest.start() runCurrent() assertThat(underTest.getProvisionedTimestamp()) .isEqualTo(DeviceProvisioningRepository.ProvisionedTimestamp.NotProvisioned) // Now provisioning happens systemClock.setCurrentTimeMillis(2L) whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } .onDeviceProvisionedChanged() runCurrent() // A bit later, we query systemClock.setCurrentTimeMillis(3L) val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo( DeviceProvisioningRepository.ProvisionedTimestamp.AtInstant( Instant.ofEpochMilli(2L) ) ) // Also, we stored it for next SystemUI start assertThat( kosmos.applicationContext .getSharedPreferences(context.packageName, Context.MODE_PRIVATE) .getLong( DeviceProvisioningRepositoryImpl.PREF_DEVICE_PROVISIONED_TIMESTAMP, 0L, ) ) .isEqualTo(2L) } } packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +24 −5 Original line number Diff line number Diff line Loading @@ -59,11 +59,14 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.NotificationChannels import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings import com.android.systemui.util.time.SystemClock import com.android.wm.shell.bubbles.Bubbles import java.time.Duration import java.util.Optional import kotlin.jvm.optionals.getOrElse Loading Loading @@ -272,6 +275,8 @@ private const val FORCE_SHOW_AVALANCHE_EDU_ONCE = "persist.force_show_avalanche_ private const val PREF_HAS_SEEN_AVALANCHE_EDU = "has_seen_avalanche_edu" private val AVALANCHE_EDU_DELAY_AFTER_SUW: Duration = Duration.ofDays(1) class AvalancheSuppressor( private val avalancheProvider: AvalancheProvider, private val systemClock: SystemClock, Loading @@ -281,6 +286,7 @@ class AvalancheSuppressor( private val context: Context, private val notificationManager: NotificationManager, private val systemSettings: SystemSettings, private val deviceProvisioningInteractor: DeviceProvisioningInteractor, ) : VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "avalanche") { val TAG = "AvalancheSuppressor" Loading @@ -302,11 +308,6 @@ class AvalancheSuppressor( // with the real settings value. private var isCooldownFlowInSync = false private fun shouldShowEdu(): Boolean { val forceShowOnce = SystemProperties.get(FORCE_SHOW_AVALANCHE_EDU_ONCE, "").equals("1") return !hasSeenEdu || (forceShowOnce && !hasShownOnceForDebug) } enum class State { ALLOW_CONVERSATION_AFTER_AVALANCHE, ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME, Loading Loading @@ -374,6 +375,24 @@ class AvalancheSuppressor( return true } private fun shouldShowEdu(): Boolean { val forceShowOnce = SystemProperties.get(FORCE_SHOW_AVALANCHE_EDU_ONCE, "").equals("1") return (!hasSeenEdu && hasTimeElapsedSinceProvisioning()) || (forceShowOnce && !hasShownOnceForDebug) } private fun hasTimeElapsedSinceProvisioning(): Boolean { val provisioned = deviceProvisioningInteractor.getProvisionedTimestamp() return when (provisioned) { is DeviceProvisioningRepository.ProvisionedTimestamp.Unknown -> true // :( is DeviceProvisioningRepository.ProvisionedTimestamp.NotProvisioned -> false is DeviceProvisioningRepository.ProvisionedTimestamp.AtInstant -> systemClock .currentTime() .isAfter(provisioned.instant.plus(AVALANCHE_EDU_DELAY_AFTER_SUW)) } } /** Show avalanche education HUN from SystemUI. */ private fun showEdu() { val res = context.resources Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +3 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings Loading Loading @@ -75,6 +76,7 @@ constructor( @ShadeDisplayAware private val context: Context, private val notificationManager: NotificationManager, private val settingsInteractor: NotificationSettingsInteractor, private val deviceProvisioningInteractor: DeviceProvisioningInteractor, ) : VisualInterruptionDecisionProvider { init { Loading Loading @@ -192,6 +194,7 @@ constructor( context, notificationManager, systemSettings, deviceProvisioningInteractor, ) ) avalancheProvider.register() Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SUPPRESSED_OLD_WHEN import com.android.systemui.statusbar.policy.FakeDeviceProvisionedController import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository import com.android.systemui.util.FakeEventLog import com.android.systemui.util.settings.SystemSettings import com.android.systemui.util.settings.fakeGlobalSettings Loading Loading @@ -142,6 +143,7 @@ abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { protected val settingsInteractor: NotificationSettingsInteractor = mock() protected val packageManager: PackageManager = mock() protected val notificationManager: NotificationManager = mock() protected val deviceProvisioningRepository = kosmos.fakeDeviceProvisioningRepository protected val logger: VisualInterruptionDecisionLogger = mock() protected abstract val provider: VisualInterruptionDecisionProvider Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt +3 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings Loading Loading @@ -64,6 +65,7 @@ object VisualInterruptionDecisionProviderTestUtil { context: Context, notificationManager: NotificationManager, settingsInteractor: NotificationSettingsInteractor, deviceProvisioningInteractor: DeviceProvisioningInteractor, ): VisualInterruptionDecisionProvider { return if (VisualInterruptionRefactor.isEnabled) { VisualInterruptionDecisionProviderImpl( Loading @@ -89,6 +91,7 @@ object VisualInterruptionDecisionProviderTestUtil { context, notificationManager, settingsInteractor, deviceProvisioningInteractor, ) } else { NotificationInterruptStateProviderWrapper( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryImplTest.kt +98 −2 Original line number Diff line number Diff line Loading @@ -15,14 +15,19 @@ */ package com.android.systemui.statusbar.policy.data.repository import android.content.Context import android.content.applicationContext import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.util.mockito.whenever import com.android.systemui.testKosmos import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import java.time.Instant import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before Loading @@ -31,11 +36,14 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val systemClock = kosmos.fakeSystemClock @Mock lateinit var deviceProvisionedController: DeviceProvisionedController lateinit var underTest: DeviceProvisioningRepositoryImpl Loading @@ -45,6 +53,9 @@ class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) underTest = DeviceProvisioningRepositoryImpl( kosmos.applicationContext, testScope.backgroundScope, systemClock, deviceProvisionedController, ) } Loading Loading @@ -75,4 +86,89 @@ class DeviceProvisioningRepositoryImplTest : SysuiTestCase() { .onDeviceProvisionedChanged() assertThat(deviceProvisioned).isFalse() } @Test fun getProvisionedTimestamp_provisionedEarlierWithoutTracking_isUnknown() = testScope.runTest { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) underTest.start() val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo(DeviceProvisioningRepository.ProvisionedTimestamp.Unknown) } @Test fun getProvisionedTimestamp_provisionedEarlierWithTracking_isProvisioningInstant() = testScope.runTest { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) kosmos.applicationContext .getSharedPreferences(context.packageName, Context.MODE_PRIVATE) .edit() .putLong(DeviceProvisioningRepositoryImpl.PREF_DEVICE_PROVISIONED_TIMESTAMP, 42L) .apply() underTest.start() val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo( DeviceProvisioningRepository.ProvisionedTimestamp.AtInstant( Instant.ofEpochMilli(42L) ) ) } @Test fun getProvisionedTimestamp_notProvisioned_isNotProvisioned() = testScope.runTest { whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) underTest.start() val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo(DeviceProvisioningRepository.ProvisionedTimestamp.NotProvisioned) } @Test fun getProvisionedTimestamp_provisionedWhileTracking_isProvisioningInstant() = testScope.runTest { // Start not provisioned -> tracking systemClock.setCurrentTimeMillis(1L) whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false) underTest.start() runCurrent() assertThat(underTest.getProvisionedTimestamp()) .isEqualTo(DeviceProvisioningRepository.ProvisionedTimestamp.NotProvisioned) // Now provisioning happens systemClock.setCurrentTimeMillis(2L) whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) withArgCaptor { verify(deviceProvisionedController).addCallback(capture()) } .onDeviceProvisionedChanged() runCurrent() // A bit later, we query systemClock.setCurrentTimeMillis(3L) val timestamp = underTest.getProvisionedTimestamp() assertThat(timestamp) .isEqualTo( DeviceProvisioningRepository.ProvisionedTimestamp.AtInstant( Instant.ofEpochMilli(2L) ) ) // Also, we stored it for next SystemUI start assertThat( kosmos.applicationContext .getSharedPreferences(context.packageName, Context.MODE_PRIVATE) .getLong( DeviceProvisioningRepositoryImpl.PREF_DEVICE_PROVISIONED_TIMESTAMP, 0L, ) ) .isEqualTo(2L) } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +24 −5 Original line number Diff line number Diff line Loading @@ -59,11 +59,14 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.data.repository.DeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.NotificationChannels import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings import com.android.systemui.util.time.SystemClock import com.android.wm.shell.bubbles.Bubbles import java.time.Duration import java.util.Optional import kotlin.jvm.optionals.getOrElse Loading Loading @@ -272,6 +275,8 @@ private const val FORCE_SHOW_AVALANCHE_EDU_ONCE = "persist.force_show_avalanche_ private const val PREF_HAS_SEEN_AVALANCHE_EDU = "has_seen_avalanche_edu" private val AVALANCHE_EDU_DELAY_AFTER_SUW: Duration = Duration.ofDays(1) class AvalancheSuppressor( private val avalancheProvider: AvalancheProvider, private val systemClock: SystemClock, Loading @@ -281,6 +286,7 @@ class AvalancheSuppressor( private val context: Context, private val notificationManager: NotificationManager, private val systemSettings: SystemSettings, private val deviceProvisioningInteractor: DeviceProvisioningInteractor, ) : VisualInterruptionFilter(types = setOf(PEEK, PULSE), reason = "avalanche") { val TAG = "AvalancheSuppressor" Loading @@ -302,11 +308,6 @@ class AvalancheSuppressor( // with the real settings value. private var isCooldownFlowInSync = false private fun shouldShowEdu(): Boolean { val forceShowOnce = SystemProperties.get(FORCE_SHOW_AVALANCHE_EDU_ONCE, "").equals("1") return !hasSeenEdu || (forceShowOnce && !hasShownOnceForDebug) } enum class State { ALLOW_CONVERSATION_AFTER_AVALANCHE, ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME, Loading Loading @@ -374,6 +375,24 @@ class AvalancheSuppressor( return true } private fun shouldShowEdu(): Boolean { val forceShowOnce = SystemProperties.get(FORCE_SHOW_AVALANCHE_EDU_ONCE, "").equals("1") return (!hasSeenEdu && hasTimeElapsedSinceProvisioning()) || (forceShowOnce && !hasShownOnceForDebug) } private fun hasTimeElapsedSinceProvisioning(): Boolean { val provisioned = deviceProvisioningInteractor.getProvisionedTimestamp() return when (provisioned) { is DeviceProvisioningRepository.ProvisionedTimestamp.Unknown -> true // :( is DeviceProvisioningRepository.ProvisionedTimestamp.NotProvisioned -> false is DeviceProvisioningRepository.ProvisionedTimestamp.AtInstant -> systemClock .currentTime() .isAfter(provisioned.instant.plus(AVALANCHE_EDU_DELAY_AFTER_SUW)) } } /** Show avalanche education HUN from SystemUI. */ private fun showEdu() { val res = context.resources Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +3 −0 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.EventLog import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SystemSettings Loading Loading @@ -75,6 +76,7 @@ constructor( @ShadeDisplayAware private val context: Context, private val notificationManager: NotificationManager, private val settingsInteractor: NotificationSettingsInteractor, private val deviceProvisioningInteractor: DeviceProvisioningInteractor, ) : VisualInterruptionDecisionProvider { init { Loading Loading @@ -192,6 +194,7 @@ constructor( context, notificationManager, systemSettings, deviceProvisioningInteractor, ) ) avalancheProvider.register() Loading