diff options
author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-09-12 06:17:39 +0000 |
---|---|---|
committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-09-12 06:17:39 +0000 |
commit | 53f4a9ce311d612d43fa770cf7e8f8e98fbb43a0 (patch) | |
tree | 59c58e58cfef0e370f39bd9c150e36c6dfcb50c0 /app/src/main/java/foundation/e/advancedprivacy/data | |
parent | 1a77e3924bc78eabca7b859ef62be30bbf2476ad (diff) | |
download | advanced-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')
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? - ) - } -} |