aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/foundation/e/advancedprivacy/domain
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-05-02 21:25:17 +0200
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-05-02 22:00:35 +0200
commita8874167f663885f2d3371801cf03681576ac817 (patch)
tree5be07b8768142efeade536d4135f2250c1ac9071 /app/src/main/java/foundation/e/advancedprivacy/domain
parenta0ee04ea9dbc0802c828afdf660eb37dc6fa350f (diff)
downloadadvanced-privacy-a8874167f663885f2d3371801cf03681576ac817.tar.gz
1200: rename everything to AdvancedPrivacy
Diffstat (limited to 'app/src/main/java/foundation/e/advancedprivacy/domain')
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt59
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt29
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt22
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt22
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/QuickPrivacyState.kt24
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackerMode.kt22
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersPeriodicStatistics.kt25
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt39
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt209
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt89
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt170
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt54
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt105
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt278
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt33
15 files changed, 1180 insertions, 0 deletions
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt
new file mode 100644
index 0000000..4169ecc
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/AppWithCounts.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ * Copyright (C) 2022 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
+ * 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.entities
+
+import android.graphics.drawable.Drawable
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+
+data class AppWithCounts(
+ val appDesc: ApplicationDescription,
+ val packageName: String,
+ val uid: Int,
+ var label: CharSequence?,
+ var icon: Drawable?,
+ val isWhitelisted: Boolean = false,
+ val trackersCount: Int = 0,
+ val whiteListedTrackersCount: Int = 0,
+ val blockedLeaks: Int = 0,
+ val leaks: Int = 0,
+) {
+ constructor(
+ app: ApplicationDescription,
+ isWhitelisted: Boolean,
+ trackersCount: Int,
+ whiteListedTrackersCount: Int,
+ blockedLeaks: Int,
+ leaks: Int,
+ ) :
+ this(
+ appDesc = app,
+ packageName = app.packageName,
+ uid = app.uid,
+ label = app.label,
+ icon = app.icon,
+ isWhitelisted = isWhitelisted,
+ trackersCount = trackersCount,
+ whiteListedTrackersCount = whiteListedTrackersCount,
+ blockedLeaks = blockedLeaks,
+ leaks = leaks
+ )
+
+ val blockedTrackersCount get() = if (isWhitelisted) 0
+ else Math.max(trackersCount - whiteListedTrackersCount, 0)
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt
new file mode 100644
index 0000000..986e798
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/InternetPrivacyMode.kt
@@ -0,0 +1,29 @@
+/*
+ * 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
+ * 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.entities
+
+enum class InternetPrivacyMode {
+ REAL_IP,
+ HIDE_IP,
+ HIDE_IP_LOADING,
+ REAL_IP_LOADING;
+
+ val isChecked get() = this == HIDE_IP || this == HIDE_IP_LOADING
+
+ val isLoading get() = this == HIDE_IP_LOADING || this == REAL_IP_LOADING
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
new file mode 100644
index 0000000..62581eb
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/LocationMode.kt
@@ -0,0 +1,22 @@
+/*
+ * 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
+ * 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.entities
+
+enum class LocationMode {
+ REAL_LOCATION, RANDOM_LOCATION, SPECIFIC_LOCATION
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
new file mode 100644
index 0000000..c63d3ab
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/MainFeatures.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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
+ * 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.entities
+
+enum class MainFeatures {
+ TRACKERS_CONTROL, FAKE_LOCATION, IP_SCRAMBLING
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/QuickPrivacyState.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/QuickPrivacyState.kt
new file mode 100644
index 0000000..c21bb1d
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/QuickPrivacyState.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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
+ * 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.entities
+
+enum class QuickPrivacyState {
+ DISABLED, ENABLED, FULL_ENABLED;
+
+ fun isEnabled(): Boolean = this != DISABLED
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackerMode.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackerMode.kt
new file mode 100644
index 0000000..2033251
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackerMode.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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
+ * 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.entities
+
+enum class TrackerMode {
+ DENIED, CUSTOM, VULNERABLE
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersPeriodicStatistics.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersPeriodicStatistics.kt
new file mode 100644
index 0000000..c0fa637
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersPeriodicStatistics.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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
+ * 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.entities
+
+data class TrackersPeriodicStatistics(
+ val callsBlockedNLeaked: List<Pair<Int, Int>>,
+ val periods: List<String>,
+ val trackersCount: Int,
+ val graduations: List<String?>? = null
+)
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt
new file mode 100644
index 0000000..8d38ee8
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/AppListUseCase.kt
@@ -0,0 +1,39 @@
+/*
+ * 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
+ * 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.privacymodules.permissions.data.ApplicationDescription
+import kotlinx.coroutines.flow.Flow
+
+class AppListUseCase(
+ private val appListsRepository: AppListsRepository
+) {
+ val dummySystemApp = appListsRepository.dummySystemApp
+ fun getApp(uid: Int): ApplicationDescription {
+ return when (uid) {
+ dummySystemApp.uid -> dummySystemApp
+ appListsRepository.dummyCompatibilityApp.uid ->
+ appListsRepository.dummyCompatibilityApp
+ else -> appListsRepository.getApp(uid) ?: dummySystemApp
+ }
+ }
+ fun getAppsUsingInternet(): Flow<List<ApplicationDescription>> {
+ return appListsRepository.mainProfileApps()
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
new file mode 100644
index 0000000..9b99b95
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt
@@ -0,0 +1,209 @@
+/*
+ * 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
+ * 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 android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.PackageManager
+import android.location.Location
+import android.location.LocationListener
+import android.location.LocationManager
+import android.os.Bundle
+import android.util.Log
+import foundation.e.advancedprivacy.data.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.domain.entities.LocationMode
+import foundation.e.advancedprivacy.dummy.CityDataSource
+import foundation.e.privacymodules.fakelocation.IFakeLocationModule
+import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
+import foundation.e.privacymodules.permissions.data.AppOpModes
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import kotlin.random.Random
+
+class FakeLocationStateUseCase(
+ private val fakeLocationModule: IFakeLocationModule,
+ private val permissionsModule: PermissionsPrivacyModule,
+ private val localStateRepository: LocalStateRepository,
+ private val citiesRepository: CityDataSource,
+ private val appDesc: ApplicationDescription,
+ private val appContext: Context,
+ coroutineScope: CoroutineScope
+) {
+ companion object {
+ private const val TAG = "FakeLocationStateUseCase"
+ }
+
+ private val _configuredLocationMode = MutableStateFlow<Triple<LocationMode, Float?, Float?>>(Triple(LocationMode.REAL_LOCATION, null, null))
+ val configuredLocationMode: StateFlow<Triple<LocationMode, Float?, Float?>> = _configuredLocationMode
+
+ init {
+ coroutineScope.launch {
+ localStateRepository.fakeLocationEnabled.collect {
+ applySettings(it, localStateRepository.fakeLocation)
+ }
+ }
+ }
+
+ private val locationManager: LocationManager
+ get() = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
+
+ private fun hasAcquireLocationPermission(): Boolean {
+ return (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) ||
+ permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true)
+ }
+
+ private fun applySettings(isEnabled: Boolean, fakeLocation: Pair<Float, Float>, isSpecificLocation: Boolean = false) {
+ _configuredLocationMode.value = computeLocationMode(isEnabled, fakeLocation, isSpecificLocation)
+
+ if (isEnabled && hasAcquireMockLocationPermission()) {
+ fakeLocationModule.startFakeLocation()
+ fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble())
+ localStateRepository.locationMode.value = configuredLocationMode.value.first
+ } else {
+ fakeLocationModule.stopFakeLocation()
+ localStateRepository.locationMode.value = LocationMode.REAL_LOCATION
+ }
+ }
+
+ private fun hasAcquireMockLocationPermission(): Boolean {
+ return (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED) ||
+ permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED)
+ }
+
+ fun setSpecificLocation(latitude: Float, longitude: Float) {
+ setFakeLocation(latitude to longitude, true)
+ }
+
+ fun setRandomLocation() {
+ val randomIndex = Random.nextInt(citiesRepository.citiesLocationsList.size)
+ val location = citiesRepository.citiesLocationsList[randomIndex]
+
+ setFakeLocation(location)
+ }
+
+ private fun setFakeLocation(location: Pair<Float, Float>, isSpecificLocation: Boolean = false) {
+ localStateRepository.fakeLocation = location
+ localStateRepository.setFakeLocationEnabled(true)
+ applySettings(true, location, isSpecificLocation)
+ }
+
+ fun stopFakeLocation() {
+ localStateRepository.setFakeLocationEnabled(false)
+ applySettings(false, localStateRepository.fakeLocation)
+ }
+
+ private fun computeLocationMode(
+ isFakeLocationEnabled: Boolean,
+ fakeLocation: Pair<Float, Float>,
+ isSpecificLocation: Boolean = false,
+ ): Triple<LocationMode, Float?, Float?> {
+ return Triple(
+ when {
+ !isFakeLocationEnabled -> LocationMode.REAL_LOCATION
+ (fakeLocation in citiesRepository.citiesLocationsList && !isSpecificLocation) ->
+ LocationMode.RANDOM_LOCATION
+ else -> LocationMode.SPECIFIC_LOCATION
+ },
+ fakeLocation.first,
+ fakeLocation.second
+ )
+ }
+
+ val currentLocation = MutableStateFlow<Location?>(null)
+
+ private var localListener = object : LocationListener {
+
+ override fun onLocationChanged(location: Location) {
+ currentLocation.update { previous ->
+ if ((previous?.time ?: 0) + 1800 < location.time ||
+ (previous?.accuracy ?: Float.MAX_VALUE) > location.accuracy
+ ) {
+ location
+ } else {
+ previous
+ }
+ }
+ }
+
+ // Deprecated since API 29, never called.
+ override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}
+
+ override fun onProviderEnabled(provider: String) {
+ reset()
+ }
+
+ override fun onProviderDisabled(provider: String) {
+ reset()
+ }
+
+ private fun reset() {
+ stopListeningLocation()
+ currentLocation.value = null
+ startListeningLocation()
+ }
+ }
+
+ fun startListeningLocation(): Boolean {
+ return if (hasAcquireLocationPermission()) {
+ requestLocationUpdates()
+ true
+ } else false
+ }
+
+ fun stopListeningLocation() {
+ locationManager.removeUpdates(localListener)
+ }
+
+ private fun requestLocationUpdates() {
+ val networkProvider = LocationManager.NETWORK_PROVIDER
+ .takeIf { it in locationManager.allProviders }
+ val gpsProvider = LocationManager.GPS_PROVIDER
+ .takeIf { it in locationManager.allProviders }
+
+ try {
+ networkProvider?.let {
+ locationManager.requestLocationUpdates(
+ it,
+ 1000L,
+ 0f,
+ localListener
+ )
+ }
+ gpsProvider?.let {
+ locationManager.requestLocationUpdates(
+ it,
+ 1000L,
+ 0f,
+ localListener
+ )
+ }
+
+ networkProvider?.let { locationManager.getLastKnownLocation(it) }
+ ?: gpsProvider?.let { locationManager.getLastKnownLocation(it) }
+ ?.let {
+ localListener.onLocationChanged(it)
+ }
+ } catch (se: SecurityException) {
+ Log.e(TAG, "Missing permission", se)
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
new file mode 100644
index 0000000..475c05d
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt
@@ -0,0 +1,89 @@
+/*
+ * 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
+ * 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.LocalStateRepository
+import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.LocationMode
+import foundation.e.advancedprivacy.domain.entities.QuickPrivacyState
+import foundation.e.advancedprivacy.domain.entities.TrackerMode
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+class GetQuickPrivacyStateUseCase(
+ private val localStateRepository: LocalStateRepository
+) {
+ val quickPrivacyState: Flow<QuickPrivacyState> = combine(
+ localStateRepository.blockTrackers,
+ localStateRepository.areAllTrackersBlocked,
+ localStateRepository.locationMode,
+ localStateRepository.internetPrivacyMode
+ ) { isBlockTrackers, isAllTrackersBlocked, locationMode, internetPrivacyMode ->
+ when {
+ !isBlockTrackers &&
+ locationMode == LocationMode.REAL_LOCATION &&
+ internetPrivacyMode == InternetPrivacyMode.REAL_IP -> QuickPrivacyState.DISABLED
+
+ isAllTrackersBlocked &&
+ locationMode != LocationMode.REAL_LOCATION &&
+ internetPrivacyMode in listOf(
+ InternetPrivacyMode.HIDE_IP,
+ InternetPrivacyMode.HIDE_IP_LOADING
+ ) -> QuickPrivacyState.FULL_ENABLED
+
+ else -> QuickPrivacyState.ENABLED
+ }
+ }
+
+ val trackerMode: Flow<TrackerMode> = combine(
+ localStateRepository.blockTrackers,
+ localStateRepository.areAllTrackersBlocked
+ ) { isBlockTrackers, isAllTrackersBlocked ->
+ when {
+ isBlockTrackers && isAllTrackersBlocked -> TrackerMode.DENIED
+ isBlockTrackers && !isAllTrackersBlocked -> TrackerMode.CUSTOM
+ else -> TrackerMode.VULNERABLE
+ }
+ }
+
+ val isLocationHidden: Flow<Boolean> = localStateRepository.locationMode.map { locationMode ->
+ locationMode != LocationMode.REAL_LOCATION
+ }
+
+ val locationMode: StateFlow<LocationMode> = localStateRepository.locationMode
+
+ val ipScramblingMode: Flow<InternetPrivacyMode> = localStateRepository.internetPrivacyMode
+
+ fun toggleTrackers() {
+ localStateRepository.setBlockTrackers(!localStateRepository.blockTrackers.value)
+ }
+
+ fun toggleLocation() {
+ localStateRepository.setFakeLocationEnabled(!localStateRepository.fakeLocationEnabled.value)
+ }
+
+ fun toggleIpScrambling() {
+ localStateRepository.setIpScramblingSetting(!localStateRepository.ipScramblingSetting.value)
+ }
+
+ val otherVpnRunning: SharedFlow<ApplicationDescription> = localStateRepository.otherVpnRunning
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
new file mode 100644
index 0000000..8c94602
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 E FOUNDATION, 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.data.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
+import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.HIDE_IP
+import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.HIDE_IP_LOADING
+import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.REAL_IP
+import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode.REAL_IP_LOADING
+import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
+import foundation.e.privacymodules.permissions.IPermissionsPrivacyModule
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class IpScramblingStateUseCase(
+ private val ipScramblerModule: IIpScramblerModule,
+ private val permissionsPrivacyModule: IPermissionsPrivacyModule,
+ private val appDesc: ApplicationDescription,
+ private val localStateRepository: LocalStateRepository,
+ private val appListsRepository: AppListsRepository,
+ private val coroutineScope: CoroutineScope
+) {
+ val internetPrivacyMode: StateFlow<InternetPrivacyMode> = callbackFlow {
+ val listener = object : IIpScramblerModule.Listener {
+ override fun onStatusChanged(newStatus: IIpScramblerModule.Status) {
+ trySend(map(newStatus))
+ }
+
+ override fun log(message: String) {}
+ override fun onTrafficUpdate(
+ upload: Long,
+ download: Long,
+ read: Long,
+ write: Long
+ ) {
+ }
+ }
+ ipScramblerModule.addListener(listener)
+ ipScramblerModule.requestStatus()
+ awaitClose { ipScramblerModule.removeListener(listener) }
+ }.stateIn(
+ scope = coroutineScope,
+ started = SharingStarted.Eagerly,
+ initialValue = REAL_IP
+ )
+
+ init {
+ coroutineScope.launch(Dispatchers.Default) {
+ localStateRepository.ipScramblingSetting.collect {
+ applySettings(it)
+ }
+ }
+
+ coroutineScope.launch {
+ internetPrivacyMode.collect { localStateRepository.internetPrivacyMode.value = it }
+ }
+ }
+
+ fun toggle(hideIp: Boolean) {
+ localStateRepository.setIpScramblingSetting(enabled = hideIp)
+ }
+
+ private fun getHiddenPackageNames(): List<String> {
+ return appListsRepository.getMainProfileHiddenSystemApps().map { it.packageName }
+ }
+
+ val bypassTorApps: Set<String> get() {
+ var whitelist = ipScramblerModule.appList
+ if (getHiddenPackageNames().any { it in whitelist }) {
+ val mutable = whitelist.toMutableSet()
+ mutable.removeAll(getHiddenPackageNames())
+ mutable.add(appListsRepository.dummySystemApp.packageName)
+ whitelist = mutable
+ }
+ 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 = ipScramblerModule.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)
+ } else {
+ rawList.add(packageName)
+ }
+ }
+ ipScramblerModule.appList = rawList
+ }
+
+ private fun applySettings(isIpScramblingEnabled: Boolean) {
+ val currentMode = localStateRepository.internetPrivacyMode.value
+ when {
+ isIpScramblingEnabled && currentMode in setOf(REAL_IP, REAL_IP_LOADING) ->
+ applyStartIpScrambling()
+
+ !isIpScramblingEnabled && currentMode in setOf(HIDE_IP, HIDE_IP_LOADING) ->
+ ipScramblerModule.stop()
+
+ else -> {}
+ }
+ }
+
+ private fun applyStartIpScrambling() {
+ ipScramblerModule.prepareAndroidVpn()?.let {
+ permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName)
+ permissionsPrivacyModule.getAlwaysOnVpnPackage()
+ }?.let {
+ coroutineScope.launch {
+ localStateRepository.emitOtherVpnRunning(
+ permissionsPrivacyModule.getApplicationDescription(packageName = it, withIcon = false)
+ )
+ }
+ localStateRepository.setIpScramblingSetting(enabled = false)
+ } ?: run {
+ ipScramblerModule.start(enableNotification = false)
+ }
+ }
+
+ private fun map(status: IIpScramblerModule.Status): InternetPrivacyMode {
+ return when (status) {
+ IIpScramblerModule.Status.OFF -> REAL_IP
+ IIpScramblerModule.Status.ON -> HIDE_IP
+ IIpScramblerModule.Status.STARTING -> HIDE_IP_LOADING
+ IIpScramblerModule.Status.STOPPING,
+ IIpScramblerModule.Status.START_DISABLED -> REAL_IP_LOADING
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt
new file mode 100644
index 0000000..11bce86
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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
+ * 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.LocalStateRepository
+import foundation.e.advancedprivacy.domain.entities.MainFeatures
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+class ShowFeaturesWarningUseCase(
+ private val localStateRepository: LocalStateRepository
+) {
+
+ fun showWarning(): Flow<MainFeatures> {
+ return merge(
+ localStateRepository.blockTrackers.drop(1).dropWhile { !it }
+ .filter { it && !localStateRepository.hideWarningTrackers }
+ .map { MainFeatures.TRACKERS_CONTROL },
+ localStateRepository.fakeLocationEnabled.drop(1).dropWhile { !it }
+ .filter { it && !localStateRepository.hideWarningLocation }
+ .map { MainFeatures.FAKE_LOCATION },
+ localStateRepository.ipScramblingSetting.drop(1).dropWhile { !it }
+ .filter { it && !localStateRepository.hideWarningIpScrambling }
+ .map { MainFeatures.IP_SCRAMBLING }
+ )
+ }
+
+ fun doNotShowAgain(feature: MainFeatures) {
+ when (feature) {
+ MainFeatures.TRACKERS_CONTROL -> localStateRepository.hideWarningTrackers = true
+ MainFeatures.FAKE_LOCATION -> localStateRepository.hideWarningLocation = true
+ MainFeatures.IP_SCRAMBLING -> localStateRepository.hideWarningIpScrambling = true
+ }
+ }
+}
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
new file mode 100644
index 0000000..882d53f
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStateUseCase.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 E FOUNDATION, 2022 - 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.data.repositories.LocalStateRepository
+import foundation.e.advancedprivacy.data.repositories.TrackersRepository
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule
+import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule
+import foundation.e.privacymodules.trackers.api.Tracker
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+class TrackersStateUseCase(
+ private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule,
+ private val trackersPrivacyModule: ITrackTrackersPrivacyModule,
+ private val localStateRepository: LocalStateRepository,
+ private val trackersRepository: TrackersRepository,
+ private val appListsRepository: AppListsRepository,
+ private val coroutineScope: CoroutineScope
+) {
+ init {
+ trackersPrivacyModule.start(
+ trackers = trackersRepository.trackers,
+ getAppByAPId = appListsRepository::getApp,
+ getAppByUid = appListsRepository::getApp,
+ enableNotification = false
+ )
+ coroutineScope.launch {
+ localStateRepository.blockTrackers.collect { enabled ->
+ if (enabled) {
+ blockTrackersPrivacyModule.enableBlocking()
+ } else {
+ blockTrackersPrivacyModule.disableBlocking()
+ }
+ updateAllTrackersBlockedState()
+ }
+ }
+ }
+
+ private fun updateAllTrackersBlockedState() {
+ localStateRepository.areAllTrackersBlocked.value = blockTrackersPrivacyModule.isBlockingEnabled() &&
+ blockTrackersPrivacyModule.isWhiteListEmpty()
+ }
+
+ fun isWhitelisted(app: ApplicationDescription): Boolean {
+ return isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule)
+ }
+
+ fun toggleAppWhitelist(app: ApplicationDescription, isWhitelisted: Boolean) {
+ appListsRepository.applyForHiddenApps(app) {
+ blockTrackersPrivacyModule.setWhiteListed(it, isWhitelisted)
+ }
+ updateAllTrackersBlockedState()
+ }
+
+ fun blockTracker(app: ApplicationDescription, tracker: Tracker, isBlocked: Boolean) {
+ appListsRepository.applyForHiddenApps(app) {
+ blockTrackersPrivacyModule.setWhiteListed(tracker, it, !isBlocked)
+ }
+ updateAllTrackersBlockedState()
+ }
+
+ fun clearWhitelist(app: ApplicationDescription) {
+ appListsRepository.applyForHiddenApps(
+ app,
+ blockTrackersPrivacyModule::clearWhiteList
+ )
+ updateAllTrackersBlockedState()
+ }
+
+ fun updateTrackers() = coroutineScope.launch {
+ trackersRepository.update()
+ trackersPrivacyModule.start(
+ trackers = trackersRepository.trackers,
+ getAppByAPId = appListsRepository::getApp,
+ getAppByUid = appListsRepository::getApp,
+ enableNotification = false
+ )
+ }
+}
+
+fun isWhitelisted(
+ app: ApplicationDescription,
+ appListsRepository: AppListsRepository,
+ blockTrackersPrivacyModule: IBlockTrackersPrivacyModule
+): Boolean {
+ return appListsRepository.anyForHiddenApps(app, blockTrackersPrivacyModule::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
new file mode 100644
index 0000000..43e4496
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersStatisticsUseCase.kt
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2021 E FOUNDATION, 2022 - 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 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.TrackersPeriodicStatistics
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule
+import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule
+import foundation.e.privacymodules.trackers.api.Tracker
+import kotlinx.coroutines.FlowPreview
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
+import java.time.temporal.ChronoUnit
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
+
+class TrackersStatisticsUseCase(
+ private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule,
+ private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule,
+ private val appListsRepository: AppListsRepository,
+ private val resources: Resources
+) {
+ fun initAppList() {
+ appListsRepository.apps()
+ }
+
+ private fun rawUpdates(): Flow<Unit> = callbackFlow {
+ val listener = object : ITrackTrackersPrivacyModule.Listener {
+ override fun onNewData() {
+ trySend(Unit)
+ }
+ }
+ trackTrackersPrivacyModule.addListener(listener)
+ awaitClose { trackTrackersPrivacyModule.removeListener(listener) }
+ }
+
+ @OptIn(FlowPreview::class)
+ fun listenUpdates(debounce: Duration = 1.seconds) = rawUpdates()
+ .throttleFirst(windowDuration = debounce)
+ .onStart { emit(Unit) }
+
+ fun getDayStatistics(): Pair<TrackersPeriodicStatistics, Int> {
+ return TrackersPeriodicStatistics(
+ callsBlockedNLeaked = trackTrackersPrivacyModule.getPastDayTrackersCalls(),
+ periods = buildDayLabels(),
+ trackersCount = trackTrackersPrivacyModule.getPastDayTrackersCount(),
+ graduations = buildDayGraduations(),
+ ) to trackTrackersPrivacyModule.getTrackersCount()
+ }
+
+ fun getNonBlockedTrackersCount(): Flow<Int> {
+ return if (blockTrackersPrivacyModule.isBlockingEnabled())
+ appListsRepository.allApps().map { apps ->
+ val whiteListedTrackers = mutableSetOf<Tracker>()
+ val whiteListedApps = blockTrackersPrivacyModule.getWhiteListedApp()
+ apps.forEach { app ->
+ if (app in whiteListedApps) {
+ whiteListedTrackers.addAll(trackTrackersPrivacyModule.getTrackersForApp(app))
+ } else {
+ whiteListedTrackers.addAll(blockTrackersPrivacyModule.getWhiteList(app))
+ }
+ }
+ whiteListedTrackers.size
+ }
+ else flowOf(trackTrackersPrivacyModule.getTrackersCount())
+ }
+
+ fun getMostLeakedApp(): ApplicationDescription? {
+ return trackTrackersPrivacyModule.getPastDayMostLeakedApp()
+ }
+
+ fun getDayTrackersCalls() = trackTrackersPrivacyModule.getPastDayTrackersCalls()
+
+ fun getDayTrackersCount() = trackTrackersPrivacyModule.getPastDayTrackersCount()
+
+ private fun buildDayGraduations(): List<String?> {
+ val formatter = DateTimeFormatter.ofPattern(
+ resources.getString(R.string.trackers_graph_hours_period_format)
+ )
+
+ val periods = mutableListOf<String?>()
+ var end = ZonedDateTime.now()
+ for (i in 1..24) {
+ val start = end.truncatedTo(ChronoUnit.HOURS)
+ periods.add(if (start.hour % 6 == 0) formatter.format(start) else null)
+ end = start.minus(1, ChronoUnit.MINUTES)
+ }
+ return periods.reversed()
+ }
+
+ private fun buildDayLabels(): List<String> {
+ val formatter = DateTimeFormatter.ofPattern(
+ resources.getString(R.string.trackers_graph_hours_period_format)
+ )
+ val periods = mutableListOf<String>()
+ var end = ZonedDateTime.now()
+ for (i in 1..24) {
+ val start = end.truncatedTo(ChronoUnit.HOURS)
+ periods.add("${formatter.format(start)} - ${formatter.format(end)}")
+ end = start.minus(1, ChronoUnit.MINUTES)
+ }
+ return periods.reversed()
+ }
+
+ private fun buildMonthLabels(): List<String> {
+ val formater = DateTimeFormatter.ofPattern(
+ resources.getString(R.string.trackers_graph_days_period_format)
+ )
+ val periods = mutableListOf<String>()
+ var day = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS)
+ for (i in 1..30) {
+ periods.add(formater.format(day))
+ day = day.minus(1, ChronoUnit.DAYS)
+ }
+ return periods.reversed()
+ }
+
+ private fun buildYearLabels(): List<String> {
+ val formater = DateTimeFormatter.ofPattern(
+ resources.getString(R.string.trackers_graph_months_period_format)
+ )
+ val periods = mutableListOf<String>()
+ var month = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).withDayOfMonth(1)
+ for (i in 1..12) {
+ periods.add(formater.format(month))
+ month = month.minus(1, ChronoUnit.MONTHS)
+ }
+ return periods.reversed()
+ }
+
+ fun getDayMonthYearStatistics(): Triple<TrackersPeriodicStatistics, TrackersPeriodicStatistics, TrackersPeriodicStatistics> {
+ return with(trackTrackersPrivacyModule) {
+ Triple(
+ TrackersPeriodicStatistics(
+ callsBlockedNLeaked = getPastDayTrackersCalls(),
+ periods = buildDayLabels(),
+ trackersCount = getPastDayTrackersCount()
+ ),
+ TrackersPeriodicStatistics(
+ callsBlockedNLeaked = getPastMonthTrackersCalls(),
+ periods = buildMonthLabels(),
+ trackersCount = getPastMonthTrackersCount()
+ ),
+ TrackersPeriodicStatistics(
+ callsBlockedNLeaked = getPastYearTrackersCalls(),
+ periods = buildYearLabels(),
+ trackersCount = getPastYearTrackersCount()
+ )
+ )
+ }
+ }
+
+ fun getTrackersWithWhiteList(app: ApplicationDescription): List<Pair<Tracker, Boolean>> {
+ return appListsRepository.mapReduceForHiddenApps(
+ app = app,
+ map = { appDesc: ApplicationDescription ->
+ (
+ trackTrackersPrivacyModule.getTrackersForApp(appDesc) to
+ blockTrackersPrivacyModule.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 {
+ return appListsRepository.mapReduceForHiddenApps(
+ app = app,
+ map = { appDesc: ApplicationDescription ->
+ blockTrackersPrivacyModule.getWhiteList(appDesc).isEmpty()
+ },
+ reduce = { areEmpty -> areEmpty.all { it } }
+ )
+ }
+
+ fun getCalls(app: ApplicationDescription): Pair<Int, Int> {
+ return appListsRepository.mapReduceForHiddenApps(
+ app = app,
+ map = trackTrackersPrivacyModule::getPastDayTrackersCallsForApp,
+ reduce = { zip ->
+ zip.unzip().let { (blocked, leaked) ->
+ blocked.sum() to leaked.sum()
+ }
+ }
+ )
+ }
+
+ fun getAppsWithCounts(): Flow<List<AppWithCounts>> {
+ val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp()
+ val hiddenAppsTrackersWithWhiteList =
+ getTrackersWithWhiteList(appListsRepository.dummySystemApp)
+ val acAppsTrackersWithWhiteList =
+ getTrackersWithWhiteList(appListsRepository.dummyCompatibilityApp)
+
+ return appListsRepository.apps()
+ .map { apps ->
+ val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps()
+ 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 = !blockTrackersPrivacyModule.isBlockingEnabled() ||
+ isWhitelisted(app, appListsRepository, blockTrackersPrivacyModule),
+ 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 ->
+ blockTrackersPrivacyModule.getWhiteList(app).size
+ },
+ blockedLeaks = calls.first,
+ leaks = calls.second
+ )
+ }
+ .sortedWith(mostLeakedAppsComparator)
+ }
+ }
+
+ 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
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt
new file mode 100644
index 0000000..94c734c
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/UpdateWidgetUseCase.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 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
+ * 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.LocalStateRepository
+import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule
+
+class UpdateWidgetUseCase(
+ private val localStateRepository: LocalStateRepository,
+ private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule,
+) {
+ init {
+ trackTrackersPrivacyModule.addListener(object : ITrackTrackersPrivacyModule.Listener {
+ override fun onNewData() {
+ }
+ })
+ }
+}