aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/foundation/e/advancedprivacy/data
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-09-12 06:17:39 +0000
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2023-09-12 06:17:39 +0000
commit53f4a9ce311d612d43fa770cf7e8f8e98fbb43a0 (patch)
tree59c58e58cfef0e370f39bd9c150e36c6dfcb50c0 /app/src/main/java/foundation/e/advancedprivacy/data
parent1a77e3924bc78eabca7b859ef62be30bbf2476ad (diff)
downloadadvanced-privacy-53f4a9ce311d612d43fa770cf7e8f8e98fbb43a0.tar.gz
2: organise module with clean archi, use Koin for injection.
Diffstat (limited to 'app/src/main/java/foundation/e/advancedprivacy/data')
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt281
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt2
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt133
3 files changed, 1 insertions, 415 deletions
diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt
deleted file mode 100644
index 2d7651d..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/AppListsRepository.kt
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2022 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.data.repositories
-
-import android.Manifest
-import android.content.Context
-import android.content.Intent
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageInfo
-import foundation.e.advancedprivacy.R
-import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
-import foundation.e.privacymodules.permissions.data.ProfileType
-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
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-
-class AppListsRepository(
- private val permissionsModule: PermissionsPrivacyModule,
- private val context: Context,
- private val coroutineScope: CoroutineScope
-) {
- companion object {
- private const val PNAME_SETTINGS = "com.android.settings"
- private const val PNAME_PWAPLAYER = "foundation.e.pwaplayer"
- private const val PNAME_INTENT_VERIFICATION = "com.android.statementservice"
- private const val PNAME_MICROG_SERVICES_CORE = "com.google.android.gms"
-
- val compatibiltyPNames = setOf(
- PNAME_PWAPLAYER, PNAME_INTENT_VERIFICATION, PNAME_MICROG_SERVICES_CORE
- )
- }
-
- val dummySystemApp = ApplicationDescription(
- packageName = "foundation.e.dummysystemapp",
- uid = -1,
- label = context.getString(R.string.dummy_system_app_label),
- icon = context.getDrawable(R.drawable.ic_e_app_logo),
- profileId = -1,
- profileType = ProfileType.MAIN
- )
-
- val dummyCompatibilityApp = ApplicationDescription(
- packageName = "foundation.e.dummyappscompatibilityapp",
- uid = -2,
- label = context.getString(R.string.dummy_apps_compatibility_app_label),
- icon = context.getDrawable(R.drawable.ic_apps_compatibility_components),
- profileId = -1,
- profileType = ProfileType.MAIN
- )
-
- private suspend fun fetchAppDescriptions(fetchMissingIcons: Boolean = false) {
- val launcherPackageNames = context.packageManager.queryIntentActivities(
- Intent(Intent.ACTION_MAIN, null).apply { addCategory(Intent.CATEGORY_LAUNCHER) },
- 0
- ).mapNotNull { it.activityInfo?.packageName }
-
- val visibleAppsFilter = { packageInfo: PackageInfo ->
- hasInternetPermission(packageInfo) &&
- isStandardApp(packageInfo.applicationInfo, launcherPackageNames)
- }
-
- val hiddenAppsFilter = { packageInfo: PackageInfo ->
- hasInternetPermission(packageInfo) &&
- isHiddenSystemApp(packageInfo.applicationInfo, launcherPackageNames)
- }
-
- val compatibilityAppsFilter = { packageInfo: PackageInfo ->
- packageInfo.packageName in compatibiltyPNames
- }
-
- val visibleApps = recycleIcons(
- newApps = permissionsModule.getApplications(visibleAppsFilter),
- fetchMissingIcons = fetchMissingIcons
- )
- val hiddenApps = permissionsModule.getApplications(hiddenAppsFilter)
- val compatibilityApps = permissionsModule.getApplications(compatibilityAppsFilter)
-
- updateMaps(visibleApps + hiddenApps + compatibilityApps)
-
- allProfilesAppDescriptions.emit(
- Triple(
- visibleApps + dummySystemApp + dummyCompatibilityApp,
- hiddenApps,
- compatibilityApps
- )
- )
- }
-
- private fun recycleIcons(
- newApps: List<ApplicationDescription>,
- fetchMissingIcons: Boolean
- ): List<ApplicationDescription> {
- val oldVisibleApps = allProfilesAppDescriptions.value.first
- return newApps.map { app ->
- app.copy(
- icon = oldVisibleApps.find { app.apId == it.apId }?.icon
- ?: if (fetchMissingIcons) permissionsModule.getApplicationIcon(app) else null
- )
- }
- }
-
- private fun updateMaps(apps: List<ApplicationDescription>) {
- val byUid = mutableMapOf<Int, ApplicationDescription>()
- val byApId = mutableMapOf<String, ApplicationDescription>()
- apps.forEach { app ->
- byUid[app.uid]?.run { packageName > app.packageName } == true
- if (byUid[app.uid].let { it == null || it.packageName > app.packageName }) {
- byUid[app.uid] = app
- }
-
- byApId[app.apId] = app
- }
- appsByUid = byUid
- appsByAPId = byApId
- }
-
- private var lastFetchApps = 0
- private var refreshAppJob: Job? = null
- private fun refreshAppDescriptions(fetchMissingIcons: Boolean = true, force: Boolean = false): Job? {
- if (refreshAppJob == null || refreshAppJob?.isCompleted == true) {
- refreshAppJob = coroutineScope.launch(Dispatchers.IO) {
- if (appsByUid.isEmpty() || appsByAPId.isEmpty() ||
- force || context.packageManager.getChangedPackages(lastFetchApps) != null
- ) {
- fetchAppDescriptions(fetchMissingIcons = fetchMissingIcons)
- if (fetchMissingIcons) {
- lastFetchApps = context.packageManager.getChangedPackages(lastFetchApps)
- ?.sequenceNumber ?: lastFetchApps
- }
- }
- }
- }
-
- return refreshAppJob
- }
-
- fun mainProfileApps(): Flow<List<ApplicationDescription>> {
- refreshAppDescriptions()
- return allProfilesAppDescriptions.map {
- it.first.filter { app -> app.profileType == ProfileType.MAIN }
- .sortedBy { app -> app.label.toString().lowercase() }
- }
- }
-
- fun getMainProfileHiddenSystemApps(): List<ApplicationDescription> {
- return allProfilesAppDescriptions.value.second.filter { it.profileType == ProfileType.MAIN }
- }
-
- fun apps(): Flow<List<ApplicationDescription>> {
- refreshAppDescriptions()
- return allProfilesAppDescriptions.map {
- it.first.sortedBy { app -> app.label.toString().lowercase() }
- }
- }
-
- fun allApps(): Flow<List<ApplicationDescription>> {
- return allProfilesAppDescriptions.map {
- it.first + it.second + it.third
- }
- }
-
- private fun getHiddenSystemApps(): List<ApplicationDescription> {
- return allProfilesAppDescriptions.value.second
- }
-
- private fun getCompatibilityApps(): List<ApplicationDescription> {
- return allProfilesAppDescriptions.value.third
- }
-
- fun anyForHiddenApps(app: ApplicationDescription, test: (ApplicationDescription) -> Boolean): Boolean {
- return if (app == dummySystemApp) {
- getHiddenSystemApps().any { test(it) }
- } else if (app == dummyCompatibilityApp) {
- getCompatibilityApps().any { test(it) }
- } else test(app)
- }
-
- fun applyForHiddenApps(app: ApplicationDescription, action: (ApplicationDescription) -> Unit) {
- mapReduceForHiddenApps(app = app, map = action, reduce = {})
- }
-
- fun <T, R> mapReduceForHiddenApps(
- app: ApplicationDescription,
- map: (ApplicationDescription) -> T,
- reduce: (List<T>) -> R
- ): R {
- return if (app == dummySystemApp) {
- reduce(getHiddenSystemApps().map(map))
- } else if (app == dummyCompatibilityApp) {
- reduce(getCompatibilityApps().map(map))
- } else reduce(listOf(map(app)))
- }
-
- private var appsByUid = mapOf<Int, ApplicationDescription>()
- private var appsByAPId = mapOf<String, ApplicationDescription>()
-
- fun getApp(appUid: Int): ApplicationDescription? {
- return appsByUid[appUid] ?: run {
- runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() }
- appsByUid[appUid]
- }
- }
-
- fun getApp(apId: String): ApplicationDescription? {
- if (apId.isBlank()) return null
-
- return appsByAPId[apId] ?: run {
- runBlocking { refreshAppDescriptions(fetchMissingIcons = false, force = true)?.join() }
- appsByAPId[apId]
- }
- }
-
- private val allProfilesAppDescriptions = MutableStateFlow(
- Triple(
- emptyList<ApplicationDescription>(),
- 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
- } else if (app.packageName == PNAME_PWAPLAYER) {
- return true
- } else if (app.hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) {
- return true
- } else if (!app.hasFlag(ApplicationInfo.FLAG_SYSTEM)) {
- return true
- } else if (launcherApps.contains(app.packageName)) {
- return true
- }
- return false
- }
-
- private fun isStandardApp(app: ApplicationInfo, launcherApps: List<String>): Boolean {
- return when {
- app.packageName == PNAME_SETTINGS -> false
- app.packageName in compatibiltyPNames -> false
- app.hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) -> true
- !app.hasFlag(ApplicationInfo.FLAG_SYSTEM) -> true
- launcherApps.contains(app.packageName) -> true
- else -> false
- }
- }
-
- private fun isHiddenSystemApp(app: ApplicationInfo, launcherApps: List<String>): Boolean {
- return when {
- app.packageName in compatibiltyPNames -> false
- else -> !isNotHiddenSystemApp(app, launcherApps)
- }
- }
-
- private fun ApplicationInfo.hasFlag(flag: Int) = (flags and flag) == 1
-}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
index 3f73c78..ba2836f 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt
@@ -18,9 +18,9 @@
package foundation.e.advancedprivacy.data.repositories
import android.content.Context
+import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode
import foundation.e.advancedprivacy.domain.entities.LocationMode
-import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt
deleted file mode 100644
index 568d76b..0000000
--- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/TrackersRepository.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.data.repositories
-
-import android.content.Context
-import com.google.gson.Gson
-import foundation.e.privacymodules.trackers.api.Tracker
-import retrofit2.Retrofit
-import retrofit2.converter.scalars.ScalarsConverterFactory
-import retrofit2.http.GET
-import timber.log.Timber
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileWriter
-import java.io.IOException
-import java.io.InputStreamReader
-import java.io.PrintWriter
-
-class TrackersRepository(private val context: Context) {
-
- private val eTrackerFileName = "e_trackers.json"
- private val eTrackerFile = File(context.filesDir.absolutePath, eTrackerFileName)
-
- var trackers: List<Tracker> = emptyList()
- private set
-
- init {
- initTrackersFile()
- }
-
- suspend fun update() {
- val api = ETrackersApi.build()
- try {
- saveData(eTrackerFile, api.trackers())
- initTrackersFile()
- } catch (e: Exception) {
- Timber.e("While updating trackers", e)
- }
- }
-
- private fun initTrackersFile() {
- try {
- var inputStream = context.assets.open(eTrackerFileName)
- if (eTrackerFile.exists()) {
- inputStream = FileInputStream(eTrackerFile)
- }
- val reader = InputStreamReader(inputStream, "UTF-8")
- val trackerResponse =
- Gson().fromJson(reader, ETrackersApi.ETrackersResponse::class.java)
-
- trackers = mapper(trackerResponse)
-
- reader.close()
- inputStream.close()
- } catch (e: Exception) {
- Timber.e("While parsing trackers in assets", e)
- }
- }
-
- private fun mapper(response: ETrackersApi.ETrackersResponse): List<Tracker> {
- return response.trackers.mapNotNull {
- try {
- it.toTracker()
- } catch (e: Exception) {
- null
- }
- }
- }
-
- private fun ETrackersApi.ETrackersResponse.ETracker.toTracker(): Tracker {
- return Tracker(
- id = id!!,
- hostnames = hostnames!!.toSet(),
- label = name!!,
- exodusId = exodusId
- )
- }
-
- private fun saveData(file: File, data: String): Boolean {
- try {
- val fos = FileWriter(file, false)
- val ps = PrintWriter(fos)
- ps.apply {
- print(data)
- flush()
- close()
- }
- return true
- } catch (e: IOException) {
- e.printStackTrace()
- }
- return false
- }
-}
-
-interface ETrackersApi {
- companion object {
- fun build(): ETrackersApi {
- val retrofit = Retrofit.Builder()
- .baseUrl("https://gitlab.e.foundation/e/os/tracker-list/-/raw/main/")
- .addConverterFactory(ScalarsConverterFactory.create())
- .build()
- return retrofit.create(ETrackersApi::class.java)
- }
- }
-
- @GET("list/e_trackers.json")
- suspend fun trackers(): String
-
- data class ETrackersResponse(val trackers: List<ETracker>) {
- data class ETracker(
- val id: String?,
- val hostnames: List<String>?,
- val name: String?,
- val exodusId: String?
- )
- }
-}