diff options
Diffstat (limited to 'app/src/main/java/foundation')
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 57ab1a4..7323769 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 { @@ -147,7 +147,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) @@ -159,7 +159,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 -> @@ -177,7 +177,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 @@ -190,7 +190,7 @@ class TrackersStatisticsUseCase( val hiddenAppsTrackersWithWhiteList = getTrackersWithWhiteList(appListsRepository.dummySystemApp.uid) - return appListsRepository.getVisibleApps() + return appListsRepository.getAllProfilesVisibleApps() .map { apps -> val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps() apps.map { app -> @@ -233,7 +233,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() } } |