From fd2f214f36b172c84e756e710e635285b8110f96 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Tue, 26 Nov 2024 08:15:35 +0100 Subject: [PATCH] Change the way we are fetching privacy data --- app/build.gradle | 1 + .../apps/data/application/data/Application.kt | 13 +- .../application/utils/GplayApiExtensions.kt | 1 - .../e/apps/data/exodus/ExodusTrackerApi.kt | 26 --- .../foundation/e/apps/data/exodus/Report.kt | 27 ++- .../apps/data/exodus/models/AppPrivacyInfo.kt | 2 +- .../AppPrivacyInfoRepositoryImpl.kt | 194 ++++++------------ .../PrivacyScoreRepositoryImpl.kt | 13 +- .../e/apps/di/network/RetrofitApiModule.kt | 12 -- .../e/apps/ui/PrivacyInfoViewModel.kt | 33 ++- .../ui/application/ApplicationFragment.kt | 78 ++----- .../ui/application/ApplicationViewModel.kt | 19 +- .../ApplicationListRVAdapter.kt | 3 - .../e/apps/ui/search/SearchViewModel.kt | 12 +- .../e/apps/utils/ExodusUriGenerator.kt | 7 +- app/src/main/res/drawable/ic_perm.xml | 10 - app/src/main/res/drawable/ic_tracker.xml | 10 - app/src/main/res/values-de/strings.xml | 6 +- app/src/main/res/values-es/strings.xml | 4 - app/src/main/res/values-fi/strings.xml | 4 - app/src/main/res/values-fr/strings.xml | 4 - app/src/main/res/values-is/strings.xml | 4 - app/src/main/res/values-it/strings.xml | 4 - app/src/main/res/values-ja/strings.xml | 4 +- app/src/main/res/values-nb-rNO/strings.xml | 4 - app/src/main/res/values-nl/strings.xml | 4 - app/src/main/res/values-ru/strings.xml | 4 - app/src/main/res/values-sv/strings.xml | 6 +- app/src/main/res/values-tr/strings.xml | 4 - app/src/main/res/values-uk/strings.xml | 4 - app/src/main/res/values/strings.xml | 5 - .../AppPrivacyInfoRepositoryImplTest.kt | 116 ----------- .../e/apps/exodus/FakeExoudsTrackerApi.kt | 49 ----- .../e/apps/exodus/FakeTrackerDao.kt | 40 ---- .../exodus/PrivacyScoreRepositoryImplTest.kt | 39 ---- 35 files changed, 133 insertions(+), 633 deletions(-) delete mode 100644 app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt delete mode 100644 app/src/main/res/drawable/ic_perm.xml delete mode 100644 app/src/main/res/drawable/ic_tracker.xml delete mode 100644 app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt delete mode 100644 app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt delete mode 100644 app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt diff --git a/app/build.gradle b/app/build.gradle index 94b6dfcca..7d29df584 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,6 +7,7 @@ plugins { id 'androidx.navigation.safeargs.kotlin' id 'com.google.dagger.hilt.android' id 'kotlin-allopen' + id 'kotlin-parcelize' } def versionMajor = 2 diff --git a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt index fb4646ebe..0c8d390f4 100644 --- a/app/src/main/java/foundation/e/apps/data/application/data/Application.kt +++ b/app/src/main/java/foundation/e/apps/data/application/data/Application.kt @@ -38,7 +38,6 @@ data class Application( val category: String = String(), val description: String = String(), var perms: List = emptyList(), - var trackers: List = emptyList(), var reportId: Long = -1L, val icon_image_path: String = String(), val last_modified: String = String(), @@ -65,17 +64,13 @@ data class Application( var type: Type = NATIVE, var privacyScore: Int = -1, var isPurchased: Boolean = false, + var updatedOn: String = String(), /* - * List of permissions from Exodus API. - * This list is now used to calculate the privacy score instead of perms variable above. - * If the value is LIST_OF_NULL - listOf("null"), it means no data is available in Exodus API for this package, - * hence display "N/A" - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5136 + * Number of permissions and trackers from Exodus Api used for privacy score calculation. */ - var permsFromExodus: List = LIST_OF_NULL, - var updatedOn: String = String(), + var numberOfPermission: Int = 0, + var numberOfTracker: Int = 0, /* * Store restriction from App. diff --git a/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt index a9ffd87cf..d43e9fc48 100644 --- a/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt +++ b/app/src/main/java/foundation/e/apps/data/application/utils/GplayApiExtensions.kt @@ -34,7 +34,6 @@ fun App.toApplication(context: Context): Application { author = this.developerName, category = this.categoryName, description = this.description, - perms = this.permissions, icon_image_path = this.iconArtwork.url, last_modified = this.updatedOn, latest_version_code = this.versionCode, diff --git a/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt b/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt deleted file mode 100644 index 0865c3369..000000000 --- a/app/src/main/java/foundation/e/apps/data/exodus/ExodusTrackerApi.kt +++ /dev/null @@ -1,26 +0,0 @@ -package foundation.e.apps.data.exodus - -import retrofit2.Response -import retrofit2.http.GET -import retrofit2.http.Headers -import retrofit2.http.Path -import retrofit2.http.Query - -interface ExodusTrackerApi { - - companion object { - const val BASE_URL = "https://exodus.ecloud.global/api/" - const val CACHE_DURATION_SECONDS = 86400 // 1 day - } - - @Headers("Cache-Control: public, max-age=$CACHE_DURATION_SECONDS") - @GET("trackers") - suspend fun getTrackerList(@Query("date") date: String): Response - - @Headers("Cache-Control: public, max-age=$CACHE_DURATION_SECONDS") - @GET("search/{appHandle}/details") - suspend fun getTrackerInfoOfApp( - @Path("appHandle") appHandle: String, - @Query("v") versionCode: Int, - ): Response> -} diff --git a/app/src/main/java/foundation/e/apps/data/exodus/Report.kt b/app/src/main/java/foundation/e/apps/data/exodus/Report.kt index 29d3eab4c..4de23637d 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/Report.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/Report.kt @@ -1,13 +1,24 @@ package foundation.e.apps.data.exodus -import com.squareup.moshi.Json +import com.google.gson.annotations.SerializedName + +data class ApiResponse( + val results: List +) data class Report( - val report: Long = -1L, - @Json(name = "updated") val updatedAt: String, - @Json(name = "version_name") val version: String, - @Json(name = "version_code") val versionCode: String, - val source: String, - val trackers: List, - val permissions: List = listOf() + val id: Int, + val handle: String, + val name: String, + val creator: String, + val downloads: String, + @SerializedName("app_uid") val appUid: String, + @SerializedName("icon_phash") val iconPhash: String, + @SerializedName("report_updated_at") val reportUpdatedAt: Double, + @SerializedName("permissions_count") val permissionsCount: Int, + @SerializedName("trackers_count") val trackersCount: Int, + @SerializedName("permissions_class") val permissionsClass: String, + @SerializedName("trackers_class") val trackersClass: String, + val version: String ) + diff --git a/app/src/main/java/foundation/e/apps/data/exodus/models/AppPrivacyInfo.kt b/app/src/main/java/foundation/e/apps/data/exodus/models/AppPrivacyInfo.kt index e1a1af00f..eb4c10d6b 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/models/AppPrivacyInfo.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/models/AppPrivacyInfo.kt @@ -1,3 +1,3 @@ package foundation.e.apps.data.exodus.models -data class AppPrivacyInfo(val trackerList: List = listOf(), val permissionList: List = listOf(), val reportId: Long = -1L) +data class AppPrivacyInfo(val numberOfTrackers: Int = 0, val numberOfPermissions: Int = 0, val reportId: Long = -1L) diff --git a/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt index 4c9b427ef..37f4b7004 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/repositories/AppPrivacyInfoRepositoryImpl.kt @@ -18,172 +18,100 @@ package foundation.e.apps.data.exodus.repositories -import foundation.e.apps.data.Result -import foundation.e.apps.data.enums.Origin -import foundation.e.apps.data.exodus.ExodusTrackerApi +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken import foundation.e.apps.data.exodus.Report -import foundation.e.apps.data.exodus.Tracker -import foundation.e.apps.data.exodus.TrackerDao import foundation.e.apps.data.exodus.models.AppPrivacyInfo import foundation.e.apps.data.application.data.Application -import foundation.e.apps.data.getResult -import foundation.e.apps.di.CommonUtilsModule.LIST_OF_NULL -import foundation.e.apps.utils.getFormattedString -import java.util.Date -import java.util.Locale +import foundation.e.apps.data.exodus.ApiResponse +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import java.lang.reflect.Modifier import javax.inject.Inject import javax.inject.Singleton +import foundation.e.apps.data.Result @Singleton class AppPrivacyInfoRepositoryImpl @Inject constructor( - private val exodusTrackerApi: ExodusTrackerApi, - private val trackerDao: TrackerDao + private val okHttpClient: OkHttpClient ) : IAppPrivacyInfoRepository { companion object { - private const val DATE_FORMAT = "ddMMyyyy" - private const val SOURCE_FDROID = "fdroid" - private const val SOURCE_GOOGLE = "google" + private const val EXODUS_PRIVACY_URL = "https://reports.exodus-privacy.eu.org/api" } - private var trackers: List = listOf() - override suspend fun getAppPrivacyInfo( application: Application, appHandle: String ): Result { - if (application.trackers.isNotEmpty() && application.permsFromExodus.isNotEmpty()) { - val appInfo = AppPrivacyInfo(application.trackers, application.permsFromExodus, application.reportId) - return Result.success(appInfo) - } - if (application.is_pwa) { - return Result.error("No need to fetch trackers for a PWA app") - } - - val appTrackerInfoResult = getResult { - exodusTrackerApi.getTrackerInfoOfApp( - appHandle, - application.latest_version_code, - ) - } - - if (appTrackerInfoResult.isSuccess()) { - return parsePrivacyInfo(application, appTrackerInfoResult) - } - return Result.error(extractErrorMessage(appTrackerInfoResult)) - } - - private suspend fun parsePrivacyInfo( - application: Application, - appTrackerInfoResult: Result> - ): Result { - val appPrivacyPrivacyInfoResult = - handleAppPrivacyInfoResultSuccess(application, appTrackerInfoResult) - - updateFusedApp(application, appPrivacyPrivacyInfoResult) - return appPrivacyPrivacyInfoResult - } - - private fun updateFusedApp( - application: Application, - appPrivacyPrivacyInfoResult: Result - ) { - application.trackers = appPrivacyPrivacyInfoResult.data?.trackerList ?: LIST_OF_NULL - application.permsFromExodus = appPrivacyPrivacyInfoResult.data?.permissionList ?: LIST_OF_NULL - application.reportId = appPrivacyPrivacyInfoResult.data?.reportId ?: -1L - if (application.permsFromExodus != LIST_OF_NULL) { - application.perms = application.permsFromExodus + return Result.success(AppPrivacyInfo()) } - } - private suspend fun handleAppPrivacyInfoResultSuccess( - application: Application, - appTrackerResult: Result>, - ): Result { - if (trackers.isEmpty()) { - generateTrackerList() + val reports = fetchReports(application.package_name) + if (reports.isEmpty()) { + return Result.error("Could not fetch reports for ${application.package_name}") } - return createAppPrivacyInfo(application, appTrackerResult) - } - private suspend fun generateTrackerList() { - val trackerListOfLocalDB = trackerDao.getTrackers() - if (trackerListOfLocalDB.isNotEmpty()) { - this.trackers = trackerListOfLocalDB - } else { - generateTrackerListFromExodusApi() - } + updateApplication(application, reports.first()) + return Result.success(buildPrivacyInfo(reports.first())) } - private suspend fun generateTrackerListFromExodusApi() { - val date = Date().getFormattedString(DATE_FORMAT, Locale("en")) - val result = getResult { exodusTrackerApi.getTrackerList(date) } - if (result.isSuccess()) { - result.data?.let { - val trackerList = it.trackers.values.toList() - trackerDao.saveTrackers(trackerList) - this.trackers = trackerList + private fun fetchReports(packageName: String) : List { + val requestBody = mapOf( + "type" to "application", + "query" to packageName, + "limit" to 50 + ) + + val jsonBody = Gson().toJson(requestBody) + val request = Request.Builder() + .url("$EXODUS_PRIVACY_URL/search") + .post(jsonBody.toRequestBody("application/json".toMediaType())) + .build() + + okHttpClient.newCall(request).execute().use { response -> + if (response.isSuccessful) { + val responseBody = response.body?.string() + return parseReports(responseBody ?: "") + } else { + throw IllegalStateException("Failed to fetch reports") } } } - private fun extractErrorMessage(appTrackerResult: Result>): String { - return appTrackerResult.message ?: "Unknown Error" - } - - private fun createAppPrivacyInfo( - application: Application, - appTrackerResult: Result>, - ): Result { - appTrackerResult.data?.let { - return Result.success(getAppPrivacyInfo(application, it)) + private fun parseReports(response: String): List { + try { + val gson = GsonBuilder() + .excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC) + .create() + + val list = gson.fromJson(response) + return list.results + } catch (e: Exception) { + e.printStackTrace() + return emptyList() } - return Result.error(extractErrorMessage(appTrackerResult)) } - private fun getAppPrivacyInfo( - application: Application, - appTrackerData: List, - ): AppPrivacyInfo { - /* - * If the response is empty, that means there is no data on Exodus API about this app, - * i.e. invalid data. - * We signal this by list of "null". - * It is not enough to send just empty lists, as an app can actually have zero trackers - * and zero permissions. This is not to be confused with invalid data. - * - * Issue: https://gitlab.e.foundation/e/backlog/-/issues/5136 - */ - if (appTrackerData.isEmpty()) { - return AppPrivacyInfo(LIST_OF_NULL, LIST_OF_NULL) - } - - val latestTrackerData = getLatestTrackerData(application, appTrackerData) - ?: return AppPrivacyInfo(LIST_OF_NULL, LIST_OF_NULL) - - val appTrackers = extractAppTrackers(latestTrackerData) - val permissions = latestTrackerData.permissions - return AppPrivacyInfo(appTrackers, permissions, latestTrackerData.report) + private inline fun Gson.fromJson(json: String): T { + val type = object : TypeToken() {}.type + return this.fromJson(json, type) } - private fun getLatestTrackerData( - application: Application, - appTrackerData: List - ): Report? { - val source = if (application.origin == Origin.CLEANAPK) SOURCE_FDROID else SOURCE_GOOGLE - val filteredAppTrackerData = appTrackerData.filter { it.source == source } - if (filteredAppTrackerData.isEmpty()) { - return null - } - - val sortedTrackerData = - filteredAppTrackerData.sortedByDescending { trackerData -> trackerData.versionCode.toLong() } - return sortedTrackerData[0] + private fun updateApplication(application: Application, report: Report) { + application.numberOfTracker = report.trackersCount + application.numberOfPermission = report.permissionsCount + application.reportId = report.id.toLong() } - private fun extractAppTrackers(latestTrackerData: Report): List { - return trackers.filter { - latestTrackerData.trackers.contains(it.id) - }.map { it.name } + private fun buildPrivacyInfo(report: Report): AppPrivacyInfo { + return AppPrivacyInfo( + report.trackersCount, + report.permissionsCount, + report.id.toLong() + ) } } diff --git a/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt index 02cb97b8e..2a84fc5f9 100644 --- a/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/exodus/repositories/PrivacyScoreRepositoryImpl.kt @@ -37,15 +37,9 @@ class PrivacyScoreRepositoryImpl @Inject constructor( privacyScore = 0 } - if (application.permsFromExodus == CommonUtilsModule.LIST_OF_NULL) { - return privacyScore - } - if (privacyScore != 0) { - val calculateTrackersScore = calculateTrackersScore(application.trackers.size) - val calculatePermissionsScore = calculatePermissionsScore( - countAndroidPermissions(application) - ) + val calculateTrackersScore = calculateTrackersScore(application.numberOfTracker) + val calculatePermissionsScore = calculatePermissionsScore(application.numberOfPermission) privacyScore = calculateTrackersScore + calculatePermissionsScore } @@ -56,9 +50,6 @@ class PrivacyScoreRepositoryImpl @Inject constructor( return if (numberOfTrackers > THRESHOLD_OF_NON_ZERO_TRACKER_SCORE) MIN_TRACKER_SCORE else MAX_TRACKER_SCORE - numberOfTrackers } - private fun countAndroidPermissions(application: Application) = - application.permsFromExodus.filter { it.contains("android.permission") }.size - private fun calculatePermissionsScore(numberOfPermission: Int): Int { return if (numberOfPermission > THRESHOLD_OF_NON_ZERO_PERMISSION_SCORE) MIN_PERMISSION_SCORE else round( FACTOR_OF_PERMISSION_SCORE * ceil((MAX_PERMISSION_SCORE - numberOfPermission) / DIVIDER_OF_PERMISSION_SCORE) diff --git a/app/src/main/java/foundation/e/apps/di/network/RetrofitApiModule.kt b/app/src/main/java/foundation/e/apps/di/network/RetrofitApiModule.kt index 2d6adc3e8..6fa6aca5e 100644 --- a/app/src/main/java/foundation/e/apps/di/network/RetrofitApiModule.kt +++ b/app/src/main/java/foundation/e/apps/di/network/RetrofitApiModule.kt @@ -26,7 +26,6 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.cleanapk.CleanApkRetrofit import foundation.e.apps.data.ecloud.EcloudApiInterface -import foundation.e.apps.data.exodus.ExodusTrackerApi import foundation.e.apps.data.fdroid.FdroidApiInterface import foundation.e.apps.data.gitlab.ReleaseInfoApi import foundation.e.apps.data.gitlab.UpdatableSystemAppsApi @@ -60,17 +59,6 @@ class RetrofitApiModule { .create(CleanApkRetrofit::class.java) } - @Singleton - @Provides - fun provideExodusApi(okHttpClient: OkHttpClient, moshi: Moshi): ExodusTrackerApi { - return Retrofit.Builder() - .baseUrl(ExodusTrackerApi.BASE_URL) - .client(okHttpClient) - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .build() - .create(ExodusTrackerApi::class.java) - } - /** * The fdroid api returns results in .yaml format. * Hence we need a yaml convertor. diff --git a/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt b/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt index e74a72737..f5985286e 100644 --- a/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/PrivacyInfoViewModel.kt @@ -11,7 +11,9 @@ import foundation.e.apps.data.exodus.models.AppPrivacyInfo import foundation.e.apps.data.exodus.repositories.IAppPrivacyInfoRepository import foundation.e.apps.data.exodus.repositories.PrivacyScoreRepository import foundation.e.apps.data.application.data.Application +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import javax.inject.Inject @HiltViewModel @@ -29,12 +31,10 @@ class PrivacyInfoViewModel @Inject constructor( } } - suspend fun getAppPrivacyInfo(application: Application): Result { - return fetchEmitAppPrivacyInfo(application) - } - fun getSingularAppPrivacyInfoLiveData(application: Application?): LiveData> { - fetchPrivacyInfo(application) + viewModelScope.launch(Dispatchers.IO) { + fetchPrivacyInfo(application) + } return singularAppPrivacyInfoLiveData } @@ -44,13 +44,11 @@ class PrivacyInfoViewModel @Inject constructor( private fun fetchPrivacyInfo(application: Application?, forced: Boolean = false) { application?.let { - if (forced) { - it.trackers = emptyList() - it.permsFromExodus = emptyList() - } - viewModelScope.launch { - singularAppPrivacyInfoLiveData.postValue(fetchEmitAppPrivacyInfo(it)) + val info = withContext(Dispatchers.IO) { + fetchEmitAppPrivacyInfo(it) + } + singularAppPrivacyInfoLiveData.postValue(info) } } } @@ -58,8 +56,10 @@ class PrivacyInfoViewModel @Inject constructor( private suspend fun fetchEmitAppPrivacyInfo( application: Application ): Result { - val appPrivacyPrivacyInfoResult = + val appPrivacyPrivacyInfoResult = withContext(Dispatchers.IO) { privacyInfoRepository.getAppPrivacyInfo(application, application.package_name) + } + return handleAppPrivacyInfoResult(appPrivacyPrivacyInfoResult) } @@ -71,15 +71,6 @@ class PrivacyInfoViewModel @Inject constructor( } else appPrivacyPrivacyInfoResult } - fun getTrackerListText(application: Application?): String { - application?.let { - if (it.trackers.isNotEmpty()) { - return it.trackers.joinToString(separator = "") { tracker -> "$tracker
" } - } - } - return "" - } - fun getPrivacyScore(application: Application?): Int { application?.let { return privacyScoreRepository.calculatePrivacyScore(it) diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index 3c1a57505..8e7a6d4a4 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -22,6 +22,7 @@ import android.annotation.SuppressLint import android.content.Intent import android.graphics.Color import android.graphics.drawable.Drawable +import android.net.Uri import android.os.Bundle import android.text.Html import android.text.format.Formatter @@ -63,7 +64,6 @@ import foundation.e.apps.data.enums.isInitialized import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.databinding.FragmentApplicationBinding -import foundation.e.apps.di.CommonUtilsModule.LIST_OF_NULL import foundation.e.apps.domain.ValidateAppAgeLimitUseCase.Companion.KEY_ANTI_FEATURES_NSFW import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.AppLoungePackageManager @@ -294,42 +294,26 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private fun updatePrivacyPanel() { binding.privacyInclude.apply { appPermissions.setOnClickListener { _ -> - ApplicationDialogFragment( - drawableResId = R.drawable.ic_perm, - title = getString(R.string.permissions), - message = getPermissionListString() - ).show(childFragmentManager, TAG) + openBrowser() } appTrackers.setOnClickListener { - val fusedApp = applicationViewModel.getFusedApp() - var trackers = - buildTrackersString(fusedApp) - - ApplicationDialogFragment( - drawableResId = R.drawable.ic_tracker, - title = getString(R.string.trackers_title), - message = trackers - ).show(childFragmentManager, TAG) + openBrowser() } } } - private fun buildTrackersString(application: Application?): String { - var trackers = - privacyInfoViewModel.getTrackerListText(application) + private fun openBrowser() { + val url = generateExodusUrl() + val intent = Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(url) + } - if (application?.trackers == LIST_OF_NULL) { - trackers = getString(R.string.tracker_information_not_found) - } else if (trackers.isNotEmpty()) { - trackers += "

" + getString( - R.string.privacy_computed_using_text, - generateExodusUrl() - ) - } else { - trackers = getString(R.string.no_tracker_found) + if (intent.resolveActivity(requireContext().packageManager) == null) { + Timber.e("Could not find a browser to open URL: $url") + return } - return trackers + startActivity(intent) } private fun updateAppInformation( @@ -381,7 +365,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { } appPrivacyScoreLayout.setOnClickListener { - if (privacyInfoViewModel.shouldRequestExodusReport(applicationViewModel.getFusedApp())) { + if (privacyInfoViewModel.shouldRequestExodusReport(applicationViewModel.getApplication())) { showRequestExodusReportDialog() return@setOnClickListener } @@ -413,7 +397,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private fun openRequestExodusReportUrl() { val openUrlIntent = Intent(Intent.ACTION_VIEW) - val packageName = applicationViewModel.getFusedApp()?.package_name + val packageName = applicationViewModel.getApplication()?.package_name if (packageName.isNullOrBlank()) { return @@ -647,7 +631,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private fun observeDownloadStatus(view: View) { applicationViewModel.appStatus.observe(viewLifecycleOwner) { status -> - val application = applicationViewModel.getFusedApp() ?: Application() + val application = applicationViewModel.getApplication() ?: Application() mainActivityViewModel.verifyUiFilter(application) { if (!application.filterLevel.isInitialized()) { return@verifyUiFilter @@ -972,31 +956,9 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { binding.downloadInclude.downloadedSize.text = downloadedSize } - private fun getPermissionListString(): String { - var permission = - applicationViewModel.transformPermsToString() - if (permission.isEmpty()) { - permission = getString( - R.string.no_permission_found - ) - } else { - permission += "
" + getString( - R.string.privacy_computed_using_text, - generateExodusUrl() - ) - } - return permission - } - private fun generateExodusUrl(): String { - // if app info not loaded yet, pass the default exodus homePage url - val fusedApp = applicationViewModel.getFusedApp() - if (fusedApp == null || fusedApp.permsFromExodus == LIST_OF_NULL) { - return ExodusUriGenerator.DEFAULT_URL - } - - val reportId = applicationViewModel.applicationLiveData.value!!.first.reportId - return ExodusUriGenerator.buildReportUri(reportId).toString() + val packageName = applicationViewModel.applicationLiveData.value?.first?.package_name ?: "" + return ExodusUriGenerator.buildReportUri(packageName).toString() } private fun fetchAppTracker(application: Application) { @@ -1018,7 +980,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private fun updatePrivacyScore() { val privacyScore = - privacyInfoViewModel.getPrivacyScore(applicationViewModel.getFusedApp()) + privacyInfoViewModel.getPrivacyScore(applicationViewModel.getApplication()) if (privacyScore != -1) { val appPrivacyScore = binding.ratingsInclude.appPrivacyScore appPrivacyScore.text = getString( @@ -1055,7 +1017,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { if (visible) { isRequestReportVisible = - privacyInfoViewModel.shouldRequestExodusReport(applicationViewModel.getFusedApp()) + privacyInfoViewModel.shouldRequestExodusReport(applicationViewModel.getApplication()) privacyScoreVisibility = if (isRequestReportVisible) View.INVISIBLE else View.VISIBLE } @@ -1072,7 +1034,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { private fun reloadPrivacyInfo() { if (shouldReloadPrivacyInfo) { togglePrivacyInfoVisibility(false) - privacyInfoViewModel.refreshAppPrivacyInfo(applicationViewModel.getFusedApp()) + privacyInfoViewModel.refreshAppPrivacyInfo(applicationViewModel.getApplication()) } shouldReloadPrivacyInfo = false diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index 2afc7f482..4e72c47f8 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -219,24 +219,7 @@ class ApplicationViewModel @Inject constructor( } } - fun transformPermsToString(): String { - var permissionString = "" - applicationLiveData.value?.first?.let { - // Filter list to only keep platform permissions - val filteredList = it.perms.filter { - it.startsWith("android.permission.") - } - // Remove prefix as we only have platform permissions remaining - val list = filteredList.map { - it.replace("[^>]*permission\\.".toRegex(), "") - } - // Make it a dialog-friendly string and return it - permissionString = list.joinToString(separator = "") { "$it
" } - } - return permissionString - } - - fun getFusedApp(): Application? { + fun getApplication(): Application? { return applicationLiveData.value?.first } diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt index 4987b29ec..d1a7cdac4 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt @@ -584,9 +584,6 @@ class ApplicationListRVAdapter( currentList.forEach { newList.find { item -> item._id == it._id }?.let { foundItem -> foundItem.privacyScore = it.privacyScore - foundItem.trackers = it.trackers - foundItem.perms = it.perms - foundItem.permsFromExodus = it.permsFromExodus } } this.submitList(newList.map { it.copy() }) diff --git a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt index 9ee353e58..19820b0d9 100644 --- a/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/search/SearchViewModel.kt @@ -90,13 +90,13 @@ class SearchViewModel @Inject constructor( this.flagOpenSource = flagOpenSource this.flagPWA = flagPWA - viewModelScope.launch { + viewModelScope.launch(IO) { emitFilteredResults(null) } } fun getSearchSuggestions(query: String, gPlayAuth: AuthObject.GPlayAuth) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(IO) { searchSuggest.postValue( applicationRepository.getSearchSuggestions(query) ) @@ -140,7 +140,7 @@ class SearchViewModel @Inject constructor( query: String, authData: AuthData? ) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(IO) { val searchResultSupreme = applicationRepository.getCleanApkSearchResults( query, authData ?: AuthData("", "") @@ -161,7 +161,7 @@ class SearchViewModel @Inject constructor( return } - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(IO) { if (autoTriggered) { delay(PREVENT_HTTP_429_DELAY_IN_MS) } @@ -170,7 +170,7 @@ class SearchViewModel @Inject constructor( } private fun fetchGplayData(query: String) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(IO) { isLoading = true val gplaySearchResult = applicationRepository.getGplaySearchResults(query, nextSubBundle) @@ -221,8 +221,6 @@ class SearchViewModel @Inject constructor( private fun hasTrackers(app: Application): Boolean { return when { - app.trackers == LIST_OF_NULL -> true // Tracker data unavailable, don't show - app.trackers.isNotEmpty() -> true // Trackers present app.privacyScore == 0 -> true // Manually blocked apps (Facebook etc.) else -> false } diff --git a/app/src/main/java/foundation/e/apps/utils/ExodusUriGenerator.kt b/app/src/main/java/foundation/e/apps/utils/ExodusUriGenerator.kt index 1bafab68f..bf5848884 100644 --- a/app/src/main/java/foundation/e/apps/utils/ExodusUriGenerator.kt +++ b/app/src/main/java/foundation/e/apps/utils/ExodusUriGenerator.kt @@ -26,7 +26,7 @@ object ExodusUriGenerator { private const val SCHEME = "https" private const val AUTHORITY = "reports.exodus-privacy.eu.org" - fun buildReportUri(reportId: Long): Uri { + fun buildReportUri(packageName: String): Uri { val language = getLanguage(Locale.getDefault().language) return Uri.Builder() @@ -34,8 +34,9 @@ object ExodusUriGenerator { .authority(AUTHORITY) .appendPath(language) .appendPath("reports") - .appendPath(reportId.toString()) - .build() // Example: https://reports.exodus-privacy.eu.org/es/reports/511980/ + .appendPath(packageName) + .appendPath("latest") + .build() } fun buildRequestReportUri(packageName: String): Uri { diff --git a/app/src/main/res/drawable/ic_perm.xml b/app/src/main/res/drawable/ic_perm.xml deleted file mode 100644 index 14dcca15d..000000000 --- a/app/src/main/res/drawable/ic_perm.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_tracker.xml b/app/src/main/res/drawable/ic_tracker.xml deleted file mode 100644 index a3e222a2d..000000000 --- a/app/src/main/res/drawable/ic_tracker.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 33eb42ddd..abc6d03d6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -84,8 +84,6 @@ Aktualisierungen App-Aktualisierungen werden nicht automatisch installiert Auf ein unlimitiertes Netzwerk warten - Keine „runtime android“-Berechtigung gefunden! - Keine Tracker/Verfolger gefunden! Keine Verbindung möglich. Bitte überprüfe die Internetverbindung und versuche es erneut Start App-Suche @@ -104,7 +102,6 @@ App-Aktualisierungen werden automatisch installiert Aktualisierungen von Apps nur in unlimitierten Netzwerken wie WLAN - Berechnet aus <a href=%1$s> Exodus Datenschutz-Analyse Alle Apps sind aktuell Oder nur anzeigen %1$s kaufen @@ -113,7 +110,6 @@ Die Datenschutz-Bewertung wird automatisch berechnet aus den Berechtigungen und den Trackern, die in den Anwendungen aufgespürt werden. Es gibt einen Überblick darüber, wie sehr die App Benutzer ausspioniert. <br /><br />Die Quelle für den Berechnungsalgorithmus kann <a href=%1$s>hier gefunden werden</a>.<br /><br />Die Tracker-Aufspürung wird durchgeführt mit <a href=%2$s>Exodus Privacy Werkzeuge</a>.<br /><br />Bewertung von 1-10.<br /><br />Wenn du mehr erfahren willst, wie die Datenschutzbewertung berechnet wird, und wie du dich vor dem Ausspionieren schützen kannst, <a href=%3$s>besuche dieses Seite</a>. Es ist nicht genügend Speicher vorhanden, um diese App herunterzuladen! Bezahl-Apps können nicht im anonymen Modus installiert werden. Bitte melde dich mit deinem Google-Konto an. - Es ist keine Tracker-Information für diese App verfügbar. Die %s App wird zurzeit nicht unterstützt. Ein Grund könnte sein, dass die App noch nicht sehr verbreitet, oder ein anderer Fehler aufgetreten ist. Diese App wird nicht unterstützt! Einstellungen öffnen @@ -207,4 +203,4 @@ Quelloffene Apps und PWA nicht verfügbar Beim Laden der allgemeinen Apps ist ein Fehler aufgetreten. Nur quelloffene Apps und PWA sind momentan verfügbar. Beim Laden von PWA und quelloffenen Apps ist ein Fehler aufgetreten. Nur allgemeine Apps sind momentan verfügbar. - \ No newline at end of file + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 7ddd324cf..53be90f04 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -86,8 +86,6 @@ Descargas Actualizaciones Las actualizaciones no se instalarán automáticamente - ¡No se encontró ningún permiso de ejecución de Android! - ¡No se encontraron rastreadores! ¡No se pudo conectar! Chequea tu conexión a internet e inténtalo de nuevo Esperando una red sin contador Inicio @@ -106,7 +104,6 @@ Los juegos más taquilleros Las actualizaciones se instalarán automáticamente - Calculado usando <a href=%1$s> Análisis de Privacidad Exodus Todas las aplicaciones están actualizadas Mensual Semanal @@ -140,7 +137,6 @@ \n \nAbre ajustes para buscar solo aplicaciones de código abierto o PWA.
¡Límite de tiempo buscando aplicaciones! - No hay información de rastreo disponible para esta aplicación. Actualización de %1$s aplicaciones completada en %2$s. La actualización de %1$s ha fallado debido a una regla de soporte (ubicación, versión del sistema operativo...). Debido a un fallo temporal, no se pueden actualizar todas tus aplicaciones. Vuelve a intentarlo más tarde. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index ad26a3cfb..09f846ce1 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -93,10 +93,6 @@ Kun klikkaat vahvista, pääset sovelluksen Google Play -sivulle ja voit suorittaa oston loppuun selaimellasi. Klikkaa vahvista ostaaksesi %1$s hintaan %2$s. Osta %1$s Yhteyden muodostaminen ei onnistu! Tarkista internetyhteytesi ja yritä uudelleen - Tästä sovelluksesta ei ole saatavilla seuraintietoja. - Seuraimia ei löytynyt! - Laskettu käyttäen <a href=%1$s> Exodus Privacy Analyysia - Android-käyttöoikeutta ei löytynyt! Odotetaan mittaamatonta verkkoa Sovelluspäivityksiä ei asenneta automaattisesti Sovelluspäivitykset asennetaan automaattisesti diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 044f24249..2a8105dbb 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -80,8 +80,6 @@ Téléchargements Mises à jour Les mises à jour d\'application ne seront pas installées automatiquement - Aucune permission d\'exécution Android trouvée ! - Aucun pisteur trouvé ! Connexion impossible ! Merci de vérifier votre connexion internet puis réessayer Accueil Aucune application trouvée… @@ -102,7 +100,6 @@ Les mises à jour d\'application seront installées automatiquement En attente d\'un réseau illimité - Calculé à partir des <a href=%1$s> analyses d\'Exodus Privacy Icône de catégorie Connectez-vous à App Lounge en utilisant votre compte Google ou sélectionnez le mode Anonyme pour protéger votre vie privée. Apps les mieux classées @@ -138,7 +135,6 @@ \n \nOuvrez les paramètres pour n\'afficher que les applications Open Source et PWA.
Expiration du délai de récupération des applications ! - Aucune information concernant les pisteurs pour cette application. Erreur lors de la mise à jour ! La mise à jour ne peut être appliquée car la signature de la mise à jour de %1$s ne correspond pas à la signature de la version installée sur votre téléphone. Pour y remédier vous pouvez désinstaller %1$s puis la réinstaller depuis App Lounge.

Note : Ce message ne s\'affichera plus.
Il est impossible d\'afficher les applications Google Play quand seules les applications Open Source sont sélectionnées. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 073464ad2..5fa134933 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -151,10 +151,6 @@ Uppfæra forrit sem önnur hugbúnaðarsöfn hafa sett inn. \nReynt verður að uppfæra slík forrit úr flokkum almenns hugbúnaðar og forrita með opnum notkunarleyfum. Uppfærslu %1$s forrita lokið á %2$s. - Engar Android keyrsluheimildir fundust! - Reiknað út frá <a href=%1$s> Exodus Privacy Analysis - Enginn rekjari fannst! - Það eru engar upplýsingar fáanlegar yfir rekjara í þessu forriti. Persónuverndarstig eru sjálfvirkt reiknuð út frá þeim heimildakröfum og rekjurum sem finnast í viðkomandi forriti. Það gefur vísbendingu um hve líklegt sé að forritið sé að fylgjast með notendum sínum.<br /><br />Grunnkóða reikniritsins má <a href=%1$s>finna hér</a>.<br /><br />Greining á rekjurum er framkvæmd með <a href=%2$s>Exodus gagnaleyndarverkfærum</a>.<br /><br />Stig af 10 mögulegum.<br /><br />Sjáðu nánar hvernig persónuverndarstig eru reiknuð, hvaða takmarkanir þau hafa og hvernig þú getur varið þig fyrir slíku snuðri <a href=%3$s>á þessari síðu</a>. Þetta gæti verið vegna þess að netþjónn svari ekki eða annarar villu á netþjóni. \n diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b95a1a5cb..b2dcd0b66 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -88,8 +88,6 @@ Aggiornamenti Gli aggiornamenti App non verranno installati automaticamente Attesa connessione WiFi - Permessi di runtime per android non rilevati! - Nessun Tracker rilevato! Le App a pagamento non sono ancora supportate da App Lounge. Un pò di pazienza e lo saranno. Non riesco a connettermi! Verifica la connessione internet e ritenta Mostra una notifica quando sono disponibili aggiornamenti App @@ -106,7 +104,6 @@ Migliori App di tendenza Gli aggiornamenti App verranno installati automaticamente - Calcolato usando <a href=%1$s> Exodus Privacy Analysis Tutte le app sono aggiornate Forzando l\'installazione, potrai scaricarla e installarla, ma non possiamo garantire che funzioni.

Tentare di installare app non supportate può risultare in arresti anomali o rallentamenti del sistema.

Cercheremo di migliorare la compatibilità con questa app in un futuro prossimo.
Questa app potrebbe non funzionare correttamente! @@ -128,7 +125,6 @@ \n \nAprire le impostazioni per selezionare solo app open source o le PWA.
Timeout nel recupero delle app! - Informazioni sui tracker di questa app non disponibili. Errore nell\'aggiornamento! L\'aggiornamento non può essere installato per la mancata corrispondenza di firma tra l\'aggiornamento di %1$s e la versione istallata sul tuo smartphone. Per ovviare, puoi disinstallare %1$s e quindi installarlo di nuovo da App Lounge.

Nota: questo messaggio non verrà più visualizzato.
Mostrane altre diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index c71ec135a..399a78fe5 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -73,7 +73,6 @@ 更新 上位の無料アプリ 上位の無料ゲーム - 追跡機能が見つかりませんでした! 詳細を表示 インストール このアプリは適切に動作しない可能性があります! @@ -125,7 +124,6 @@ %sの追加のファイル PWAの人気アプリ PWAの人気ゲーム - このアプリの追跡機能に関する情報はありません。 接続できません!インターネットの接続を確認して、再度試してください %1$sのアップデートが%2$sに完了しました。 アプリのアップデートは自動的にインストールされます @@ -152,4 +150,4 @@ App Loungeが適切に機能するよう、電話の空き領域を設定してください。 アプリ %s は現在サポートされていません。アプリがまだ公開されていないか、別のエラーによる可能性があります。 %sが追加のモジュールをインストールしようとしています。インストールを実行するには、App Loungeに再度サインインする必要があります。 - \ No newline at end of file + diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 4ad70dd98..4c72619ed 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -45,7 +45,6 @@ \nÅpne instillinger for å kun se etter åpen kildekode-applikasjoner eller PWAer.
Tidsavbrudd ved henting av applikasjoner! Kan ikke koble til! Kontroller internettilgangen og prøv igjen - Utregnet med <a href=%1$s> Exodus personvernanalyse Oppdatering av %1$s applikasjoner ble fullført %2$s. Venter på ubegrenset nettverk Applikasjonsoppdateringer vil ikke bli installert automatisk @@ -166,11 +165,8 @@ Populære PWA spill Mest innbringende applikasjoner Mest innbringende spill - Ingen sporingsinformasjon tilgjengelig for denne applikasjonen. - Ingen runtime Android-tillatelse funnet! Den anonyme kontoen du bruker er utilgjengelig. Vennligst oppdater økten for å få en ny. OPPDATER ØKTEN - Ingen sporer funnet! Feil ved kjøp! Når du klikker på Bekreft, vil den ta deg til Google Play-siden for applikasjonen for å fullføre kjøpet i nettleseren din. Klikk bekreft for å kjøpe %1$s for %2$s. Applikasjoner som koster penger kan ikke installeres i anonym modus. Logg inn på Google-kontoen din for å installere betalte applikasjoner. diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index b8887c22f..7f1d25e96 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -34,9 +34,6 @@ Meer info Open Instellingen Kan niet verbinden! Kijk alstublieft je internet verbinding na en probeer opnieuw - Geen tracker informatie beschikbaar voor deze app. - Geen trackers gevonden! - Berekend met behulp van <a href=%1$s> Exodus Privacy Analysis Update van %1$s apps voltooid op %2$s. Wachten op niet-gemeten netwerk App updates zullen niet automatisch geïnstalleerd worden @@ -148,7 +145,6 @@ \n \nOpen Instellingen om enkel voor Open source apps of PWA\'s te kijken.
Time-out bij het ophalen van applicaties! - Geen \"runtime android\" machtiging gevonden! Top Populaire Games Top Populaire Apps Open Source Top Geüpdatete Games diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 827fb00d8..36e577c07 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -100,10 +100,6 @@ \nОткройте настройки, чтобы искать только Open Source приложения или PWA.
Тайм-аут загрузки приложений! Не удается подключиться! Пожалуйста, проверьте подключение к Интернету и повторите попытку - Информация о трекере для этого приложения отсутствует. - Трекеры не найдены! - Подсчитано с помощью <a href=%1$s> Exodus Privacy Analysis - Не найдено разрешение на выполнение для android! Обновление приложений %1$s завершено на %2$s. В ожидании безлимитной сети Обновления приложений не будут устанавливаться автоматически diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 477d99a06..1274e9417 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -116,8 +116,6 @@ Appuppdateringar kommer installera automatiskt Appuppdateringar kommer inte att installeras automatiskt Mer info - Ingen spårare hittades! - Ingen information tillgänglig om spårare för denna app. Kan inte ansluta! Kontrollera din internetanslutning och försök igen Öppna inställningar Det anonyma kontot du använder är inte tillgängligt. Uppdatera sessionen för att få ett annat. @@ -154,7 +152,6 @@ Nätverksproblem förhindrar hämtning av alla applikationer. Topplista: Uppdaterade appar med öppen källkod Väntar på obegränsat nätverk - Beräknad med <a href=%1$s> Exodus integritetsanalys Nätverksproblem förhindrar hämtning av alla applikationer. \n \nÖppna inställningar för att endast kolla efter appar med öppen källkod eller PWA. @@ -176,7 +173,6 @@ Begär Exodusrapport Fortsätt till Google-inloggning När du tryckt på bekräfta kommer du tas till appens Google Play-sida för att fullborda köpet med din webbläsare. Tryck bekräfta för att köpa %1$s för %2$s. - Ingen behörighet för androidkörtiden hittades! %1$ss uppdatering misslyckades på grund av en stödregel (plats, OS-verison…). Vi rekommenderar att använda ett dedikerat Googlekonto för att: \n @@ -205,4 +201,4 @@ Vanliga appar är inte tillgängliga Appar med öppen källkod och PWA-appar är inte tillgängliga Ett fil uppstod vid inläsning av vanliga appar. Endast appar med öppen källkod och PWA-appar är tillgängliga just nu. - \ No newline at end of file + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c8afa6a5e..d18b67c49 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -97,8 +97,6 @@ Uygulama güncellemeleri otomatik olarak yüklenmeyecek Tarifesiz ağ bekleniyor %1$s uygulamanın güncellemesi %2$s zamanında bitti. - Çalışma zamanı android izni bulunamadı! - İzleyici Bulunamadı! En İyi Ücretsiz Uygulamalar En İyi Ücretsiz Oyunlar Ana Menü @@ -137,8 +135,6 @@ Bu uygulama uygunsuz içerik içerebilir. Sistem uygulaması Ücretli uygulamalar anonim modda yüklenemez. Ücretli uygulamaları yüklemek için lütfen Google hesabınıza giriş yapın. - <a href=%1$s> Kullanılarak hesaplanmıştır. Exodus Gizlilik Analizi - Bu uygulama için izleyici bilgisi mevcut değil. Bağlantı kurulamıyor! Lütfen internet bağlantınızı kontrol edin ve tekrar deneyin Uygulamaları getirirken zaman aşımı! Bazı ağ sorunları tüm uygulamaların getirilmesini engelliyor. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a02f1f8a6..b981abb77 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -20,7 +20,6 @@ Непередбачувана помилка! Інформація Трекери - Не знайдено інформації стосовно трекерів для цього додатку. Умови використання сервісів Акаунт Скопійовано @@ -146,9 +145,6 @@ \nВідкрийте налаштування, щоб перевірити опції \"Тільки застосунки з відкритим кодом\" та \"Тільки прогресивні вебзастосунки\". Час запиту на отримання застосунків вичерпано! Неможливо приєднатись до мережі! Будь ласка, перевірте налаштування Вашої мережі та спробуйте знову - Трекерів не було знайдено! - Обчислено, використовуючи <a href=%1$s> аналіз приватності Exodus - Не було знайдено дозволів runtime android! Оновлення застосунків %1$s було завершено %2$s. Це оновлення не може бути застосоване, тому що не співпадають сигнатури версії %1$s та версії, що встановлена на Вашому телефоні. Щоб виправити це, Ви можете видалити %1$s, а потім знову встановити з Магазину.

Примітка: Це повідомлення не показуватиметься знову.
Щотижня diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 97b7df512..84a84cd38 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -164,11 +164,6 @@ App updates will not be installed automatically Waiting for un-metered network Update of %1$s apps completed on %2$s. - - No runtime android permission found! - Computed using <a href="%1$s"> Exodus Privacy Analysis - No Tracker Found! - No tracker information available for this app. updateCheckIntervals updateNotify diff --git a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt deleted file mode 100644 index 21ff0bec3..000000000 --- a/app/src/test/java/foundation/e/apps/exodus/AppPrivacyInfoRepositoryImplTest.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * 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 . - */ - -package foundation.e.apps.exodus - -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import foundation.e.apps.data.enums.Status -import foundation.e.apps.data.exodus.repositories.AppPrivacyInfoRepositoryImpl -import foundation.e.apps.data.application.data.Application -import foundation.e.apps.util.MainCoroutineRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Rule -import org.junit.Test - -@ExperimentalCoroutinesApi -class AppPrivacyInfoRepositoryImplTest { - - // Run tasks synchronously - @Rule - @JvmField - val instantExecutorRule = InstantTaskExecutorRule() - - // Sets the main coroutines dispatcher to a TestCoroutineScope for unit testing. - @ExperimentalCoroutinesApi - @get:Rule - var mainCoroutineRule = MainCoroutineRule() - - private lateinit var fakeTrackerDao: FakeTrackerDao - private lateinit var fakeExodusTrackerApi: FakeExoudsTrackerApi - private lateinit var appPrivacyInfoRepository: AppPrivacyInfoRepositoryImpl - - @Before - fun setup() { - fakeExodusTrackerApi = FakeExoudsTrackerApi() - fakeTrackerDao = FakeTrackerDao() - appPrivacyInfoRepository = - AppPrivacyInfoRepositoryImpl(fakeExodusTrackerApi, fakeTrackerDao) - } - - @Test - fun getAppPrivacyInfoWhenSuccess() = runTest { - val application = Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "foundation.e.demothree", - latest_version_code = 123, - is_pwa = false, - ) - val result = appPrivacyInfoRepository.getAppPrivacyInfo(application, application.package_name) - assertEquals("getAppPrivacyInfo", true, result.isSuccess()) - assertEquals("getAppPrivacyInfo", 3, result.data?.trackerList?.size) - } - - @Test - fun getAppPrivacyInfoWhenError() = runTest { - val application = Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "", - latest_version_code = 123, - is_pwa = false, - ) - val result = appPrivacyInfoRepository.getAppPrivacyInfo(application, application.package_name) - assertEquals("getAppPrivacyInfo", false, result.isSuccess()) - } - - @Test - fun getAppPrivacyInfoWhenTrackerDaoIsEmpty() = runTest { - val application = Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "a.b.c", - latest_version_code = 123, - is_pwa = false, - ) - fakeTrackerDao.trackers.clear() - val result = appPrivacyInfoRepository.getAppPrivacyInfo(application, application.package_name) - assertEquals("getAppPrivacyInfo", 2, result.data?.trackerList?.size) - } - - @Test - fun getAppPrivacyInfoWhenIsPwa() = runTest { - val application = Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "a.b.c", - latest_version_code = 123, - is_pwa = true, - ) - val result = appPrivacyInfoRepository.getAppPrivacyInfo(application, application.package_name) - assertEquals(false, result.isSuccess()) - assertEquals("No need to fetch trackers for a PWA app", result.message) - } -} diff --git a/app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt b/app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt deleted file mode 100644 index c5a1b45f7..000000000 --- a/app/src/test/java/foundation/e/apps/exodus/FakeExoudsTrackerApi.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * 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 . - */ - -package foundation.e.apps.exodus - -import foundation.e.apps.data.exodus.ExodusTrackerApi -import foundation.e.apps.data.exodus.Report -import foundation.e.apps.data.exodus.Tracker -import foundation.e.apps.data.exodus.Trackers -import retrofit2.Response - -class FakeExoudsTrackerApi : ExodusTrackerApi { - private val trackers = mutableListOf( - Tracker(1, "Tracker A", "It;s tracer A", "", "", "", ""), - Tracker(2, "Tracker B", "It;s tracer B", "", "", "", ""), - Tracker(3, "Tracker C", "It;s tracer C", "", "", "", "") - ) - - override suspend fun getTrackerList(date: String): Response { - return Response.success(Trackers(mutableMapOf(Pair("one", trackers[0]), Pair("two", trackers[1])))) - } - - override suspend fun getTrackerInfoOfApp( - appHandle: String, - versionCode: Int - ): Response> { - if (appHandle.isEmpty()) { - return Response.error(404, null) - } - val reportOne = Report(System.currentTimeMillis(), "12-12-12", "1.2.3", "123", "google", listOf(1, 2, 3), listOf()) - val reportTwo = Report(System.currentTimeMillis(), "12-12-12", "1.2.3", "123", "fdroid", listOf(1, 2, 3), listOf()) - return Response.success(listOf(reportOne, reportTwo)) - } -} diff --git a/app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt b/app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt deleted file mode 100644 index 7290413ee..000000000 --- a/app/src/test/java/foundation/e/apps/exodus/FakeTrackerDao.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright MURENA SAS 2023 - * Apps Quickly and easily install Android apps onto your device! - * - * 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 . - */ - -package foundation.e.apps.exodus - -import foundation.e.apps.data.exodus.Tracker -import foundation.e.apps.data.exodus.TrackerDao - -class FakeTrackerDao : TrackerDao { - - val trackers = mutableListOf( - Tracker(1, "Tracker A", "It;s tracer A", "", "", "", ""), - Tracker(2, "Tracker B", "It;s tracer B", "", "", "", ""), - Tracker(3, "Tracker C", "It;s tracer C", "", "", "", "") - ) - - override suspend fun saveTrackers(trackerList: List): List { - trackers.addAll(trackerList) - return trackerList.map { it.id } - } - - override suspend fun getTrackers(): List { - return trackers - } -} diff --git a/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt b/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt index 818cf22dc..fcd0aa5cf 100644 --- a/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt +++ b/app/src/test/java/foundation/e/apps/exodus/PrivacyScoreRepositoryImplTest.kt @@ -54,47 +54,11 @@ class PrivacyScoreRepositoryImplTest { package_name = "com.test.fakePackage", latest_version_code = 123, is_pwa = true, - permsFromExodus = listOf(), - perms = listOf(), - trackers = listOf() ) val privacyScore = privacyScoreRepository.calculatePrivacyScore(application) Assert.assertEquals("failed to retrieve valid privacy score", 10, privacyScore) } - @Test - fun calculatePrivacyScoreWhenPermsAreNotAvailable() { - val application = Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "com.test.fakePackage", - latest_version_code = 123, - is_pwa = true, - perms = listOf(), - trackers = listOf() - ) - val privacyScore = privacyScoreRepository.calculatePrivacyScore(application) - Assert.assertEquals("failed to retrieve valid privacy score", -1, privacyScore) - } - - @Test - fun calculatePrivacyScoreWhenTrackersAreNotAvailable() { - val application = Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "com.test.fakePackage", - latest_version_code = 123, - is_pwa = true, - permsFromExodus = listOf(), - perms = listOf(), - trackers = CommonUtilsModule.LIST_OF_NULL - ) - val privacyScore = privacyScoreRepository.calculatePrivacyScore(application) - Assert.assertEquals("failed to retrieve valid privacy score", 9, privacyScore) - } - @Test fun `test calculate privacyScore for the app are available in privacyScoreZero applist`() { val application = Application( @@ -104,9 +68,6 @@ class PrivacyScoreRepositoryImplTest { package_name = "com.test.fakePackage.privacyZero", latest_version_code = 123, is_pwa = true, - permsFromExodus = listOf(), - perms = listOf(), - trackers = CommonUtilsModule.LIST_OF_NULL ) Mockito.`when`(blockedAppRepository.isPrivacyScoreZero(eq("com.test.fakePackage.privacyZero"))).thenReturn(true) -- GitLab