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

Commit 2234842a authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

Merge branch '1203-trackers_oriented_view' into 'main'

1203 trackers oriented view

See merge request !151
parents 0db4d250 2e897cc8
Loading
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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,

+26 −1
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -131,6 +137,10 @@ val appModule = module {
    singleOf(::ShowFeaturesWarningUseCase)
    singleOf(::TrackersStateUseCase)
    singleOf(::TrackersStatisticsUseCase)
    singleOf(::TrackersAndAppsListsUseCase)

    singleOf(::AppTrackersUseCase)
    singleOf(::TrackerDetailsUseCase)

    single<IPermissionsPrivacyModule> {
        PermissionsPrivacyModuleImpl(context = androidContext())
@@ -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)
+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
                }
        }
    }
}
+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() }
    }
}
+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