diff --git a/app-tracking-protection/vpn-api/src/main/java/com/duckduckgo/mobile/android/vpn/state/VpnStateMonitor.kt b/app-tracking-protection/vpn-api/src/main/java/com/duckduckgo/mobile/android/vpn/state/VpnStateMonitor.kt index dc326ac1e14c..51b875bcf652 100644 --- a/app-tracking-protection/vpn-api/src/main/java/com/duckduckgo/mobile/android/vpn/state/VpnStateMonitor.kt +++ b/app-tracking-protection/vpn-api/src/main/java/com/duckduckgo/mobile/android/vpn/state/VpnStateMonitor.kt @@ -15,6 +15,7 @@ */ package com.duckduckgo.mobile.android.vpn.state + import com.duckduckgo.mobile.android.vpn.VpnFeature import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.AlwaysOnState.Companion.DEFAULT import kotlinx.coroutines.flow.Flow @@ -45,7 +46,10 @@ interface VpnStateMonitor { val alwaysOnState: AlwaysOnState = DEFAULT, ) - data class AlwaysOnState(val enabled: Boolean, val lockedDown: Boolean) { + data class AlwaysOnState( + val enabled: Boolean, + val lockedDown: Boolean + ) { companion object { val DEFAULT = AlwaysOnState(enabled = false, lockedDown = false) val ALWAYS_ON_ENABLED = AlwaysOnState(enabled = true, lockedDown = false) @@ -56,17 +60,18 @@ interface VpnStateMonitor { } sealed class VpnRunningState { - object ENABLING : VpnRunningState() - object ENABLED : VpnRunningState() - data class DISABLED(val snoozedTriggerAtMillis: Long? = null) : VpnRunningState() - object INVALID : VpnRunningState() + data object ENABLING : VpnRunningState() + data object ENABLED : VpnRunningState() + data object DISABLED : VpnRunningState() + data object INVALID : VpnRunningState() } - enum class VpnStopReason { - SELF_STOP, - ERROR, - REVOKED, - UNKNOWN, - RESTART, + sealed class VpnStopReason { + data object SELF_STOP : VpnStopReason() + data object ERROR : VpnStopReason() + data object REVOKED : VpnStopReason() + data object UNKNOWN : VpnStopReason() + data object RESTART : VpnStopReason() + data class SNOOZED(val snoozedTriggerAtMillis: Long? = null) : VpnStopReason() } } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/heartbeat/VpnServiceHeartbeat.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/heartbeat/VpnServiceHeartbeat.kt index 4a70bf0b8ce7..70c3a22943e4 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/heartbeat/VpnServiceHeartbeat.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/heartbeat/VpnServiceHeartbeat.kt @@ -27,6 +27,7 @@ import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.ERR import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.RESTART import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.REVOKED import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.SELF_STOP +import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.SNOOZED import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.UNKNOWN import com.duckduckgo.mobile.android.vpn.store.VpnDatabase import com.squareup.anvil.annotations.ContributesMultibinding @@ -75,7 +76,7 @@ class VpnServiceHeartbeat @Inject constructor( logcat { "onVpnStopped called" } when (vpnStopReason) { ERROR -> logcat { "HB monitor: sudden vpn stopped $vpnStopReason" } - SELF_STOP, REVOKED, RESTART, UNKNOWN -> { + SELF_STOP, REVOKED, RESTART, UNKNOWN, is SNOOZED -> { logcat { "HB monitor: self stopped or revoked or restart: $vpnStopReason" } // we absolutely want this to finish before VPN is stopped to avoid race conditions reading out the state runBlocking { storeHeartbeat(VpnServiceHeartbeatMonitor.DATA_HEART_BEAT_TYPE_STOPPED) } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/TrackerBlockingVpnService.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/TrackerBlockingVpnService.kt index 7c89c927d02a..36ce06a4a400 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/TrackerBlockingVpnService.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/TrackerBlockingVpnService.kt @@ -230,15 +230,21 @@ class TrackerBlockingVpnService : VpnService(), CoroutineScope by MainScope(), V stopSelf() } } - ACTION_STOP_VPN, ACTION_SNOOZE_VPN -> { + ACTION_STOP_VPN -> { + synchronized(this) { + launch(serviceDispatcher) { + async { + stopVpn(VpnStopReason.SELF_STOP) + }.await() + } + } + } + ACTION_SNOOZE_VPN -> { synchronized(this) { launch(serviceDispatcher) { async { val snoozeTriggerAtMillisExtra = intent.getLongExtra(ACTION_SNOOZE_VPN_EXTRA, 0L) - stopVpn( - VpnStopReason.SELF_STOP, - snoozed = snoozeTriggerAtMillisExtra != 0L, - ) + stopVpn(VpnStopReason.SNOOZED(snoozeTriggerAtMillisExtra)) }.await() } } @@ -526,7 +532,6 @@ class TrackerBlockingVpnService : VpnService(), CoroutineScope by MainScope(), V private suspend fun stopVpn( reason: VpnStopReason, hasVpnAlreadyStarted: Boolean = true, - snoozed: Boolean = false, ) = withContext(serviceDispatcher) { logcat { "VPN log: Stopping VPN. $reason" } @@ -552,12 +557,7 @@ class TrackerBlockingVpnService : VpnService(), CoroutineScope by MainScope(), V } // Set the state to DISABLED here, then call the on stop/failure callbacks - vpnServiceStateStatsDao.insert( - createVpnState( - state = if (snoozed) VpnServiceState.SNOOZED else VpnServiceState.DISABLED, - stopReason = reason, - ), - ) + vpnServiceStateStatsDao.insert(createVpnState(state = VpnServiceState.DISABLED, stopReason = reason)) vpnStateServiceReference?.let { runCatching { unbindService(vpnStateServiceConnection).also { vpnStateServiceReference = null } } @@ -569,7 +569,7 @@ class TrackerBlockingVpnService : VpnService(), CoroutineScope by MainScope(), V private fun sendStopPixels(reason: VpnStopReason) { when (reason) { - VpnStopReason.SELF_STOP, VpnStopReason.RESTART, VpnStopReason.UNKNOWN -> {} // no-op + VpnStopReason.SELF_STOP, VpnStopReason.RESTART, VpnStopReason.UNKNOWN, is VpnStopReason.SNOOZED -> {} // no-op VpnStopReason.ERROR -> deviceShieldPixels.startError() VpnStopReason.REVOKED -> deviceShieldPixels.suddenKillByVpnRevoked() } @@ -688,6 +688,7 @@ class TrackerBlockingVpnService : VpnService(), CoroutineScope by MainScope(), V VpnStopReason.REVOKED -> VpnStoppingReason.REVOKED VpnStopReason.ERROR -> VpnStoppingReason.ERROR VpnStopReason.UNKNOWN -> VpnStoppingReason.UNKNOWN + is VpnStopReason.SNOOZED -> VpnStoppingReason.SNOOZED } } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/RealVpnStateMonitor.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/RealVpnStateMonitor.kt index 6f4c636d38e3..42c578b0133c 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/RealVpnStateMonitor.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/RealVpnStateMonitor.kt @@ -30,12 +30,12 @@ import com.duckduckgo.mobile.android.vpn.model.VpnStoppingReason.ERROR import com.duckduckgo.mobile.android.vpn.model.VpnStoppingReason.RESTART import com.duckduckgo.mobile.android.vpn.model.VpnStoppingReason.REVOKED import com.duckduckgo.mobile.android.vpn.model.VpnStoppingReason.SELF_STOP +import com.duckduckgo.mobile.android.vpn.model.VpnStoppingReason.SNOOZED import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason import com.squareup.anvil.annotations.ContributesBinding -import java.util.concurrent.TimeUnit import javax.inject.Inject import kotlinx.coroutines.flow.* import logcat.logcat @@ -52,14 +52,14 @@ class RealVpnStateMonitor @Inject constructor( return vpnServiceStateStatsDao.getStateStats().map { mapState(it) } .filter { // we only care about the following states - (it.state == VpnRunningState.ENABLED) || (it.state == VpnRunningState.ENABLING) || (it.state is VpnRunningState.DISABLED) + (it.state == VpnRunningState.ENABLED) || (it.state == VpnRunningState.ENABLING) || (it.state == VpnRunningState.DISABLED) } .onEach { logcat { "service state value $it" } } .map { vpnState -> val isFeatureEnabled = vpnFeaturesRegistry.isFeatureRunning(vpnFeature) if (!isFeatureEnabled && vpnState.state !is VpnRunningState.DISABLED) { - vpnState.copy(state = VpnRunningState.DISABLED()) + vpnState.copy(state = VpnRunningState.DISABLED) } else { vpnState } @@ -67,7 +67,7 @@ class RealVpnStateMonitor @Inject constructor( .onStart { val vpnState = mapState(vpnServiceStateStatsDao.getLastStateStats()) VpnState( - state = if (vpnFeaturesRegistry.isFeatureRunning(vpnFeature)) VpnRunningState.ENABLED else VpnRunningState.DISABLED(), + state = if (vpnFeaturesRegistry.isFeatureRunning(vpnFeature)) VpnRunningState.ENABLED else VpnRunningState.DISABLED, alwaysOnState = vpnState.alwaysOnState, stopReason = vpnState.stopReason, ).also { emit(it) } @@ -105,13 +105,13 @@ class RealVpnStateMonitor @Inject constructor( SELF_STOP -> VpnStopReason.SELF_STOP REVOKED -> VpnStopReason.REVOKED ERROR -> VpnStopReason.ERROR + SNOOZED -> VpnStopReason.SNOOZED() else -> VpnStopReason.UNKNOWN } val runningState = when (lastState?.state) { ENABLING -> VpnRunningState.ENABLING ENABLED -> VpnRunningState.ENABLED - DISABLED -> VpnRunningState.DISABLED() - SNOOZED -> VpnRunningState.DISABLED(TimeUnit.MINUTES.toMillis(20)) // TODO - remove hardcoded time + DISABLED -> VpnRunningState.DISABLED null, INVALID -> VpnRunningState.INVALID } val alwaysOnState = when (lastState?.alwaysOnState) { diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/VpnStateMonitorService.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/VpnStateMonitorService.kt index a89fd0ae1d25..751c4841d13b 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/VpnStateMonitorService.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/service/state/VpnStateMonitorService.kt @@ -28,7 +28,6 @@ import com.duckduckgo.di.scopes.VpnScope import com.duckduckgo.mobile.android.vpn.dao.HeartBeatEntity import com.duckduckgo.mobile.android.vpn.heartbeat.VpnServiceHeartbeatMonitor import com.duckduckgo.mobile.android.vpn.model.VpnServiceState.DISABLED -import com.duckduckgo.mobile.android.vpn.model.VpnServiceState.SNOOZED import com.duckduckgo.mobile.android.vpn.model.VpnServiceStateStats import com.duckduckgo.mobile.android.vpn.service.TrackerBlockingVpnService import com.duckduckgo.mobile.android.vpn.store.VpnDatabase @@ -78,7 +77,7 @@ class VpnStateMonitorService : Service() { // check last state, if it was enabled then we store disabled state reason unknown private fun maybeUpdateVPNState() { val lastStateStats = vpnDatabase.vpnServiceStateDao().getLastStateStats() - if (lastStateStats?.state != DISABLED && lastStateStats?.state != SNOOZED) { + if (lastStateStats?.state != DISABLED) { logcat { "VpnStateMonitorService destroyed but VPN state stored as ${lastStateStats?.state}, inserting DISABLED" } vpnDatabase.vpnServiceStateDao().insert(VpnServiceStateStats(state = DISABLED)) } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationScheduler.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationScheduler.kt index 496061c2b9d1..0e41ff313052 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationScheduler.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationScheduler.kt @@ -29,7 +29,6 @@ import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.plugins.PluginPoint import com.duckduckgo.di.scopes.VpnScope import com.duckduckgo.mobile.android.app.tracking.AppTrackingProtection -import com.duckduckgo.mobile.android.vpn.AppTpVpnFeature import com.duckduckgo.mobile.android.vpn.feature.removal.VpnFeatureRemover import com.duckduckgo.mobile.android.vpn.service.TrackerBlockingVpnService import com.duckduckgo.mobile.android.vpn.service.VpnReminderNotificationContentPlugin @@ -39,15 +38,12 @@ import com.duckduckgo.mobile.android.vpn.service.VpnReminderNotificationWorker import com.duckduckgo.mobile.android.vpn.service.VpnReminderReceiver import com.duckduckgo.mobile.android.vpn.service.VpnServiceCallbacks import com.duckduckgo.mobile.android.vpn.service.notification.getHighestPriorityPluginForType -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason import com.squareup.anvil.annotations.ContributesMultibinding import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import logcat.logcat @@ -61,7 +57,6 @@ class AppTPReminderNotificationScheduler @Inject constructor( private val vpnReminderNotificationBuilder: VpnReminderNotificationBuilder, private val vpnReminderNotificationContentPluginPoint: PluginPoint, private val appTrackingProtection: AppTrackingProtection, - private val vpnStateMonitor: VpnStateMonitor, ) : VpnServiceCallbacks { private var isAppTPEnabled: AtomicReference = AtomicReference(false) @@ -87,6 +82,7 @@ class AppTPReminderNotificationScheduler @Inject constructor( VpnStopReason.RESTART -> {} // no-op VpnStopReason.SELF_STOP -> onVPNManuallyStopped(coroutineScope) VpnStopReason.REVOKED -> onVPNRevoked() + is VpnStopReason.SNOOZED -> onVPNSnooze(coroutineScope, vpnStopReason.snoozedTriggerAtMillis) else -> onVPNUndesiredStop() } } @@ -97,15 +93,18 @@ class AppTPReminderNotificationScheduler @Inject constructor( if (vpnFeatureRemover.isFeatureRemoved()) { logcat { "VPN Manually stopped because user disabled the feature, nothing to do" } } else { - val snoozeTrigger = vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN).drop(1).firstOrNull() - snoozeTrigger?.let { vpnState -> - if (vpnState.state is VpnStateMonitor.VpnRunningState.DISABLED) { - val state = vpnState.state as VpnStateMonitor.VpnRunningState.DISABLED - if (state.snoozedTriggerAtMillis == null) { - handleNotifForDisabledAppTP() - } - } - } + handleNotifForDisabledAppTP() + } + } + } + + private fun onVPNSnooze( + coroutineScope: CoroutineScope, + triggerAtMillis: Long?, + ) { + coroutineScope.launch(dispatchers.io()) { + if (triggerAtMillis == null) { + handleNotifForDisabledAppTP() } } } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModel.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModel.kt index d12956375561..2fbddf6b4e84 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModel.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModel.kt @@ -123,7 +123,7 @@ class DeviceShieldActivityFeedViewModel @Inject constructor( .flowOn(dispatcherProvider.io()) .onStart { startTickerRefresher() - emit(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED(), ERROR))) + emit(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED, ERROR))) delay(300) } } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt index 383383837900..22ffefe28835 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivity.kt @@ -63,6 +63,7 @@ import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.REVOKED import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.SELF_STOP +import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.SNOOZED import com.duckduckgo.mobile.android.vpn.ui.AppBreakageCategory import com.duckduckgo.mobile.android.vpn.ui.alwayson.AlwaysOnAlertDialogFragment import com.duckduckgo.mobile.android.vpn.ui.report.DeviceShieldAppTrackersInfo @@ -569,25 +570,27 @@ class DeviceShieldTrackerActivity : binding.deviceShieldTrackerShieldImage.setImageResource(R.drawable.apptp_shield_disabled) binding.deviceShieldTrackerLabelEnabled.gone() - val (disabledLabel, annotation) = if (runningState.stopReason == REVOKED) { - R.string.atp_ActivityRevokedLabel to REPORT_ISSUES_ANNOTATION - } else if (runningState.stopReason == SELF_STOP) { - R.string.atp_ActivityDisabledLabel to REPORT_ISSUES_ANNOTATION - } else { - R.string.atp_ActivityDisabledBySystemLabel to RE_ENABLE_ANNOTATION - } - binding.deviceShieldTrackerLabelDisabled.apply { - setClickableLink( - annotation, - getText(disabledLabel), - ) { - if (annotation == REPORT_ISSUES_ANNOTATION) { - launchFeedback() - } else if (annotation == RE_ENABLE_ANNOTATION) { - reEnableAppTrackingProtection() + if (runningState.stopReason !is SNOOZED) { + val (disabledLabel, annotation) = if (runningState.stopReason == REVOKED) { + R.string.atp_ActivityRevokedLabel to REPORT_ISSUES_ANNOTATION + } else if (runningState.stopReason == SELF_STOP) { + R.string.atp_ActivityDisabledLabel to REPORT_ISSUES_ANNOTATION + } else { + R.string.atp_ActivityDisabledBySystemLabel to RE_ENABLE_ANNOTATION + } + binding.deviceShieldTrackerLabelDisabled.apply { + setClickableLink( + annotation, + getText(disabledLabel), + ) { + if (annotation == REPORT_ISSUES_ANNOTATION) { + launchFeedback() + } else if (annotation == RE_ENABLE_ANNOTATION) { + reEnableAppTrackingProtection() + } } + show() } - show() } } } diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivityViewModel.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivityViewModel.kt index dff3b4fb578d..eb85ff48f006 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivityViewModel.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldTrackerActivityViewModel.kt @@ -63,7 +63,7 @@ class DeviceShieldTrackerActivityViewModel @Inject constructor( return@withContext vpnStateMonitor .getStateFlow(AppTpVpnFeature.APPTP_VPN) // we only cared about enabled and disabled states for AppTP - .filter { (it.state == VpnStateMonitor.VpnRunningState.ENABLED) || (it.state is VpnStateMonitor.VpnRunningState.DISABLED) } + .filter { (it.state == VpnStateMonitor.VpnRunningState.ENABLED) || (it.state == VpnStateMonitor.VpnRunningState.DISABLED) } .combine(refreshVpnRunningState.asStateFlow()) { state, _ -> state } } diff --git a/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationSchedulerTest.kt b/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationSchedulerTest.kt index e60bb5b9e61d..f470b65bb929 100644 --- a/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationSchedulerTest.kt +++ b/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/notification/AppTPReminderNotificationSchedulerTest.kt @@ -27,7 +27,6 @@ import androidx.work.testing.WorkManagerTestInitHelper import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.common.utils.plugins.PluginPoint import com.duckduckgo.mobile.android.app.tracking.AppTrackingProtection -import com.duckduckgo.mobile.android.vpn.AppTpVpnFeature import com.duckduckgo.mobile.android.vpn.R import com.duckduckgo.mobile.android.vpn.feature.removal.VpnFeatureRemover import com.duckduckgo.mobile.android.vpn.service.VpnReminderNotificationContentPlugin @@ -39,14 +38,11 @@ import com.duckduckgo.mobile.android.vpn.service.VpnReminderNotificationContentP import com.duckduckgo.mobile.android.vpn.service.VpnReminderNotificationWorker import com.duckduckgo.mobile.android.vpn.service.VpnReminderReceiverManager import com.duckduckgo.mobile.android.vpn.service.notification.getHighestPriorityPluginForType -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.REVOKED import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.SELF_STOP +import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason.SNOOZED import java.util.concurrent.TimeUnit import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.* import org.junit.runner.RunWith @@ -73,7 +69,6 @@ class AppTPReminderNotificationSchedulerTest { private val mockPluginPoint: PluginPoint = mock() private val vpnReminderNotificationBuilder: VpnReminderNotificationBuilder = mock() private val appTrackingProtection: AppTrackingProtection = mock() - private val vpnStateMonitor: VpnStateMonitor = mock() @Before fun before() { @@ -90,7 +85,6 @@ class AppTPReminderNotificationSchedulerTest { vpnReminderNotificationBuilder, mockPluginPoint, appTrackingProtection, - vpnStateMonitor, ) } @@ -153,12 +147,6 @@ class AppTPReminderNotificationSchedulerTest { @Test fun whenVPNManuallyStopsDueToSnoozeThenDailyReminderIsNotEnqueued() = runTest { - whenever(vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN)).thenReturn( - flowOf( - VpnState(state = VpnRunningState.ENABLED), - VpnState(state = VpnRunningState.DISABLED(snoozedTriggerAtMillis = 100000L)), - ), - ) whenever(vpnFeatureRemover.isFeatureRemoved()).thenReturn(false) assertWorkersAreNotEnqueued(VpnReminderNotificationWorker.WORKER_VPN_REMINDER_DAILY_TAG) whenever(appTrackingProtection.isEnabled()).thenReturn(true) @@ -167,19 +155,13 @@ class AppTPReminderNotificationSchedulerTest { testee.onVpnStarted(coroutinesTestRule.testScope) whenever(appTrackingProtection.isEnabled()).thenReturn(false) - testee.onVpnStopped(coroutinesTestRule.testScope, SELF_STOP) + testee.onVpnStopped(coroutinesTestRule.testScope, SNOOZED(1234L)) assertWorkersAreNotEnqueued(VpnReminderNotificationWorker.WORKER_VPN_REMINDER_DAILY_TAG) } @Test fun whenVPNManuallyStopsThenDailyReminderIsEnqueued() = runTest { - whenever(vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN)).thenReturn( - flowOf( - VpnState(state = VpnRunningState.ENABLED), - VpnState(state = VpnRunningState.DISABLED()), - ), - ) whenever(vpnFeatureRemover.isFeatureRemoved()).thenReturn(false) assertWorkersAreNotEnqueued(VpnReminderNotificationWorker.WORKER_VPN_REMINDER_DAILY_TAG) whenever(appTrackingProtection.isEnabled()).thenReturn(true) @@ -238,12 +220,6 @@ class AppTPReminderNotificationSchedulerTest { @Test fun whenVPNManuallyStopsAndUndesiredReminderWasScheduledThenUndesiredReminderIsNoLongerScheduled() = runTest { - whenever(vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN)).thenReturn( - flowOf( - VpnState(state = VpnRunningState.ENABLED), - VpnState(state = VpnRunningState.DISABLED()), - ), - ) whenever(vpnFeatureRemover.isFeatureRemoved()).thenReturn(false) enqueueUndesiredReminderNotificationWorker() assertWorkersAreEnqueued(VpnReminderNotificationWorker.WORKER_VPN_REMINDER_UNDESIRED_TAG) @@ -271,12 +247,6 @@ class AppTPReminderNotificationSchedulerTest { @Test fun whenUserHasOnboardedAndVPNManuallyStopsAndWithContentPluginForDisabledThenImmediateNotificationShouldBeShown() = runTest { - whenever(vpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN)).thenReturn( - flowOf( - VpnState(state = VpnRunningState.ENABLED), - VpnState(state = VpnRunningState.DISABLED()), - ), - ) whenever(mockPluginPoint.getPlugins()).thenReturn(listOf(fakeRevokedPlugin, fakeDisabledPlugin)) whenever(vpnFeatureRemover.isFeatureRemoved()).thenReturn(false) whenever(appTrackingProtection.isEnabled()).thenReturn(true) diff --git a/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModelTest.kt b/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModelTest.kt index c5d66e0d6ed8..027236d25100 100644 --- a/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModelTest.kt +++ b/app-tracking-protection/vpn-impl/src/test/java/com/duckduckgo/mobile/android/vpn/ui/tracker_activity/DeviceShieldActivityFeedViewModelTest.kt @@ -99,7 +99,7 @@ class DeviceShieldActivityFeedViewModelTest { @Test fun whenGetMostRecentTrackersCalledStartWithSkeleton() = runBlocking { viewModel.getMostRecentTrackers(timeWindow, config).test { - assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED(), ERROR)), awaitItem()) + assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED, ERROR)), awaitItem()) cancelAndConsumeRemainingEvents() } } @@ -113,7 +113,7 @@ class DeviceShieldActivityFeedViewModelTest { getAppsAndProtectionInfoReturnsEmptyList() viewModel.getMostRecentTrackers(timeWindow, config).test { - assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED(), ERROR)), awaitItem()) + assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED, ERROR)), awaitItem()) assertEquals( TrackerFeedViewState( listOf( @@ -137,7 +137,7 @@ class DeviceShieldActivityFeedViewModelTest { assertEquals( TrackerFeedViewState( listOf(TrackerLoadingSkeleton), - vpnState = VpnState(state = DISABLED(), stopReason = ERROR), + vpnState = VpnState(state = DISABLED, stopReason = ERROR), ), awaitItem(), ) @@ -153,7 +153,7 @@ class DeviceShieldActivityFeedViewModelTest { val boundedConfig = config.copy(maxRows = 5) viewModel.getMostRecentTrackers(timeWindow, boundedConfig).test { - assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED(), ERROR)), awaitItem()) + assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED, ERROR)), awaitItem()) assertEquals( TrackerFeedViewState( listOf( @@ -189,13 +189,13 @@ class DeviceShieldActivityFeedViewModelTest { val boundedConfig = config.copy(maxRows = 5) viewModel.getMostRecentTrackers(timeWindow, boundedConfig).test { - assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED(), ERROR)), awaitItem()) + assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED, ERROR)), awaitItem()) assertEquals( TrackerFeedViewState( listOf( TrackerFeedItem.TrackerDescriptionFeed, ), - vpnState = VpnState(state = DISABLED(), stopReason = UNKNOWN), + vpnState = VpnState(state = DISABLED, stopReason = UNKNOWN), ), awaitItem(), ) @@ -215,7 +215,7 @@ class DeviceShieldActivityFeedViewModelTest { val boundedConfig = config.copy(maxRows = 2) viewModel.getMostRecentTrackers(timeWindow, boundedConfig).test { - assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED(), ERROR)), awaitItem()) + assertEquals(TrackerFeedViewState(listOf(TrackerLoadingSkeleton), VpnState(DISABLED, ERROR)), awaitItem()) assertEquals( TrackerFeedViewState( listOf( @@ -240,7 +240,7 @@ class DeviceShieldActivityFeedViewModelTest { } private fun mockVpnDisabled() { - whenever(mockVpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN)).thenReturn(flowOf(VpnState(DISABLED(), UNKNOWN))) + whenever(mockVpnStateMonitor.getStateFlow(AppTpVpnFeature.APPTP_VPN)).thenReturn(flowOf(VpnState(DISABLED, UNKNOWN))) } companion object { diff --git a/app-tracking-protection/vpn-store/src/main/java/com/duckduckgo/mobile/android/vpn/model/VpnDatabaseModels.kt b/app-tracking-protection/vpn-store/src/main/java/com/duckduckgo/mobile/android/vpn/model/VpnDatabaseModels.kt index b4bcaadeffd2..2322b796214f 100644 --- a/app-tracking-protection/vpn-store/src/main/java/com/duckduckgo/mobile/android/vpn/model/VpnDatabaseModels.kt +++ b/app-tracking-protection/vpn-store/src/main/java/com/duckduckgo/mobile/android/vpn/model/VpnDatabaseModels.kt @@ -43,7 +43,6 @@ enum class VpnServiceState { ENABLING, ENABLED, DISABLED, - SNOOZED, INVALID, } @@ -53,6 +52,7 @@ enum class VpnStoppingReason { REVOKED, UNKNOWN, RESTART, + SNOOZED } data class AlwaysOnState(val alwaysOnEnabled: Boolean, val alwaysOnLockedDown: Boolean) { diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModel.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModel.kt index dd57b263b1b3..fd26fe99d380 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModel.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModel.kt @@ -140,11 +140,11 @@ class NetworkProtectionManagementViewModel @Inject constructor( vpnStopReason: VpnStopReason?, vpnAlwaysOnState: AlwaysOnState, ): AlertState { - return if (vpnRunningState is DISABLED && (reconnectState == Reconnecting || reconnectState == ReconnectingFailed)) { + return if (vpnRunningState == DISABLED && (reconnectState == Reconnecting || reconnectState == ReconnectingFailed)) { ShowReconnectingFailed } else if (reconnectState == Reconnecting) { ShowReconnecting - } else if (vpnRunningState is DISABLED && (vpnStopReason == REVOKED || vpnStopReason == ERROR)) { + } else if (vpnRunningState == DISABLED && (vpnStopReason == REVOKED || vpnStopReason == ERROR)) { ShowRevoked } else if (vpnRunningState == ENABLED && vpnAlwaysOnState.isAlwaysOnLockedDown()) { ShowAlwaysOnLockdownEnabled @@ -158,7 +158,7 @@ class NetworkProtectionManagementViewModel @Inject constructor( .combine(refreshVpnRunningState.asStateFlow()) { state, _ -> state } private fun VpnState.toConnectionState(reconnectState: ReconnectStatus): ConnectionState = - if (this.state !is DISABLED && reconnectState == Reconnecting) { + if (this.state != DISABLED && reconnectState == Reconnecting) { Connecting } else { when (this.state) { diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationScheduler.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationScheduler.kt index a632219b3111..2a84e60e59b1 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationScheduler.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationScheduler.kt @@ -24,18 +24,14 @@ import com.duckduckgo.app.di.AppCoroutineScope import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.VpnScope import com.duckduckgo.mobile.android.vpn.service.VpnServiceCallbacks -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason import com.duckduckgo.networkprotection.api.NetworkProtectionState -import com.duckduckgo.networkprotection.impl.NetPVpnFeature import com.duckduckgo.networkprotection.impl.settings.NetPSettingsLocalConfig import com.duckduckgo.networkprotection.impl.waitlist.NetPRemoteFeature import com.squareup.anvil.annotations.ContributesMultibinding import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import logcat.logcat @@ -49,7 +45,6 @@ class NetPDisabledNotificationScheduler @Inject constructor( @AppCoroutineScope private val coroutineScope: CoroutineScope, private val dispatcherProvider: DispatcherProvider, private val netPRemoteFeature: NetPRemoteFeature, - private val vpnStateMonitor: VpnStateMonitor, ) : VpnServiceCallbacks { private var isNetPEnabled: AtomicReference = AtomicReference(false) @@ -77,6 +72,7 @@ class NetPDisabledNotificationScheduler @Inject constructor( VpnStopReason.RESTART -> {} // no-op VpnStopReason.SELF_STOP -> onVPNManuallyStopped() VpnStopReason.REVOKED -> onVPNRevoked() + is VpnStopReason.SNOOZED -> onVpnSnoozed(vpnStopReason.snoozedTriggerAtMillis) else -> {} } } @@ -90,7 +86,7 @@ class NetPDisabledNotificationScheduler @Inject constructor( private suspend fun onVPNManuallyStopped() { if (shouldShowImmediateNotification()) { logcat { "VPN Manually stopped, showing disabled notification for NetP" } - showDisabledOrSnoozeNotification() + showDisabledNotification() isNetPEnabled.set(false) } } @@ -109,31 +105,18 @@ class NetPDisabledNotificationScheduler @Inject constructor( } } - private fun showDisabledOrSnoozeNotification() { - fun showSnoozedNotification(triggerAtMillis: Long) { - coroutineScope.launch(dispatcherProvider.io()) { + private fun onVpnSnoozed(triggerAtMillis: Long?) { + coroutineScope.launch(dispatcherProvider.io()) { + if (triggerAtMillis != null) { if (!netPSettingsLocalConfig.vpnNotificationAlerts().isEnabled()) return@launch notificationManager.notify( NETP_REMINDER_NOTIFICATION_ID, netPDisabledNotificationBuilder.buildSnoozeNotification(context, triggerAtMillis), ) + } else { + showDisabledNotification() } } - - coroutineScope.launch(dispatcherProvider.io()) { - // FIXME drop to skip the default value - val snoozeTrigger = vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN).drop(1).firstOrNull() - snoozeTrigger?.let { vpnState -> - if (vpnState.state is VpnStateMonitor.VpnRunningState.DISABLED) { - val state = vpnState.state as VpnStateMonitor.VpnRunningState.DISABLED - state.snoozedTriggerAtMillis?.let { triggerAtMillis -> - showSnoozedNotification(triggerAtMillis) - } ?: showDisabledNotification() - } else { - showDisabledNotification() - } - } ?: showDisabledNotification() - } } private fun showDisabledNotification() { diff --git a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModelTest.kt b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModelTest.kt index f349d62481a7..6cf5f6416472 100644 --- a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModelTest.kt +++ b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/management/NetworkProtectionManagementViewModelTest.kt @@ -235,7 +235,7 @@ class NetworkProtectionManagementViewModelTest { whenever(vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN)).thenReturn( flowOf( VpnState( - state = DISABLED(), + state = DISABLED, ), ), ) @@ -372,7 +372,7 @@ class NetworkProtectionManagementViewModelTest { whenever(vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN)).thenReturn( flowOf( VpnState( - state = DISABLED(), + state = DISABLED, ), ), ) @@ -444,12 +444,12 @@ class NetworkProtectionManagementViewModelTest { @Test fun whenVpnStateIsDisabledAndReconnectingThenAlertStateIsShowReconnectingFailed() { - assertEquals(ShowReconnectingFailed, testee.getAlertState(DISABLED(), Reconnecting, null, AlwaysOnState.DEFAULT)) + assertEquals(ShowReconnectingFailed, testee.getAlertState(DISABLED, Reconnecting, null, AlwaysOnState.DEFAULT)) } @Test fun whenVpnStateIsDisabledAndReconnectingFailedThenAlertStateIsShowReconnectingFailed() { - assertEquals(ShowReconnectingFailed, testee.getAlertState(DISABLED(), ReconnectingFailed, UNKNOWN, AlwaysOnState.DEFAULT)) + assertEquals(ShowReconnectingFailed, testee.getAlertState(DISABLED, ReconnectingFailed, UNKNOWN, AlwaysOnState.DEFAULT)) } @Test @@ -469,12 +469,12 @@ class NetworkProtectionManagementViewModelTest { @Test fun whenNotReconnectingThenAlertStateIsNone() { - assertEquals(None, testee.getAlertState(DISABLED(), NotReconnecting, UNKNOWN, AlwaysOnState.DEFAULT)) + assertEquals(None, testee.getAlertState(DISABLED, NotReconnecting, UNKNOWN, AlwaysOnState.DEFAULT)) } @Test fun whenStopReasonIsRevokedAndNotReconnectingThenAlertStateIsShowRevoked() { - assertEquals(ShowRevoked, testee.getAlertState(DISABLED(), NotReconnecting, REVOKED, AlwaysOnState.DEFAULT)) + assertEquals(ShowRevoked, testee.getAlertState(DISABLED, NotReconnecting, REVOKED, AlwaysOnState.DEFAULT)) } @Test diff --git a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationSchedulerTest.kt b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationSchedulerTest.kt index a8efaa167c07..9b85ba65173e 100644 --- a/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationSchedulerTest.kt +++ b/network-protection/network-protection-impl/src/test/java/com/duckduckgo/networkprotection/impl/notification/NetPDisabledNotificationSchedulerTest.kt @@ -21,21 +21,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.duckduckgo.common.test.CoroutineTestRule import com.duckduckgo.feature.toggles.api.Toggle.State -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState.DISABLED -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState.ENABLED -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnRunningState.INVALID -import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnState import com.duckduckgo.mobile.android.vpn.state.VpnStateMonitor.VpnStopReason import com.duckduckgo.networkprotection.api.NetworkProtectionState -import com.duckduckgo.networkprotection.impl.NetPVpnFeature import com.duckduckgo.networkprotection.impl.settings.FakeNetPSettingsLocalConfigFactory import com.duckduckgo.networkprotection.impl.settings.NetPSettingsLocalConfig import com.duckduckgo.networkprotection.impl.waitlist.FakeNetPRemoteFeatureFactory import com.duckduckgo.networkprotection.impl.waitlist.NetPRemoteFeature import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before @@ -66,9 +58,6 @@ class NetPDisabledNotificationSchedulerTest { @Mock private lateinit var networkProtectionState: NetworkProtectionState - @Mock - private lateinit var vpnStateMonitor: VpnStateMonitor - @Before fun setUp() { MockitoAnnotations.openMocks(this) @@ -85,37 +74,15 @@ class NetPDisabledNotificationSchedulerTest { TestScope(), coroutineRule.testDispatcherProvider, netPRemoteFeature, - vpnStateMonitor, - ) - } - - @Test - fun whenVpnManuallyStoppedWithInvalidStateThenDoNotShowSnooze() = runTest { - netPRemoteFeature.waitlistBetaActive().setEnabled(State(enable = true)) - netPSettingsLocalConfig.vpnNotificationAlerts().setEnabled(State(enable = true)) - whenever(networkProtectionState.isEnabled()).thenReturn(true) - whenever(networkProtectionState.isOnboarded()).thenReturn(true) - whenever(vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN)).thenReturn( - flowOf( - VpnState(state = ENABLED), - VpnState(state = INVALID), - ), ) - - testee.onVpnStarted(coroutineRule.testScope) - testee.onVpnStopped(coroutineRule.testScope, VpnStopReason.SELF_STOP) - - verify(netPDisabledNotificationBuilder).buildDisabledNotification(any()) } @Test - fun whenVpnManuallyStoppedWithNoVPNStateThenDoNotShowSnooze() = runTest { + fun whenVpnManuallyStoppedThenDoNotShowSnooze() = runTest { netPRemoteFeature.waitlistBetaActive().setEnabled(State(enable = true)) netPSettingsLocalConfig.vpnNotificationAlerts().setEnabled(State(enable = true)) whenever(networkProtectionState.isEnabled()).thenReturn(true) whenever(networkProtectionState.isOnboarded()).thenReturn(true) - whenever(vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN)).thenReturn(emptyFlow()) - testee.onVpnStarted(coroutineRule.testScope) testee.onVpnStopped(coroutineRule.testScope, VpnStopReason.SELF_STOP) @@ -123,39 +90,27 @@ class NetPDisabledNotificationSchedulerTest { } @Test - fun whenVpnManuallyStoppedWithButIsNotSnoozedThenShowDisabledNotification() = runTest { + fun whenVpnManuallyStoppedWithSnoozeButNoTriggerTimeThenDoNotShowSnooze() = runTest { netPRemoteFeature.waitlistBetaActive().setEnabled(State(enable = true)) netPSettingsLocalConfig.vpnNotificationAlerts().setEnabled(State(enable = true)) whenever(networkProtectionState.isEnabled()).thenReturn(true) whenever(networkProtectionState.isOnboarded()).thenReturn(true) - whenever(vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN)).thenReturn( - flowOf( - VpnState(state = ENABLED), - VpnState(state = DISABLED()), - ), - ) testee.onVpnStarted(coroutineRule.testScope) - testee.onVpnStopped(coroutineRule.testScope, VpnStopReason.SELF_STOP) + testee.onVpnStopped(coroutineRule.testScope, VpnStopReason.SNOOZED()) verify(netPDisabledNotificationBuilder).buildDisabledNotification(any()) } @Test - fun whenVpnManuallyStoppedAndSnoozedThenShowSnoozeNotification() = runTest { + fun whenVpnSnoozedThenShowSnoozeNotification() = runTest { netPRemoteFeature.waitlistBetaActive().setEnabled(State(enable = true)) netPSettingsLocalConfig.vpnNotificationAlerts().setEnabled(State(enable = true)) whenever(networkProtectionState.isEnabled()).thenReturn(true) whenever(networkProtectionState.isOnboarded()).thenReturn(true) - whenever(vpnStateMonitor.getStateFlow(NetPVpnFeature.NETP_VPN)).thenReturn( - flowOf( - VpnState(state = DISABLED(20000L)), - VpnState(state = DISABLED(20000L)), - ), - ) testee.onVpnStarted(coroutineRule.testScope) - testee.onVpnStopped(coroutineRule.testScope, VpnStopReason.SELF_STOP) + testee.onVpnStopped(coroutineRule.testScope, VpnStopReason.SNOOZED(20000L)) verify(netPDisabledNotificationBuilder).buildSnoozeNotification(any(), eq(20000L)) }