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

Commit 30be358b authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

feat:3060: computation and UI for new trackers reports

parent 87d185be
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -228,7 +228,7 @@ val appModule = module {
    viewModelOf(::InternetPrivacyViewModel)
    viewModelOf(::DashboardViewModel)

    single { WeeklyReportViewFactory() }
    single { WeeklyReportViewFactory(context = androidContext()) }

    single {
        NotificationsPresenter(
+1 −1
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ class TrackersAndAppsListsUseCase(

    private suspend fun getCountByEntityMaps(period: Period): CountByEntitiesMaps {
        val periodStart: Instant = period.getPeriodStart()
        val trackersAndAppsIds = statsDatabase.getDistinctTrackerAndApp(periodStart)
        val trackersAndAppsIds = statsDatabase.getDistinctTrackerAndApp(periodStart, Instant.now())
        val trackersAndApps = mapIdsToEntities(trackersAndAppsIds)
        return foldToCountByEntityMaps(trackersAndApps)
    }
+13 −3
Original line number Diff line number Diff line
@@ -18,14 +18,20 @@
package foundation.e.advancedprivacy.domain.usecases

import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository
import foundation.e.advancedprivacy.features.trackers.Period
import foundation.e.advancedprivacy.features.trackers.TrackerTab
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.withContext

class TrackersScreenUseCase(
    private val localStateRepository: LocalStateRepository,
    private val backgroundDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
    private val _goToTab = MutableSharedFlow<Pair<Int, Int>>()
    val goToTab: SharedFlow<Pair<Int, Int>> = _goToTab

    suspend fun getLastPosition(): Int = withContext(backgroundDispatcher) {
        localStateRepository.getTrackersScreenLastPosition()
@@ -43,8 +49,12 @@ class TrackersScreenUseCase(
        localStateRepository.trackersScreenTabStartPosition = -1
    }

    suspend fun preselectTab(periodPosition: Int, tabPosition: Int) = withContext(backgroundDispatcher) {
        localStateRepository.setTrackersScreenLastPosition(periodPosition)
        localStateRepository.trackersScreenTabStartPosition = tabPosition
    suspend fun preselectTab(period: Period, tab: TrackerTab) = withContext(backgroundDispatcher) {
        localStateRepository.setTrackersScreenLastPosition(period.ordinal)
        localStateRepository.trackersScreenTabStartPosition = tab.ordinal
    }

    suspend fun selectTab(period: Period, tab: TrackerTab) {
        _goToTab.emit(period.ordinal to tab.ordinal)
    }
}
+51 −28
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import foundation.e.advancedprivacy.data.repositories.WeeklyReportLocalRepositor
import foundation.e.advancedprivacy.domain.entities.weeklyreport.DisplayableReport
import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReport
import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReportScore
import foundation.e.advancedprivacy.trackers.data.StatsDatabase
import foundation.e.advancedprivacy.trackers.data.TrackersRepository
import java.time.Duration
import java.time.Instant
@@ -40,6 +41,7 @@ class WeeklyReportUseCase(
    private val trackersRepository: TrackersRepository,
    private val appListRepository: AppListRepository,
    private val weeklyReportRepository: WeeklyReportLocalRepository,
    private val statsDatabase: StatsDatabase,
    private val scope: CoroutineScope
) {
    private val _currentReport = MutableStateFlow<DisplayableReport?>(null)
@@ -55,7 +57,7 @@ class WeeklyReportUseCase(
        }
    }

    private suspend fun updateCurrent() {
    suspend fun updateCurrent() {
        val weeklyReport = weeklyReportRepository.getLastWeeklyReport() ?: return
        val now = Instant.now()
        val endOfDisplay = weeklyReport.timestamp + displayDuration
@@ -97,7 +99,7 @@ class WeeklyReportUseCase(
            result.reversed()
        }

    private fun buildCandidates(endOfWeek: Instant, history: List<WeeklyReport>): List<WeeklyReportScore> {
    private suspend fun buildCandidates(endOfWeek: Instant, history: List<WeeklyReport>): List<WeeklyReportScore> {
        val candidates = mutableListOf<WeeklyReport>()
        addCallPerAppCandidates(candidates, endOfWeek)
        addNewTrackerCandidates(candidates, endOfWeek)
@@ -143,38 +145,55 @@ class WeeklyReportUseCase(
        )
    }

    private fun addNewTrackerCandidates(candidates: MutableList<WeeklyReport>, endOfWeek: Instant) {
        val anyApp2 = appListRepository.displayableApps.value.first().id
    private suspend fun addNewTrackerCandidates(candidates: MutableList<WeeklyReport>, endOfWeek: Instant) {
        val startOfWeek = endOfWeek.minus(7, ChronoUnit.DAYS)
        val startOfYear = endOfWeek.minus(365, ChronoUnit.DAYS)

        val trackerAppsHistoric = statsDatabase.getDistinctTrackerAndApp(startOfYear, startOfWeek)
        val trackerAppsOfWeek = statsDatabase.getDistinctTrackerAndApp(startOfWeek, endOfWeek)
        val historicTrackers = trackerAppsHistoric.map { it.first }.toSet()

        val newTrackersDetected = trackerAppsOfWeek.filter { it.first !in historicTrackers }

        val appsIntroducingNewTracker = newTrackersDetected.mapNotNull { appListRepository.getAppById(it.second) }.toSet()
        // TODO dummy apps ?&& displayableApp != appListRepository
        appsIntroducingNewTracker.forEach { app ->
            candidates.add(
                WeeklyReport(
                    endOfWeek,
                    WeeklyReport.StatType.NEW_TRACKER,
                    WeeklyReport.LabelId.NEW_TRACKER_1,
                anyApp2,
                    app.id,
                    emptyList()
                )
            )
        }

        val newTrackersCount = newTrackersDetected.map { it.first }.toSet().size
        if (newTrackersCount > 0) {
            candidates.add(
                WeeklyReport(
                    endOfWeek,
                    WeeklyReport.StatType.NEW_TRACKER,
                    WeeklyReport.LabelId.NEW_TRACKER_2,
                    "",
                listOf(Random.nextInt(4).toString())
                    listOf(newTrackersCount.toString())
                )
            )
        }

        if (appsIntroducingNewTracker.isNotEmpty()) {
            candidates.add(
                WeeklyReport(
                    endOfWeek,
                    WeeklyReport.StatType.NEW_TRACKER,
                    WeeklyReport.LabelId.NEW_TRACKER_3,
                    "",
                listOf(Random.nextInt(4).toString())
                    listOf(appsIntroducingNewTracker.size.toString())
                )
            )
        }
    }

    private fun addCallAndLeaksCandidates(candidates: MutableList<WeeklyReport>, endOfWeek: Instant) {
        val blockedRate = Random.nextInt(100).toString()
@@ -268,6 +287,10 @@ class WeeklyReportUseCase(
        )
    }

    private fun getStartOfWeek(endOfWeek: Instant): Instant {
        return endOfWeek.minus(7, ChronoUnit.DAYS)
    }

    private fun WeeklyReport.toDisplayableReport(): DisplayableReport? {
        val weeklyReport = this
        return when (weeklyReport.labelId) {
+2 −2
Original line number Diff line number Diff line
@@ -130,12 +130,12 @@ class DashboardViewModel(
    }

    fun onClickViewAllApps() = viewModelScope.launch {
        trackersScreenUseCase.preselectTab(Period.MONTH.ordinal, TrackerTab.APPS.ordinal)
        trackersScreenUseCase.preselectTab(Period.MONTH, TrackerTab.APPS)
        _navigate.emit(DashboardFragmentDirections.gotoTrackersFragment())
    }

    fun onClickViewAllTrackers() = viewModelScope.launch {
        trackersScreenUseCase.preselectTab(Period.MONTH.ordinal, TrackerTab.TRACKERS.ordinal)
        trackersScreenUseCase.preselectTab(Period.MONTH, TrackerTab.TRACKERS)
        _navigate.emit(DashboardFragmentDirections.gotoTrackersFragment())
    }
    private suspend fun fetchStatistics() = withContext(Dispatchers.IO) {
Loading