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

Commit 223cc3e7 authored by Matías Hernández's avatar Matías Hernández Committed by Android (Google) Code Review
Browse files

Merge "Don't show Avalanche Edu notification unless 1 day has passed since SUW" into main

parents 0e96fc8a 40446e0f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
@@ -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

+3 −0
Original line number Diff line number Diff line
@@ -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
@@ -64,6 +65,7 @@ object VisualInterruptionDecisionProviderTestUtil {
        context: Context,
        notificationManager: NotificationManager,
        settingsInteractor: NotificationSettingsInteractor,
        deviceProvisioningInteractor: DeviceProvisioningInteractor,
    ): VisualInterruptionDecisionProvider {
        return if (VisualInterruptionRefactor.isEnabled) {
            VisualInterruptionDecisionProviderImpl(
@@ -89,6 +91,7 @@ object VisualInterruptionDecisionProviderTestUtil {
                context,
                notificationManager,
                settingsInteractor,
                deviceProvisioningInteractor,
            )
        } else {
            NotificationInterruptStateProviderWrapper(
+98 −2
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -45,6 +53,9 @@ class DeviceProvisioningRepositoryImplTest : SysuiTestCase() {
        MockitoAnnotations.initMocks(this)
        underTest =
            DeviceProvisioningRepositoryImpl(
                kosmos.applicationContext,
                testScope.backgroundScope,
                systemClock,
                deviceProvisionedController,
            )
    }
@@ -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)
        }
}
+24 −5
Original line number Diff line number Diff line
@@ -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

@@ -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,
@@ -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"

@@ -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,
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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
@@ -75,6 +76,7 @@ constructor(
    @ShadeDisplayAware private val context: Context,
    private val notificationManager: NotificationManager,
    private val settingsInteractor: NotificationSettingsInteractor,
    private val deviceProvisioningInteractor: DeviceProvisioningInteractor,
) : VisualInterruptionDecisionProvider {

    init {
@@ -192,6 +194,7 @@ constructor(
                context,
                notificationManager,
                systemSettings,
                deviceProvisioningInteractor,
            )
        )
        avalancheProvider.register()
Loading