Loading app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -163,7 +163,7 @@ dependencies { libs.androidx.fragment.ktx, libs.androidx.lifecycle.runtime, libs.androidx.lifecycle.viewmodel, libs.androidx.viewpager2, libs.bundles.koin, libs.google.material, Loading app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +26 −1 Original line number Diff line number Diff line Loading @@ -27,10 +27,13 @@ import foundation.e.advancedprivacy.domain.entities.NotificationContent 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 import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase import foundation.e.advancedprivacy.domain.usecases.TrackerDetailsUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersAndAppsListsUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersStateUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase import foundation.e.advancedprivacy.dummy.CityDataSource Loading @@ -41,8 +44,11 @@ import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyView import foundation.e.advancedprivacy.features.location.FakeLocationViewModel import foundation.e.advancedprivacy.features.trackers.TrackersViewModel import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersViewModel import foundation.e.advancedprivacy.features.trackers.trackerdetails.TrackerDetailsViewModel import foundation.e.advancedprivacy.ipscrambler.ipScramblerModule import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModuleImpl 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 org.koin.android.ext.koin.androidContext Loading Loading @@ -131,6 +137,10 @@ val appModule = module { singleOf(::ShowFeaturesWarningUseCase) singleOf(::TrackersStateUseCase) singleOf(::TrackersStatisticsUseCase) singleOf(::TrackersAndAppsListsUseCase) singleOf(::AppTrackersUseCase) singleOf(::TrackerDetailsUseCase) single<IPermissionsPrivacyModule> { PermissionsPrivacyModuleImpl(context = androidContext()) Loading @@ -144,9 +154,24 @@ val appModule = module { app = app, trackersStateUseCase = get(), trackersStatisticsUseCase = get(), getQuickPrivacyStateUseCase = get() getQuickPrivacyStateUseCase = get(), appTrackersUseCase = get() ) } viewModel { parameters -> val trackersRepository: TrackersRepository = get() val tracker = trackersRepository.getTracker(parameters.get()) ?: Tracker("-1", emptySet(), "dummy", null) TrackerDetailsViewModel( tracker = tracker, trackersStateUseCase = get(), trackersStatisticsUseCase = get(), getQuickPrivacyStateUseCase = get(), trackerDetailsUseCase = get() ) } viewModelOf(::TrackersViewModel) viewModelOf(::FakeLocationViewModel) viewModelOf(::InternetPrivacyViewModel) Loading app/src/main/java/foundation/e/advancedprivacy/common/extensions/ViewPager2Extensions.kt 0 → 100644 +43 −0 Original line number Diff line number Diff line /* * 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.common.extensions import android.view.View import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 fun ViewPager2.findViewHolderForAdapterPosition(position: Int): RecyclerView.ViewHolder? { return (getChildAt(0) as RecyclerView).findViewHolderForAdapterPosition(position) } fun ViewPager2.updatePagerHeightForChild(itemView: View) { itemView.post { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(itemView.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) itemView.measure(wMeasureSpec, hMeasureSpec) if (layoutParams.height != itemView.measuredHeight) { layoutParams = (layoutParams) .also { lp -> // applying Fragment Root View Height to // the pager LayoutParams, so they match lp.height = itemView.measuredHeight } } } } app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * 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() } } } app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * 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) } } Loading
app/build.gradle +1 −1 Original line number Diff line number Diff line Loading @@ -163,7 +163,7 @@ dependencies { libs.androidx.fragment.ktx, libs.androidx.lifecycle.runtime, libs.androidx.lifecycle.viewmodel, libs.androidx.viewpager2, libs.bundles.koin, libs.google.material, Loading
app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +26 −1 Original line number Diff line number Diff line Loading @@ -27,10 +27,13 @@ import foundation.e.advancedprivacy.domain.entities.NotificationContent 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 import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase import foundation.e.advancedprivacy.domain.usecases.TrackerDetailsUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersAndAppsListsUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersStateUseCase import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase import foundation.e.advancedprivacy.dummy.CityDataSource Loading @@ -41,8 +44,11 @@ import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyView import foundation.e.advancedprivacy.features.location.FakeLocationViewModel import foundation.e.advancedprivacy.features.trackers.TrackersViewModel import foundation.e.advancedprivacy.features.trackers.apptrackers.AppTrackersViewModel import foundation.e.advancedprivacy.features.trackers.trackerdetails.TrackerDetailsViewModel import foundation.e.advancedprivacy.ipscrambler.ipScramblerModule import foundation.e.advancedprivacy.permissions.externalinterfaces.PermissionsPrivacyModuleImpl 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 org.koin.android.ext.koin.androidContext Loading Loading @@ -131,6 +137,10 @@ val appModule = module { singleOf(::ShowFeaturesWarningUseCase) singleOf(::TrackersStateUseCase) singleOf(::TrackersStatisticsUseCase) singleOf(::TrackersAndAppsListsUseCase) singleOf(::AppTrackersUseCase) singleOf(::TrackerDetailsUseCase) single<IPermissionsPrivacyModule> { PermissionsPrivacyModuleImpl(context = androidContext()) Loading @@ -144,9 +154,24 @@ val appModule = module { app = app, trackersStateUseCase = get(), trackersStatisticsUseCase = get(), getQuickPrivacyStateUseCase = get() getQuickPrivacyStateUseCase = get(), appTrackersUseCase = get() ) } viewModel { parameters -> val trackersRepository: TrackersRepository = get() val tracker = trackersRepository.getTracker(parameters.get()) ?: Tracker("-1", emptySet(), "dummy", null) TrackerDetailsViewModel( tracker = tracker, trackersStateUseCase = get(), trackersStatisticsUseCase = get(), getQuickPrivacyStateUseCase = get(), trackerDetailsUseCase = get() ) } viewModelOf(::TrackersViewModel) viewModelOf(::FakeLocationViewModel) viewModelOf(::InternetPrivacyViewModel) Loading
app/src/main/java/foundation/e/advancedprivacy/common/extensions/ViewPager2Extensions.kt 0 → 100644 +43 −0 Original line number Diff line number Diff line /* * 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.common.extensions import android.view.View import androidx.recyclerview.widget.RecyclerView import androidx.viewpager2.widget.ViewPager2 fun ViewPager2.findViewHolderForAdapterPosition(position: Int): RecyclerView.ViewHolder? { return (getChildAt(0) as RecyclerView).findViewHolderForAdapterPosition(position) } fun ViewPager2.updatePagerHeightForChild(itemView: View) { itemView.post { val wMeasureSpec = View.MeasureSpec.makeMeasureSpec(itemView.width, View.MeasureSpec.EXACTLY) val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) itemView.measure(wMeasureSpec, hMeasureSpec) if (layoutParams.height != itemView.measuredHeight) { layoutParams = (layoutParams) .also { lp -> // applying Fragment Root View Height to // the pager LayoutParams, so they match lp.height = itemView.measuredHeight } } } }
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppTrackersUseCase.kt 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * 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() } } }
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackerDetailsUseCase.kt 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * 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) } }