diff options
Diffstat (limited to 'app/src/main/java/foundation/e/advancedprivacy/domain')
5 files changed, 224 insertions, 98 deletions
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt new file mode 100644 index 0000000..92550ab --- /dev/null +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2023 MURENA SAS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package foundation.e.advancedprivacy.domain.usecases + +import foundation.e.advancedprivacy.data.repositories.AppListsRepository +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.trackers.data.StatsDatabase +import foundation.e.advancedprivacy.trackers.data.TrackersRepository +import foundation.e.advancedprivacy.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker +import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase + +class AppTrackersUseCase( + private val whitelistRepository: WhitelistRepository, + private val trackersStateUseCase: TrackersStateUseCase, + private val appListsRepository: AppListsRepository, + private val statsDatabase: StatsDatabase, + private val trackersRepository: TrackersRepository, + private val filterHostnameUseCase: FilterHostnameUseCase, +) { + suspend fun toggleAppWhitelist(app: ApplicationDescription, isBlocked: Boolean) { + appListsRepository.applyForHiddenApps(app) { + whitelistRepository.setWhiteListed(it.apId, !isBlocked) + val trackerIds = statsDatabase.getTrackerIds(listOf(app.apId)) + whitelistRepository.setWhitelistedTrackersForApp(it.apId, trackerIds, !isBlocked) + } + trackersStateUseCase.updateAllTrackersBlockedState() + } + + suspend fun clearWhitelist(app: ApplicationDescription) { + appListsRepository.applyForHiddenApps( + app + ) { + whitelistRepository.clearWhiteList(it.apId) + } + trackersStateUseCase.updateAllTrackersBlockedState() + } + + suspend fun getCalls(app: ApplicationDescription): Pair<Int, Int> { + return appListsRepository.mapReduceForHiddenApps( + app = app, + map = { + statsDatabase.getCallsForApp(app.apId) + }, + reduce = { zip -> + zip.unzip().let { (blocked, leaked) -> + blocked.sum() to leaked.sum() + } + } + ) + } + + suspend fun getTrackersWithBlockedList(app: ApplicationDescription): List<Pair<Tracker, Boolean>> { + val realApIds = appListsRepository.getRealApps(app).map { it.apId } + val trackers = statsDatabase.getTrackerIds(realApIds) + .mapNotNull { trackersRepository.getTracker(it) } + + return enrichWithBlockedState(app, trackers) + } + + suspend fun enrichWithBlockedState(app: ApplicationDescription, trackers: List<Tracker>): List<Pair<Tracker, Boolean>> { + val realAppUids = appListsRepository.getRealApps(app).map { it.uid } + return trackers.map { tracker -> + tracker to !realAppUids.any { uid -> + filterHostnameUseCase.isWhitelisted(uid, tracker.id) + } + }.sortedBy { it.first.label.lowercase() } + } +} diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt new file mode 100644 index 0000000..27f3e78 --- /dev/null +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 MURENA SAS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package foundation.e.advancedprivacy.domain.usecases + +import foundation.e.advancedprivacy.data.repositories.AppListsRepository +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.trackers.data.StatsDatabase +import foundation.e.advancedprivacy.trackers.data.WhitelistRepository +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker +import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCase + +class TrackerDetailsUseCase( + private val whitelistRepository: WhitelistRepository, + private val trackersStateUseCase: TrackersStateUseCase, + private val appListsRepository: AppListsRepository, + private val statsDatabase: StatsDatabase, + private val filterHostnameUseCase: FilterHostnameUseCase, +) { + suspend fun toggleTrackerWhitelist(tracker: Tracker, isBlocked: Boolean) { + whitelistRepository.setWhiteListed(tracker, !isBlocked) + whitelistRepository.setWhitelistedAppsForTracker(statsDatabase.getApIds(tracker.id), tracker.id, !isBlocked) + trackersStateUseCase.updateAllTrackersBlockedState() + } + + suspend fun getAppsWithBlockedState(tracker: Tracker): List<Pair<ApplicationDescription, Boolean>> { + return enrichWithBlockedState( + statsDatabase.getApIds(tracker.id).mapNotNull { + appListsRepository.getDisplayableApp(it) + }.sortedBy { it.label?.toString() }, + tracker + ) + } + + suspend fun enrichWithBlockedState(apps: List<ApplicationDescription>, tracker: Tracker): List<Pair<ApplicationDescription, Boolean>> { + return apps.map { it to !filterHostnameUseCase.isWhitelisted(it.uid, tracker.id) } + } + + suspend fun getCalls(tracker: Tracker): Pair<Int, Int> { + return statsDatabase.getCallsForTracker(tracker.id) + } +} diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt new file mode 100644 index 0000000..8292a6d --- /dev/null +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023 MURENA SAS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package foundation.e.advancedprivacy.domain.usecases + +import foundation.e.advancedprivacy.data.repositories.AppListsRepository +import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.features.trackers.AppWithTrackersCount +import foundation.e.advancedprivacy.features.trackers.TrackerWithAppsCount +import foundation.e.advancedprivacy.trackers.data.StatsDatabase +import foundation.e.advancedprivacy.trackers.data.TrackersRepository +import foundation.e.advancedprivacy.trackers.domain.entities.Tracker +import kotlinx.coroutines.flow.first + +class TrackersAndAppsListsUseCase( + private val statsDatabase: StatsDatabase, + private val trackersRepository: TrackersRepository, + private val appListsRepository: AppListsRepository, +) { + + suspend fun getAppsAndTrackersCounts(): Pair<List<AppWithTrackersCount>, List<TrackerWithAppsCount>> { + val trackersAndAppsIds = statsDatabase.getDistinctTrackerAndApp() + val trackersAndApps = mapIdsToEntities(trackersAndAppsIds) + val (countByApp, countByTracker) = foldToCountByEntityMaps(trackersAndApps) + + val appList = buildAppList(countByApp) + val trackerList = buildTrackerList(countByTracker) + return appList to trackerList + } + + private fun buildTrackerList(countByTracker: Map<Tracker, Int>): List<TrackerWithAppsCount> { + return countByTracker.map { (tracker, count) -> + TrackerWithAppsCount(tracker = tracker, appsCount = count) + }.sortedByDescending { it.appsCount } + } + + private suspend fun buildAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { + return appListsRepository.apps().first().map { app: ApplicationDescription -> + AppWithTrackersCount(app = app, trackersCount = countByApp[app] ?: 0) + }.sortedByDescending { it.trackersCount } + } + + private suspend fun mapIdsToEntities(trackersAndAppsIds: List<Pair<String, String>>): List<Pair<Tracker, ApplicationDescription>> { + return trackersAndAppsIds.mapNotNull { (trackerId, apId) -> + trackersRepository.getTracker(trackerId)?.let { tracker -> + appListsRepository.getDisplayableApp(apId)?.let { app -> + tracker to app + } + } + // appListsRepository.getDisplayableApp() may transform many apId to one + // ApplicationDescription, so the lists is not distinct anymore. + }.distinct() + } + + private fun foldToCountByEntityMaps(trackersAndApps: List<Pair<Tracker, ApplicationDescription>>): + Pair<Map<ApplicationDescription, Int>, Map<Tracker, Int>> { + return trackersAndApps.fold( + mutableMapOf<ApplicationDescription, Int>() to mutableMapOf<Tracker, Int>() + ) { (countByApp, countByTracker), (tracker, app) -> + countByApp[app] = countByApp.getOrDefault(app, 0) + 1 + countByTracker[tracker] = countByTracker.getOrDefault(tracker, 0) + 1 + countByApp to countByTracker + } + } +} diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt index 2c47d70..dddc6a2 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt @@ -41,7 +41,7 @@ class TrackersStateUseCase( } } - private fun updateAllTrackersBlockedState() { + fun updateAllTrackersBlockedState() { localStateRepository.areAllTrackersBlocked.value = whitelistRepository.isBlockingEnabled && whitelistRepository.areWhiteListEmpty() } @@ -50,28 +50,16 @@ class TrackersStateUseCase( return isWhitelisted(app, appListsRepository, whitelistRepository) } - fun toggleAppWhitelist(app: ApplicationDescription, isWhitelisted: Boolean) { - appListsRepository.applyForHiddenApps(app) { - whitelistRepository.setWhiteListed(it.apId, isWhitelisted) - } - updateAllTrackersBlockedState() + fun isWhitelisted(tracker: Tracker): Boolean { + return whitelistRepository.isWhiteListed(tracker) } - fun blockTracker(app: ApplicationDescription, tracker: Tracker, isBlocked: Boolean) { + suspend fun blockTracker(app: ApplicationDescription, tracker: Tracker, isBlocked: Boolean) { appListsRepository.applyForHiddenApps(app) { whitelistRepository.setWhiteListed(tracker, it.apId, !isBlocked) } updateAllTrackersBlockedState() } - - fun clearWhitelist(app: ApplicationDescription) { - appListsRepository.applyForHiddenApps( - app - ) { - whitelistRepository.clearWhiteList(it.apId) - } - updateAllTrackersBlockedState() - } } fun isWhitelisted( diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt index 3d6ade0..8f290b8 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt @@ -1,5 +1,6 @@ /* - * Copyright (C) 2021 E FOUNDATION, 2022 - 2023 MURENA SAS + * Copyright (C) 2022 - 2023 MURENA SAS + * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +22,6 @@ import android.content.res.Resources import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.throttleFirst import foundation.e.advancedprivacy.data.repositories.AppListsRepository -import foundation.e.advancedprivacy.domain.entities.AppWithCounts import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.TrackersPeriodicStatistics import foundation.e.advancedprivacy.trackers.data.StatsDatabase @@ -167,27 +167,7 @@ class TrackersStatisticsUseCase( ) } - fun getTrackersWithWhiteList(app: ApplicationDescription): List<Pair<Tracker, Boolean>> { - return appListsRepository.mapReduceForHiddenApps( - app = app, - map = { appDesc: ApplicationDescription -> - ( - statisticsUseCase.getTrackers(listOf(appDesc)) to - getWhiteList(appDesc) - ) - }, - reduce = { lists -> - lists.unzip().let { (trackerLists, whiteListedIdLists) -> - val whiteListedIds = whiteListedIdLists.flatten().map { it.id }.toSet() - - trackerLists.flatten().distinctBy { it.id }.sortedBy { it.label.lowercase() } - .map { tracker -> tracker to (tracker.id in whiteListedIds) } - } - } - ) - } - - fun isWhiteListEmpty(app: ApplicationDescription): Boolean { + suspend fun isWhiteListEmpty(app: ApplicationDescription): Boolean { return appListsRepository.mapReduceForHiddenApps( app = app, map = { appDesc: ApplicationDescription -> @@ -197,7 +177,7 @@ class TrackersStatisticsUseCase( ) } - fun getCalls(app: ApplicationDescription): Pair<Int, Int> { + suspend fun getCalls(app: ApplicationDescription): Pair<Int, Int> { return appListsRepository.mapReduceForHiddenApps( app = app, map = { @@ -211,67 +191,9 @@ class TrackersStatisticsUseCase( ) } - fun getAppsWithCounts(): Flow<List<AppWithCounts>> { - val trackersCounts = statisticsUseCase.getContactedTrackersCountByApp() - val hiddenAppsTrackersWithWhiteList = - getTrackersWithWhiteList(appListsRepository.dummySystemApp) - val acAppsTrackersWithWhiteList = - getTrackersWithWhiteList(appListsRepository.dummyCompatibilityApp) - - return appListsRepository.apps() - .map { apps -> - val callsByApp = statisticsUseCase.getCallsByApps(24, ChronoUnit.HOURS) - apps.map { app -> - val calls = appListsRepository.mapReduceForHiddenApps( - app = app, - map = { callsByApp.getOrDefault(app, 0 to 0) }, - reduce = { - it.unzip().let { (blocked, leaked) -> - blocked.sum() to leaked.sum() - } - } - ) - - AppWithCounts( - app = app, - isWhitelisted = !whitelistRepository.isBlockingEnabled || - isWhitelisted(app, appListsRepository, whitelistRepository), - trackersCount = when (app) { - appListsRepository.dummySystemApp -> - hiddenAppsTrackersWithWhiteList.size - appListsRepository.dummyCompatibilityApp -> - acAppsTrackersWithWhiteList.size - else -> trackersCounts.getOrDefault(app, 0) - }, - whiteListedTrackersCount = when (app) { - appListsRepository.dummySystemApp -> - hiddenAppsTrackersWithWhiteList.count { it.second } - appListsRepository.dummyCompatibilityApp -> - acAppsTrackersWithWhiteList.count { it.second } - else -> - getWhiteList(app).size - }, - blockedLeaks = calls.first, - leaks = calls.second - ) - } - .sortedWith(mostLeakedAppsComparator) - } - } - private fun getWhiteList(app: ApplicationDescription): List<Tracker> { return whitelistRepository.getWhiteListForApp(app).mapNotNull { trackersRepository.getTracker(it) } } - - private val mostLeakedAppsComparator: Comparator<AppWithCounts> = Comparator { o1, o2 -> - val leaks = o2.leaks - o1.leaks - if (leaks != 0) leaks else { - val whitelisted = o2.whiteListedTrackersCount - o1.whiteListedTrackersCount - if (whitelisted != 0) whitelisted else { - o2.trackersCount - o1.trackersCount - } - } - } } |