Loading app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +38 −34 Original line number Diff line number Diff line Loading @@ -18,16 +18,16 @@ package foundation.e.advancedprivacy import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Process import foundation.e.advancedprivacy.core.coreModule import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.data.repositories.LocalStateRepositoryImpl import foundation.e.advancedprivacy.data.repositories.ResourcesRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG import foundation.e.advancedprivacy.domain.entities.NotificationContent import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.domain.entities.ProfileType import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository import foundation.e.advancedprivacy.domain.usecases.AppListUseCase import foundation.e.advancedprivacy.domain.usecases.AppTrackersUseCase import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase Loading Loading @@ -55,16 +55,31 @@ import foundation.e.advancedprivacy.trackers.data.TrackersRepository import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import foundation.e.advancedprivacy.trackers.service.trackerServiceModule import foundation.e.advancedprivacy.trackers.trackersModule import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.core.module.dsl.singleOf import org.koin.core.qualifier.named import org.koin.dsl.module import timber.log.Timber val appModule = module { includes(coreModule, trackersModule, fakelocationModule, ipScramblerModule, trackerServiceModule) single<CoroutineScope> { CoroutineScope( SupervisorJob() + Dispatchers.IO + CoroutineExceptionHandler { _, throwable -> Timber.e("Uncaught error in backgroundScope", throwable) } ) } factory<Resources> { androidContext().resources } single<LocalStateRepository> { LocalStateRepositoryImpl(context = androidContext()) Loading @@ -81,42 +96,23 @@ val appModule = module { ) } single<ApplicationDescription>(named("DummySystemApp")) { ApplicationDescription( packageName = "foundation.e.dummysystemapp", uid = -1, label = androidContext().getString(R.string.dummy_system_app_label), icon = androidContext().getDrawable(R.drawable.ic_e_app_logo), profileId = -1, profileType = ProfileType.MAIN ) single<CharSequence>(named("SystemAppLabel")) { androidContext().getString(R.string.dummy_system_app_label) } single<ApplicationDescription>(named("DummyCompatibilityApp")) { ApplicationDescription( packageName = "foundation.e.dummyappscompatibilityapp", uid = -2, label = androidContext().getString(R.string.dummy_apps_compatibility_app_label), icon = androidContext().getDrawable(R.drawable.ic_apps_compatibility_components), profileId = -1, profileType = ProfileType.MAIN ) single<Drawable>(named("SystemAppIcon")) { androidContext().getDrawable(R.drawable.ic_e_app_logo)!! } single<NotificationContent>(named("notificationTrackerFlag")) { NotificationContent( channelId = CHANNEL_TRACKER_FLAG, icon = R.drawable.ic_e_app_logo, title = R.string.notifications_tracker_title, description = R.string.notifications_tracker_content, pendingIntent = null ) single<CharSequence>(named("CompatibilityAppLabel")) { androidContext().getString(R.string.dummy_apps_compatibility_app_label) } single<Drawable>(named("CompatibilityAppIcon")) { androidContext().getDrawable(R.drawable.ic_apps_compatibility_components)!! } single { CityDataSource } single { ResourcesRepository(androidContext()) } singleOf(::AppListUseCase) single { FakeLocationStateUseCase( fakeLocationModule = get(), Loading @@ -135,7 +131,7 @@ val appModule = module { orbotSupervisor = get(), localStateRepository = get(), appListsRepository = get(), coroutineScope = get() backgroundScope = get() ) } Loading @@ -155,8 +151,16 @@ val appModule = module { } viewModel { parameters -> val appListUseCase: AppListUseCase = get() val app = appListUseCase.getApp(parameters.get()) val appListsRepository: AppListsRepository = get() val app = appListsRepository.getAppById(parameters.get()) ?: DisplayableApp( id = "dummy-app", label = androidContext().resources.getString(R.string.app_name), icon = get(named("SystemAppIcon")), apps = setOf(get(named("AdvancedPrivacy"))), hasLocationPermission = true, hasInternetPermission = true, profileType = ProfileType.MAIN ) AppTrackersViewModel( app = app, Loading app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt +2 −1 Original line number Diff line number Diff line /* * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify Loading @@ -25,7 +26,7 @@ data class TrackersAndAppsLists( ) data class AppWithCount( val app: ApplicationDescription, val app: DisplayableApp, val count: Int = 0 ) Loading app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt +16 −25 Original line number Diff line number Diff line /* * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify Loading @@ -16,8 +17,7 @@ */ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.trackers.data.StatsDatabase import foundation.e.advancedprivacy.trackers.data.TrackersRepository import foundation.e.advancedprivacy.trackers.data.WhitelistRepository Loading @@ -27,13 +27,12 @@ import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCa 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, trackers: List<Tracker>, isBlocked: Boolean) { val realApIds = appListsRepository.getRealApps(app).map { it.apId } suspend fun toggleAppWhitelist(app: DisplayableApp, trackers: List<Tracker>, isBlocked: Boolean) { val realApIds = app.apps.map { it.apId } val trackerIds = trackers.map { it.id } whitelistRepository.setWhiteListed(realApIds, !isBlocked) Loading @@ -41,39 +40,31 @@ class AppTrackersUseCase( trackersStateUseCase.updateAllTrackersBlockedState() } suspend fun clearWhitelist(app: ApplicationDescription) { appListsRepository.applyForHiddenApps( app ) { suspend fun clearWhitelist(app: DisplayableApp) { app.apps.forEach { 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) -> suspend fun getCalls(app: DisplayableApp): Pair<Int, Int> { return app.apps.map { statsDatabase.getCallsForApp(it.apId) }.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 } suspend fun getTrackersWithBlockedList(app: DisplayableApp): List<Pair<Tracker, Boolean>> { val realApIds = app.apps.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 } suspend fun enrichWithBlockedState(app: DisplayableApp, trackers: List<Tracker>): List<Pair<Tracker, Boolean>> { val realAppUids = app.apps.map { it.uid } return trackers.map { tracker -> tracker to !realAppUids.any { uid -> filterHostnameUseCase.isWhitelisted(uid, tracker.id) Loading app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt +39 −40 Original line number Diff line number Diff line /* * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021 - 2024 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 Loading @@ -19,77 +19,72 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.entities.ProfileType import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository import foundation.e.advancedprivacy.ipscrambler.OrbotSupervisor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update class IpScramblingStateUseCase( private val orbotSupervisor: OrbotSupervisor, private val localStateRepository: LocalStateRepository, private val appListsRepository: AppListsRepository, private val coroutineScope: CoroutineScope private val backgroundScope: CoroutineScope ) { val internetPrivacyMode: StateFlow<FeatureState> = orbotSupervisor.state private val whitelistedPackages = MutableStateFlow(orbotSupervisor.appList) init { orbotSupervisor.requestStatus() orbotSupervisor.state.map { localStateRepository.internetPrivacyMode.value = it }.launchIn(coroutineScope) }.launchIn(backgroundScope) whitelistedPackages.map { orbotSupervisor.appList = it }.launchIn(backgroundScope) } fun toggle(hideIp: Boolean) { localStateRepository.setIpScramblingSetting(enabled = hideIp) } private fun getHiddenPackageNames(): List<String> { return appListsRepository.getMainProfileHiddenSystemApps().map { it.packageName } suspend fun getAppsWithUseTor(): Flow<List<Pair<DisplayableApp, Boolean>>> { return combine( appListsRepository.displayableApp.map { apps -> apps.filter { app -> app.hasInternetPermission && app.profileType == ProfileType.MAIN } val bypassTorApps: Set<String> get() { var whitelist = orbotSupervisor.appList if (getHiddenPackageNames().any { it in whitelist }) { val mutable = whitelist.toMutableSet() mutable.removeAll(getHiddenPackageNames()) mutable.add(appListsRepository.dummySystemApp.packageName) whitelist = mutable }, whitelistedPackages ) { apps, pNames -> apps.map { app -> app to !app.isWhitelisted(pNames) } if (AppListsRepository.compatibiltyPNames.any { it in whitelist }) { val mutable = whitelist.toMutableSet() mutable.removeAll(AppListsRepository.compatibiltyPNames) mutable.add(appListsRepository.dummyCompatibilityApp.packageName) whitelist = mutable } return whitelist } fun toggleBypassTor(packageName: String) { val visibleList = bypassTorApps.toMutableSet() val rawList = orbotSupervisor.appList.toMutableSet() if (visibleList.contains(packageName)) { if (packageName == appListsRepository.dummySystemApp.packageName) { rawList.removeAll(getHiddenPackageNames()) } else if (packageName == appListsRepository.dummyCompatibilityApp.packageName) { rawList.removeAll(AppListsRepository.compatibiltyPNames) } else { rawList.remove(packageName) } } else { if (packageName == appListsRepository.dummySystemApp.packageName) { rawList.addAll(getHiddenPackageNames()) } else if (packageName == appListsRepository.dummyCompatibilityApp.packageName) { rawList.addAll(AppListsRepository.compatibiltyPNames) fun toggleBypassTor(app: DisplayableApp) { whitelistedPackages.update { whitelist -> val mutable = whitelist.toMutableSet() val packageNames = app.apps.map { it.packageName } if (app.isWhitelisted()) { mutable.removeAll(packageNames) } else { rawList.add(packageName) mutable.addAll(packageNames) } mutable } orbotSupervisor.appList = rawList } val availablesLocations: List<String> = orbotSupervisor.getAvailablesLocations().sorted() Loading @@ -101,4 +96,8 @@ class IpScramblingStateUseCase( orbotSupervisor.setExitCountryCode(locationId) } } private fun DisplayableApp.isWhitelisted(whitelistedPNames: Set<String> = whitelistedPackages.value): Boolean = apps.any { it.packageName in whitelistedPNames } } app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt +9 −10 Original line number Diff line number Diff line /* * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify Loading @@ -17,7 +18,7 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.trackers.data.StatsDatabase import foundation.e.advancedprivacy.trackers.data.WhitelistRepository import foundation.e.advancedprivacy.trackers.domain.entities.Tracker Loading @@ -30,30 +31,28 @@ class TrackerDetailsUseCase( private val statsDatabase: StatsDatabase, private val filterHostnameUseCase: FilterHostnameUseCase ) { suspend fun toggleTrackerWhitelist(tracker: Tracker, apps: List<ApplicationDescription>, isBlocked: Boolean) { suspend fun toggleTrackerWhitelist(tracker: Tracker, apps: List<DisplayableApp>, isBlocked: Boolean) { whitelistRepository.setWhiteListed(tracker, !isBlocked) whitelistRepository.setWhitelistedAppsForTracker( apps.flatMap { appListsRepository.getRealApps(it) }.map { it.apId }, apps.flatMap { it.apps }.map { it.apId }, tracker.id, !isBlocked ) trackersStateUseCase.updateAllTrackersBlockedState() } suspend fun getAppsWithBlockedState(tracker: Tracker): List<Pair<ApplicationDescription, Boolean>> { suspend fun getAppsWithBlockedState(tracker: Tracker): List<Pair<DisplayableApp, Boolean>> { return enrichWithBlockedState( statsDatabase.getApIds(tracker.id).mapNotNull { appListsRepository.getDisplayableApp(it) }.sortedBy { it.label?.toString() }, appListsRepository.getInternetAppByApId(it) }.distinct().sortedBy { it.label.toString() }, tracker ) } suspend fun enrichWithBlockedState(apps: List<ApplicationDescription>, tracker: Tracker): List<Pair<ApplicationDescription, Boolean>> { suspend fun enrichWithBlockedState(apps: List<DisplayableApp>, tracker: Tracker): List<Pair<DisplayableApp, Boolean>> { return apps.map { app -> app to appListsRepository.anyForHiddenApps(app) { realApp -> !filterHostnameUseCase.isWhitelisted(realApp.uid, tracker.id) } app to app.apps.any { !filterHostnameUseCase.isWhitelisted(it.uid, tracker.id) } } } Loading Loading
app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +38 −34 Original line number Diff line number Diff line Loading @@ -18,16 +18,16 @@ package foundation.e.advancedprivacy import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.Process import foundation.e.advancedprivacy.core.coreModule import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.data.repositories.LocalStateRepositoryImpl import foundation.e.advancedprivacy.data.repositories.ResourcesRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG import foundation.e.advancedprivacy.domain.entities.NotificationContent import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.domain.entities.ProfileType import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository import foundation.e.advancedprivacy.domain.usecases.AppListUseCase import foundation.e.advancedprivacy.domain.usecases.AppTrackersUseCase import foundation.e.advancedprivacy.domain.usecases.FakeLocationStateUseCase import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase Loading Loading @@ -55,16 +55,31 @@ import foundation.e.advancedprivacy.trackers.data.TrackersRepository import foundation.e.advancedprivacy.trackers.domain.entities.Tracker import foundation.e.advancedprivacy.trackers.service.trackerServiceModule import foundation.e.advancedprivacy.trackers.trackersModule import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModelOf import org.koin.core.module.dsl.singleOf import org.koin.core.qualifier.named import org.koin.dsl.module import timber.log.Timber val appModule = module { includes(coreModule, trackersModule, fakelocationModule, ipScramblerModule, trackerServiceModule) single<CoroutineScope> { CoroutineScope( SupervisorJob() + Dispatchers.IO + CoroutineExceptionHandler { _, throwable -> Timber.e("Uncaught error in backgroundScope", throwable) } ) } factory<Resources> { androidContext().resources } single<LocalStateRepository> { LocalStateRepositoryImpl(context = androidContext()) Loading @@ -81,42 +96,23 @@ val appModule = module { ) } single<ApplicationDescription>(named("DummySystemApp")) { ApplicationDescription( packageName = "foundation.e.dummysystemapp", uid = -1, label = androidContext().getString(R.string.dummy_system_app_label), icon = androidContext().getDrawable(R.drawable.ic_e_app_logo), profileId = -1, profileType = ProfileType.MAIN ) single<CharSequence>(named("SystemAppLabel")) { androidContext().getString(R.string.dummy_system_app_label) } single<ApplicationDescription>(named("DummyCompatibilityApp")) { ApplicationDescription( packageName = "foundation.e.dummyappscompatibilityapp", uid = -2, label = androidContext().getString(R.string.dummy_apps_compatibility_app_label), icon = androidContext().getDrawable(R.drawable.ic_apps_compatibility_components), profileId = -1, profileType = ProfileType.MAIN ) single<Drawable>(named("SystemAppIcon")) { androidContext().getDrawable(R.drawable.ic_e_app_logo)!! } single<NotificationContent>(named("notificationTrackerFlag")) { NotificationContent( channelId = CHANNEL_TRACKER_FLAG, icon = R.drawable.ic_e_app_logo, title = R.string.notifications_tracker_title, description = R.string.notifications_tracker_content, pendingIntent = null ) single<CharSequence>(named("CompatibilityAppLabel")) { androidContext().getString(R.string.dummy_apps_compatibility_app_label) } single<Drawable>(named("CompatibilityAppIcon")) { androidContext().getDrawable(R.drawable.ic_apps_compatibility_components)!! } single { CityDataSource } single { ResourcesRepository(androidContext()) } singleOf(::AppListUseCase) single { FakeLocationStateUseCase( fakeLocationModule = get(), Loading @@ -135,7 +131,7 @@ val appModule = module { orbotSupervisor = get(), localStateRepository = get(), appListsRepository = get(), coroutineScope = get() backgroundScope = get() ) } Loading @@ -155,8 +151,16 @@ val appModule = module { } viewModel { parameters -> val appListUseCase: AppListUseCase = get() val app = appListUseCase.getApp(parameters.get()) val appListsRepository: AppListsRepository = get() val app = appListsRepository.getAppById(parameters.get()) ?: DisplayableApp( id = "dummy-app", label = androidContext().resources.getString(R.string.app_name), icon = get(named("SystemAppIcon")), apps = setOf(get(named("AdvancedPrivacy"))), hasLocationPermission = true, hasInternetPermission = true, profileType = ProfileType.MAIN ) AppTrackersViewModel( app = app, Loading
app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt +2 −1 Original line number Diff line number Diff line /* * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify Loading @@ -25,7 +26,7 @@ data class TrackersAndAppsLists( ) data class AppWithCount( val app: ApplicationDescription, val app: DisplayableApp, val count: Int = 0 ) Loading
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt +16 −25 Original line number Diff line number Diff line /* * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify Loading @@ -16,8 +17,7 @@ */ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.trackers.data.StatsDatabase import foundation.e.advancedprivacy.trackers.data.TrackersRepository import foundation.e.advancedprivacy.trackers.data.WhitelistRepository Loading @@ -27,13 +27,12 @@ import foundation.e.advancedprivacy.trackers.domain.usecases.FilterHostnameUseCa 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, trackers: List<Tracker>, isBlocked: Boolean) { val realApIds = appListsRepository.getRealApps(app).map { it.apId } suspend fun toggleAppWhitelist(app: DisplayableApp, trackers: List<Tracker>, isBlocked: Boolean) { val realApIds = app.apps.map { it.apId } val trackerIds = trackers.map { it.id } whitelistRepository.setWhiteListed(realApIds, !isBlocked) Loading @@ -41,39 +40,31 @@ class AppTrackersUseCase( trackersStateUseCase.updateAllTrackersBlockedState() } suspend fun clearWhitelist(app: ApplicationDescription) { appListsRepository.applyForHiddenApps( app ) { suspend fun clearWhitelist(app: DisplayableApp) { app.apps.forEach { 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) -> suspend fun getCalls(app: DisplayableApp): Pair<Int, Int> { return app.apps.map { statsDatabase.getCallsForApp(it.apId) }.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 } suspend fun getTrackersWithBlockedList(app: DisplayableApp): List<Pair<Tracker, Boolean>> { val realApIds = app.apps.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 } suspend fun enrichWithBlockedState(app: DisplayableApp, trackers: List<Tracker>): List<Pair<Tracker, Boolean>> { val realAppUids = app.apps.map { it.uid } return trackers.map { tracker -> tracker to !realAppUids.any { uid -> filterHostnameUseCase.isWhitelisted(uid, tracker.id) Loading
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt +39 −40 Original line number Diff line number Diff line /* * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021 - 2024 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 Loading @@ -19,77 +19,72 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.entities.ProfileType import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository import foundation.e.advancedprivacy.ipscrambler.OrbotSupervisor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update class IpScramblingStateUseCase( private val orbotSupervisor: OrbotSupervisor, private val localStateRepository: LocalStateRepository, private val appListsRepository: AppListsRepository, private val coroutineScope: CoroutineScope private val backgroundScope: CoroutineScope ) { val internetPrivacyMode: StateFlow<FeatureState> = orbotSupervisor.state private val whitelistedPackages = MutableStateFlow(orbotSupervisor.appList) init { orbotSupervisor.requestStatus() orbotSupervisor.state.map { localStateRepository.internetPrivacyMode.value = it }.launchIn(coroutineScope) }.launchIn(backgroundScope) whitelistedPackages.map { orbotSupervisor.appList = it }.launchIn(backgroundScope) } fun toggle(hideIp: Boolean) { localStateRepository.setIpScramblingSetting(enabled = hideIp) } private fun getHiddenPackageNames(): List<String> { return appListsRepository.getMainProfileHiddenSystemApps().map { it.packageName } suspend fun getAppsWithUseTor(): Flow<List<Pair<DisplayableApp, Boolean>>> { return combine( appListsRepository.displayableApp.map { apps -> apps.filter { app -> app.hasInternetPermission && app.profileType == ProfileType.MAIN } val bypassTorApps: Set<String> get() { var whitelist = orbotSupervisor.appList if (getHiddenPackageNames().any { it in whitelist }) { val mutable = whitelist.toMutableSet() mutable.removeAll(getHiddenPackageNames()) mutable.add(appListsRepository.dummySystemApp.packageName) whitelist = mutable }, whitelistedPackages ) { apps, pNames -> apps.map { app -> app to !app.isWhitelisted(pNames) } if (AppListsRepository.compatibiltyPNames.any { it in whitelist }) { val mutable = whitelist.toMutableSet() mutable.removeAll(AppListsRepository.compatibiltyPNames) mutable.add(appListsRepository.dummyCompatibilityApp.packageName) whitelist = mutable } return whitelist } fun toggleBypassTor(packageName: String) { val visibleList = bypassTorApps.toMutableSet() val rawList = orbotSupervisor.appList.toMutableSet() if (visibleList.contains(packageName)) { if (packageName == appListsRepository.dummySystemApp.packageName) { rawList.removeAll(getHiddenPackageNames()) } else if (packageName == appListsRepository.dummyCompatibilityApp.packageName) { rawList.removeAll(AppListsRepository.compatibiltyPNames) } else { rawList.remove(packageName) } } else { if (packageName == appListsRepository.dummySystemApp.packageName) { rawList.addAll(getHiddenPackageNames()) } else if (packageName == appListsRepository.dummyCompatibilityApp.packageName) { rawList.addAll(AppListsRepository.compatibiltyPNames) fun toggleBypassTor(app: DisplayableApp) { whitelistedPackages.update { whitelist -> val mutable = whitelist.toMutableSet() val packageNames = app.apps.map { it.packageName } if (app.isWhitelisted()) { mutable.removeAll(packageNames) } else { rawList.add(packageName) mutable.addAll(packageNames) } mutable } orbotSupervisor.appList = rawList } val availablesLocations: List<String> = orbotSupervisor.getAvailablesLocations().sorted() Loading @@ -101,4 +96,8 @@ class IpScramblingStateUseCase( orbotSupervisor.setExitCountryCode(locationId) } } private fun DisplayableApp.isWhitelisted(whitelistedPNames: Set<String> = whitelistedPackages.value): Boolean = apps.any { it.packageName in whitelistedPNames } }
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt +9 −10 Original line number Diff line number Diff line /* * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify Loading @@ -17,7 +18,7 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.trackers.data.StatsDatabase import foundation.e.advancedprivacy.trackers.data.WhitelistRepository import foundation.e.advancedprivacy.trackers.domain.entities.Tracker Loading @@ -30,30 +31,28 @@ class TrackerDetailsUseCase( private val statsDatabase: StatsDatabase, private val filterHostnameUseCase: FilterHostnameUseCase ) { suspend fun toggleTrackerWhitelist(tracker: Tracker, apps: List<ApplicationDescription>, isBlocked: Boolean) { suspend fun toggleTrackerWhitelist(tracker: Tracker, apps: List<DisplayableApp>, isBlocked: Boolean) { whitelistRepository.setWhiteListed(tracker, !isBlocked) whitelistRepository.setWhitelistedAppsForTracker( apps.flatMap { appListsRepository.getRealApps(it) }.map { it.apId }, apps.flatMap { it.apps }.map { it.apId }, tracker.id, !isBlocked ) trackersStateUseCase.updateAllTrackersBlockedState() } suspend fun getAppsWithBlockedState(tracker: Tracker): List<Pair<ApplicationDescription, Boolean>> { suspend fun getAppsWithBlockedState(tracker: Tracker): List<Pair<DisplayableApp, Boolean>> { return enrichWithBlockedState( statsDatabase.getApIds(tracker.id).mapNotNull { appListsRepository.getDisplayableApp(it) }.sortedBy { it.label?.toString() }, appListsRepository.getInternetAppByApId(it) }.distinct().sortedBy { it.label.toString() }, tracker ) } suspend fun enrichWithBlockedState(apps: List<ApplicationDescription>, tracker: Tracker): List<Pair<ApplicationDescription, Boolean>> { suspend fun enrichWithBlockedState(apps: List<DisplayableApp>, tracker: Tracker): List<Pair<DisplayableApp, Boolean>> { return apps.map { app -> app to appListsRepository.anyForHiddenApps(app) { realApp -> !filterHostnameUseCase.isWhitelisted(realApp.uid, tracker.id) } app to app.apps.any { !filterHostnameUseCase.isWhitelisted(it.uid, tracker.id) } } } Loading