summaryrefslogtreecommitdiff
path: root/app/src/main/java/foundation/e/privacycentralapp
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-01-02 08:35:21 +0000
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-01-02 08:35:21 +0000
commitf1e0961daf535628252f18c06fe22001f84015b5 (patch)
tree78a37bc4da310662f44c4e6c362c0d58de2cc2e6 /app/src/main/java/foundation/e/privacycentralapp
parent7333a0d80ef3fb879fb6d261988deb78b4857393 (diff)
parentf1d5b12dd11019508208571db5e5f57f43e3c4b6 (diff)
Merge branch '5648-apps_pro_profile' into 'main'
5648: display trackers for pro-profile app instances. See merge request e/os/advanced-privacy!100
Diffstat (limited to 'app/src/main/java/foundation/e/privacycentralapp')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt1
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt8
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt107
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt10
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt14
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt8
7 files changed, 91 insertions, 63 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index e6d4c42..6ad84a7 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -182,7 +182,6 @@ class ViewModelsFactory(
TrackersViewModel::class.java ->
TrackersViewModel(
- getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
trackersStatisticsUseCase = trackersStatisticsUseCase
)
FakeLocationViewModel::class.java ->
diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt
index 16b0144..7b09c51 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -28,11 +28,11 @@ import foundation.e.privacycentralapp.domain.entities.AppWithCounts
class AppsAdapter(
private val itemsLayout: Int,
- private val listener: (String) -> Unit
+ private val listener: (Int) -> Unit
) :
RecyclerView.Adapter<AppsAdapter.ViewHolder>() {
- class ViewHolder(view: View, private val listener: (String) -> Unit) : RecyclerView.ViewHolder(view) {
+ class ViewHolder(view: View, private val listener: (Int) -> Unit) : RecyclerView.ViewHolder(view) {
val appName: TextView = view.findViewById(R.id.title)
val counts: TextView = view.findViewById(R.id.counts)
val icon: ImageView = view.findViewById(R.id.icon)
@@ -46,7 +46,7 @@ class AppsAdapter(
)
icon.setImageDrawable(item.icon)
- itemView.setOnClickListener { listener(item.packageName) }
+ itemView.setOnClickListener { listener(item.uid) }
}
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt
index febda91..65ae478 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 E FOUNDATION
+ * Copyright (C) 2022 E FOUNDATION, 2022 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
@@ -21,11 +21,13 @@ import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
+import android.content.pm.PackageInfo
import foundation.e.privacycentralapp.R
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
@@ -48,24 +50,67 @@ class AppListsRepository(
icon = context.getDrawable(R.drawable.ic_e_app_logo)
)
- fun getVisibleApps(): Flow<List<ApplicationDescription>> {
- coroutineScope.launch {
- val (visible, hidden) = splitVisibleToHidden(getAppsUsingInternet())
- appDescriptions.emit(
- Pair(
- visible.map { permissionsModule.buildApplicationDescription(it, withIcon = true) } + dummySystemApp,
- hidden.map { permissionsModule.buildApplicationDescription(it, withIcon = false) },
- )
- )
+ private suspend fun fetchAppDescriptions() {
+ val launcherPackageNames = pm.queryIntentActivities(
+ Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) },
+ 0
+ ).mapNotNull { it.activityInfo?.packageName }
+
+ val visibleAppsFilter = { packageInfo: PackageInfo ->
+ hasInternetPermission(packageInfo) &&
+ isNotHiddenSystemApp(packageInfo.applicationInfo, launcherPackageNames)
+ }
+
+ val hiddenAppsFilter = { packageInfo: PackageInfo ->
+ hasInternetPermission(packageInfo) &&
+ !isNotHiddenSystemApp(packageInfo.applicationInfo, launcherPackageNames)
+ }
+
+ val visibleApps = permissionsModule.getApplications(visibleAppsFilter, true)
+ val hiddenApps = permissionsModule.getApplications(hiddenAppsFilter, false)
+
+ val workProfileVisibleApps = permissionsModule.getWorkProfileApplications(visibleAppsFilter, true)
+ val workProfileHiddenApps = permissionsModule.getWorkProfileApplications(hiddenAppsFilter, false)
+
+ appDescriptions.emit((visibleApps + dummySystemApp) to hiddenApps)
+ allProfilesAppDescriptions.emit(
+ (visibleApps + workProfileVisibleApps + dummySystemApp)
+ to (hiddenApps + workProfileHiddenApps)
+ )
+ }
+
+ private var refreshAppJob: Job? = null
+ private fun refreshAppDescriptions() {
+ if (refreshAppJob != null) {
+ return
+ } else {
+ refreshAppJob = coroutineScope.launch(Dispatchers.IO) {
+ fetchAppDescriptions()
+ refreshAppJob = null
+ }
}
+ }
+
+ fun getVisibleApps(): Flow<List<ApplicationDescription>> {
+ refreshAppDescriptions()
return appDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } }
}
+
fun getHiddenSystemApps(): List<ApplicationDescription> {
return appDescriptions.value.second
}
- fun getVisibleAndHiddenApps(): Flow<List<ApplicationDescription>> = getVisibleApps()
- .map { it + getHiddenSystemApps() }
+ fun getAllProfilesVisibleApps(): Flow<List<ApplicationDescription>> {
+ refreshAppDescriptions()
+ return allProfilesAppDescriptions.map { it.first.sortedBy { app -> app.label.toString().lowercase() } }
+ }
+
+ fun getAllProfilesHiddenSystemApps(): List<ApplicationDescription> {
+ return allProfilesAppDescriptions.value.second
+ }
+
+ fun getAllApps(): Flow<List<ApplicationDescription>> = getAllProfilesVisibleApps()
+ .map { it + getAllProfilesHiddenSystemApps() }
fun getApplicationDescription(packageName: String): ApplicationDescription? {
return appDescriptions.value.first.find { it.packageName == packageName }
@@ -77,7 +122,7 @@ class AppListsRepository(
fun foldForHiddenSystemApp(appUid: Int, appValueGetter: (Int) -> Int): Int {
return if (appUid == dummySystemApp.uid) {
- getHiddenSystemApps().fold(0) { acc, app ->
+ getAllProfilesHiddenSystemApps().fold(0) { acc, app ->
acc + appValueGetter(app.uid)
}
} else appValueGetter(appUid)
@@ -92,14 +137,18 @@ class AppListsRepository(
)
)
- private fun getAppsUsingInternet(): List<ApplicationInfo> {
- return pm.getInstalledPackages(PackageManager.GET_PERMISSIONS).filter {
- it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
- }.map {
- it.applicationInfo
- }
+ private val allProfilesAppDescriptions = MutableStateFlow(
+ Pair(
+ emptyList<ApplicationDescription>(),
+ emptyList<ApplicationDescription>()
+ )
+ )
+
+ private fun hasInternetPermission(packageInfo: PackageInfo): Boolean {
+ return packageInfo.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
}
+ @Suppress("ReturnCount")
private fun isNotHiddenSystemApp(app: ApplicationInfo, launcherApps: List<String>): Boolean {
if (app.packageName == PNAME_SETTINGS) {
return false
@@ -116,22 +165,4 @@ class AppListsRepository(
}
private fun ApplicationInfo.hasFlag(flag: Int) = (flags and flag) == 1
-
- private fun splitVisibleToHidden(apps: List<ApplicationInfo>): Pair<List<ApplicationInfo>, List<ApplicationInfo>> {
- val launcherPackageNames = pm.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) },
- 0
- ).mapNotNull { it.activityInfo?.packageName }
- return apps.fold(
- mutableListOf<ApplicationInfo>() to mutableListOf<ApplicationInfo>()
- ) {
- acc, app ->
- if (isNotHiddenSystemApp(app, launcherPackageNames)) {
- acc.first.add(app)
- } else {
- acc.second.add(app)
- }
- acc
- }
- }
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
index 8b37152..11f0466 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -68,7 +68,7 @@ class TrackersStateUseCase(
fun getTrackersWhitelistIds(appUid: Int): List<String> {
return if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().fold(mutableSetOf<String>()) { acc, app ->
+ appListsRepository.getAllProfilesHiddenSystemApps().fold(mutableSetOf<String>()) { acc, app ->
acc.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid).map { it.id })
acc
}.toList()
@@ -77,7 +77,7 @@ class TrackersStateUseCase(
fun toggleAppWhitelist(appUid: Int, isWhitelisted: Boolean) {
if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().forEach {
+ appListsRepository.getAllProfilesHiddenSystemApps().forEach {
blockTrackersPrivacyModule.setWhiteListed(it.uid, isWhitelisted)
}
} else blockTrackersPrivacyModule.setWhiteListed(appUid, isWhitelisted)
@@ -87,7 +87,7 @@ class TrackersStateUseCase(
fun blockTracker(appUid: Int, tracker: Tracker, isBlocked: Boolean) {
if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().forEach {
+ appListsRepository.getAllProfilesHiddenSystemApps().forEach {
blockTrackersPrivacyModule.setWhiteListed(tracker, it.uid, !isBlocked)
}
} else blockTrackersPrivacyModule.setWhiteListed(tracker, appUid, !isBlocked)
@@ -107,7 +107,7 @@ fun isWhitelisted(
blockTrackersPrivacyModule: IBlockTrackersPrivacyModule
): Boolean {
return if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().any {
+ appListsRepository.getAllProfilesHiddenSystemApps().any {
blockTrackersPrivacyModule.isWhitelisted(it.uid)
}
} else blockTrackersPrivacyModule.isWhitelisted(appUid)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
index 5103eb2..404032b 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -47,7 +47,7 @@ class TrackersStatisticsUseCase(
private val resources: Resources
) {
fun initAppList() {
- appListsRepository.getVisibleApps()
+ appListsRepository.getAllProfilesVisibleApps()
}
private fun rawUpdates(): Flow<Unit> = callbackFlow {
@@ -163,7 +163,7 @@ class TrackersStatisticsUseCase(
fun getTrackers(appUid: Int): List<Tracker> {
val trackers = if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().map {
+ appListsRepository.getAllProfilesHiddenSystemApps().map {
trackTrackersPrivacyModule.getTrackersForApp(it.uid)
}.flatten().distinctBy { it.id }
} else trackTrackersPrivacyModule.getTrackersForApp(appUid)
@@ -175,7 +175,7 @@ class TrackersStatisticsUseCase(
val trackers: List<Tracker>
val whiteListedTrackersIds: Set<String>
if (appUid == appListsRepository.dummySystemApp.uid) {
- val hiddenApps = appListsRepository.getHiddenSystemApps()
+ val hiddenApps = appListsRepository.getAllProfilesHiddenSystemApps()
trackers = trackTrackersPrivacyModule.getTrackers(hiddenApps.map { it.uid })
whiteListedTrackersIds = hiddenApps.fold(HashSet<String>()) { acc, app ->
@@ -193,7 +193,7 @@ class TrackersStatisticsUseCase(
fun getCalls(appUid: Int): Pair<Int, Int> {
return if (appUid == appListsRepository.dummySystemApp.uid) {
- appListsRepository.getHiddenSystemApps().map {
+ appListsRepository.getAllProfilesHiddenSystemApps().map {
trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(it.uid)
}.reduce { (accBlocked, accLeaked), (blocked, leaked) ->
accBlocked + blocked to accLeaked + leaked
@@ -206,7 +206,7 @@ class TrackersStatisticsUseCase(
val hiddenAppsTrackersWithWhiteList =
getTrackersWithWhiteList(appListsRepository.dummySystemApp.uid)
- return appListsRepository.getVisibleApps()
+ return appListsRepository.getAllProfilesVisibleApps()
.map { apps ->
val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps()
apps.map { app ->
@@ -249,7 +249,7 @@ class TrackersStatisticsUseCase(
fun getNonBlockedTrackersCount(): Flow<Int> {
return if (blockTrackersPrivacyModule.isBlockingEnabled())
- appListsRepository.getVisibleAndHiddenApps().map { apps ->
+ appListsRepository.getAllApps().map { apps ->
val whiteListedTrackers = mutableSetOf<Tracker>()
val whiteListedAppUids = blockTrackersPrivacyModule.getWhiteListedApp()
apps.forEach { app ->
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
index 83359e1..cb32c2c 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -78,9 +78,9 @@ class TrackersFragment :
binding.apps.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
- adapter = AppsAdapter(R.layout.trackers_item_app) { packageName ->
+ adapter = AppsAdapter(R.layout.trackers_item_app) { appUid ->
viewModel.submitAction(
- TrackersViewModel.Action.ClickAppAction(packageName)
+ TrackersViewModel.Action.ClickAppAction(appUid)
)
}
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
index 2cdfabc..8b5cc32 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 E FOUNDATION
+ * Copyright (C) 2021 E FOUNDATION, 2022 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
@@ -21,7 +21,6 @@ import android.net.Uri
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.privacycentralapp.domain.entities.AppWithCounts
-import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -35,7 +34,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class TrackersViewModel(
- private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
private val trackersStatisticsUseCase: TrackersStatisticsUseCase
) : ViewModel() {
@@ -79,7 +77,7 @@ class TrackersViewModel(
}
private suspend fun actionClickApp(action: Action.ClickAppAction) {
- state.value.apps?.find { it.packageName == action.packageName }?.let {
+ state.value.apps?.find { it.uid == action.appUid }?.let {
_singleEvents.emit(SingleEvent.OpenAppDetailsEvent(it))
}
}
@@ -91,7 +89,7 @@ class TrackersViewModel(
}
sealed class Action {
- data class ClickAppAction(val packageName: String) : Action()
+ data class ClickAppAction(val appUid: Int) : Action()
object ClickLearnMore : Action()
}
}