Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions app/src/main/java/com/duckduckgo/app/fire/DataClearing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -167,18 +167,17 @@ class DataClearing @Inject constructor(
BrowserMode.REGULAR -> {
performFireModeClear()
clearRegularDataUsingManualFireOptions(shouldRestartIfRequired)
clearDataAction.setAppUsedSinceLastClearFlag(wasAppUsedSinceLastClear)
}
BrowserMode.FIRE -> {
performFireModeClear()

if (shouldRestartIfRequired) {
dataClearingWideEvent.finishSuccess() // If there is an open wide event, complete it before killing the process.
dataClearingWideEvent.finishSuccess()
clearDataAction.killAndRestartProcess(notifyDataCleared = false)
}
}
}

clearDataAction.setAppUsedSinceLastClearFlag(wasAppUsedSinceLastClear)
}

private suspend fun clearRegularDataUsingManualFireOptions(
Expand Down Expand Up @@ -213,7 +212,7 @@ class DataClearing @Inject constructor(
duckAiFeatureState.showClearDuckAIChatHistory.value
val wasDataCleared = options.contains(FireClearOption.DATA) || wasDuckAiChatsCleared
if (killProcessIfNeeded && wasDataCleared) {
dataClearingWideEvent.finishSuccess() // If there is an open wide event, complete it before killing the process.
dataClearingWideEvent.finishSuccess()
clearDataAction.killProcess()
return false
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ interface DataClearingWideEvent {
NONGRANULAR_FIRE_DIALOG("nongranular_fire_dialog"),
SINGLE_TAB_FIRE_DIALOG("single_tab_fire_dialog"),
APP_SHORTCUT("app_shortcut"),
FIRE_TABS_EMPTIED("fire_tabs_emptied"),

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lmac012 There is a new "entry point" in data clearing when the last Fire tab causes a Fire mode data clearing. I added it to the wide event. What do you think?

AUTO_FOREGROUND("auto_foreground"),
AUTO_BACKGROUND("auto_background"),
LEGACY_FIRE_DIALOG("legacy_fire_dialog"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ import com.duckduckgo.app.browser.api.OmnibarRepository
import com.duckduckgo.app.browser.favicon.FaviconManager
import com.duckduckgo.app.browser.omnibar.OmnibarType
import com.duckduckgo.app.di.AppCoroutineScope
import com.duckduckgo.app.fire.ManualDataClearing
import com.duckduckgo.app.fire.promo.FireTabsPromos
import com.duckduckgo.app.fire.wideevents.DataClearingWideEvent
import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.pixels.AppPixelName.TAB_MANAGER_GRID_VIEW_BUTTON_CLICKED
import com.duckduckgo.app.pixels.AppPixelName.TAB_MANAGER_LIST_VIEW_BUTTON_CLICKED
import com.duckduckgo.app.pixels.duckchat.createWasUsedBeforePixelParams
import com.duckduckgo.app.settings.clear.FireClearOption
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Daily
import com.duckduckgo.app.tabs.model.TabEntity
Expand Down Expand Up @@ -111,6 +114,8 @@ class TabSwitcherViewModel @Inject constructor(
private val trackersAnimationInfoPanelPixels: TrackersAnimationInfoPanelPixels,
private val omnibarRepository: OmnibarRepository,
private val tabTitleResolver: TabTitleResolver,
private val dataClearing: ManualDataClearing,
private val dataClearingWideEvent: DataClearingWideEvent,
@param:AppCoroutineScope private val appCoroutineScope: CoroutineScope,
private val fireTabsPromos: FireTabsPromos,
private val remoteMessageModel: RemoteMessageModel,
Expand Down Expand Up @@ -288,6 +293,28 @@ class TabSwitcherViewModel @Inject constructor(
_viewState.update { it.copy(isFireTabsPromoVisible = false) }
}

// Runs on the app scope so the burn survives the activity recreate() that the Fire -> Regular
// mode switch triggers, otherwise the clear would be cancelled mid-flight.
private fun switchToRegularModeClearingFireData() {
appCoroutineScope.launch(dispatcherProvider.io()) {
dataClearingWideEvent.start(
entryPoint = DataClearingWideEvent.EntryPoint.FIRE_TABS_EMPTIED,
clearOptions = setOf(FireClearOption.TABS, FireClearOption.DATA, FireClearOption.DUCKAI_CHATS),
)
try {
dataClearing.clearDataUsingManualFireOptions(
shouldRestartIfRequired = false,
browserMode = BrowserMode.FIRE,
)
dataClearingWideEvent.finishSuccess()
} catch (e: Exception) {
dataClearingWideEvent.finishFailure(e)
throw e
}
}
command.value = Command.SwitchToRegularMode
}

suspend fun onTabSelected(tabId: String) {
val mode = viewState.value.mode as? Selection ?: Normal
if (mode is Selection) {
Expand Down Expand Up @@ -457,7 +484,7 @@ class TabSwitcherViewModel @Inject constructor(
if (fireModeAvailable && currentMode.value == BrowserMode.FIRE) {
// emptying all Fire tabs returns the user to Regular mode, matching the single-tab
// close path; the tab switcher stays open instead of closing with an undo snackbar
command.value = Command.SwitchToRegularMode
switchToRegularModeClearingFireData()
} else {
// the undo snackbar will be displayed when the tab switcher is closed
command.value = Command.CloseAndShowUndoMessage(tabIds)
Expand Down Expand Up @@ -486,7 +513,7 @@ class TabSwitcherViewModel @Inject constructor(
val isLastTab = tabs.size == 1
if (isLastTab && fireModeAvailable && currentMode.value == BrowserMode.FIRE) {
markTabAsDeletable(tab, swipeGestureUsed)
command.value = Command.SwitchToRegularMode
switchToRegularModeClearingFireData()
} else if (isLastTab) {
// mark the tab as deletable, the undo snackbar will be shown after tab switcher is closed
markTabAsDeletable(tab, swipeGestureUsed)
Expand Down
4 changes: 2 additions & 2 deletions app/src/test/java/com/duckduckgo/app/fire/DataClearingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,7 @@ class DataClearingTest {

verify(mockDataClearingWideEvent).finishSuccess()
verify(mockClearDataAction).killAndRestartProcess(notifyDataCleared = false)
verify(mockClearDataAction).setAppUsedSinceLastClearFlag(false)
verify(mockClearDataAction, never()).setAppUsedSinceLastClearFlag(any())
}

@Test
Expand All @@ -1247,7 +1247,7 @@ class DataClearingTest {
testee.clearDataUsingManualFireOptions(shouldRestartIfRequired = false, wasAppUsedSinceLastClear = true, browserMode = BrowserMode.FIRE)

verify(mockClearDataAction, never()).killAndRestartProcess(any(), any(), any())
verify(mockClearDataAction).setAppUsedSinceLastClearFlag(true)
verify(mockClearDataAction, never()).setAppUsedSinceLastClearFlag(any())
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ import app.cash.turbine.test
import com.duckduckgo.app.browser.api.OmnibarRepository
import com.duckduckgo.app.browser.favicon.FaviconManager
import com.duckduckgo.app.browser.omnibar.OmnibarType
import com.duckduckgo.app.fire.ManualDataClearing
import com.duckduckgo.app.fire.promo.FireTabsPromos
import com.duckduckgo.app.fire.wideevents.DataClearingWideEvent
import com.duckduckgo.app.pixels.AppPixelName
import com.duckduckgo.app.settings.clear.FireClearOption
import com.duckduckgo.app.statistics.pixels.Pixel
import com.duckduckgo.app.statistics.pixels.Pixel.PixelType.Daily
import com.duckduckgo.app.statistics.store.StatisticsDataStore
Expand Down Expand Up @@ -170,6 +173,10 @@ class TabSwitcherViewModelTest {

private val mockTabTitleResolver: TabTitleResolver = mock()

private val mockDataClearing: ManualDataClearing = mock()

private val mockDataClearingWideEvent: DataClearingWideEvent = mock()

private val swipingTabsFeature = FakeFeatureToggleFactory.create(SwipingTabsFeature::class.java)
private val swipingTabsFeatureProvider = SwipingTabsFeatureProvider(swipingTabsFeature)

Expand Down Expand Up @@ -247,6 +254,8 @@ class TabSwitcherViewModelTest {
mockTrackersAnimationInfoPanelPixels,
mockOmnibarFeatureRepository,
mockTabTitleResolver,
mockDataClearing,
mockDataClearingWideEvent,
coroutinesTestRule.testScope,
fireTabsPromos,
remoteMessageModel,
Expand Down Expand Up @@ -2081,6 +2090,15 @@ class TabSwitcherViewModelTest {
verify(mockFireTabRepository).markDeletable(fireTab)
verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture())
assertEquals(Command.SwitchToRegularMode, commandCaptor.lastValue)
verify(mockDataClearingWideEvent).start(
entryPoint = DataClearingWideEvent.EntryPoint.FIRE_TABS_EMPTIED,
clearOptions = setOf(FireClearOption.TABS, FireClearOption.DATA, FireClearOption.DUCKAI_CHATS),
)
verify(mockDataClearing).clearDataUsingManualFireOptions(
shouldRestartIfRequired = false,
browserMode = BrowserMode.FIRE,
)
verify(mockDataClearingWideEvent).finishSuccess()
}

@Test
Expand All @@ -2107,6 +2125,39 @@ class TabSwitcherViewModelTest {
verify(mockFireTabRepository).markDeletable(fireTabs.map { it.tabId })
verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture())
assertEquals(Command.SwitchToRegularMode, commandCaptor.lastValue)
verify(mockDataClearingWideEvent).start(
entryPoint = DataClearingWideEvent.EntryPoint.FIRE_TABS_EMPTIED,
clearOptions = setOf(FireClearOption.TABS, FireClearOption.DATA, FireClearOption.DUCKAI_CHATS),
)
verify(mockDataClearing).clearDataUsingManualFireOptions(
shouldRestartIfRequired = false,
browserMode = BrowserMode.FIRE,
)
verify(mockDataClearingWideEvent).finishSuccess()
}

@Test
fun `when toggling to regular mode with fire tabs present then does not clear fire data`() = runTest {
val fireTabs = listOf(
TabEntity("fire-1", url = "https://fire.example/1", position = 1),
TabEntity("fire-2", url = "https://fire.example/2", position = 2),
)
whenever(mockTabRepositoryProvider.forMode(BrowserMode.FIRE)).thenReturn(mockFireTabRepository)
whenever(mockFireTabRepository.flowTabs).thenReturn(flowOf(fireTabs))
whenever(mockFireTabRepository.flowSelectedTab).thenReturn(flowOf(fireTabs.first()))
whenever(mockFireTabRepository.flowDeletableTabs).thenReturn(flowOf(emptyList()))
whenever(mockFireTabRepository.tabSwitcherData).thenReturn(flowOf(tabSwitcherData))

backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
testee.viewState.collect()
}
currentModeFlow.value = BrowserMode.FIRE
advanceUntilIdle()

testee.onBrowserModeToggled(BrowserMode.REGULAR)
advanceUntilIdle()

verify(mockDataClearing, never()).clearDataUsingManualFireOptions(any(), any(), any())
}

@Test
Expand Down Expand Up @@ -2136,6 +2187,8 @@ class TabSwitcherViewModelTest {
mockTrackersAnimationInfoPanelPixels,
mockOmnibarFeatureRepository,
mockTabTitleResolver,
mockDataClearing,
mockDataClearingWideEvent,
coroutinesTestRule.testScope,
fireTabsPromos,
remoteMessageModel,
Expand Down Expand Up @@ -2181,6 +2234,8 @@ class TabSwitcherViewModelTest {
mockTrackersAnimationInfoPanelPixels,
mockOmnibarFeatureRepository,
mockTabTitleResolver,
mockDataClearing,
mockDataClearingWideEvent,
coroutinesTestRule.testScope,
fireTabsPromos,
remoteMessageModel,
Expand Down Expand Up @@ -2250,6 +2305,8 @@ class TabSwitcherViewModelTest {
mockTrackersAnimationInfoPanelPixels,
mockOmnibarFeatureRepository,
mockTabTitleResolver,
mockDataClearing,
mockDataClearingWideEvent,
coroutinesTestRule.testScope,
fireTabsPromos,
remoteMessageModel,
Expand Down
Loading