diff --git a/app/build.gradle b/app/build.gradle index 94b6dfccad3d1d84d8db0b78b2790bd105cac4e3..7d29df584f97d1252c13aa68a061f2b8ce010be2 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 fb4646ebeeac51be74a3e9f48021d3ee3dc6c6ae..0c8d390f4dd06e9bbcf134829944ea9d1eef80d4 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 a9ffd87cfaa624a3492ae619e20fab8d3d06c3c8..d43e9fc48c224dfd2f5d3b1bc275ce0b1a31b12f 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 0865c33694c5e8d674983f165c0c542c70f1d924..0000000000000000000000000000000000000000 --- 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 29d3eab4cca2cedca2d79d79642902a6835e3b2e..4de23637dfcb8312cd5e32412acd4644c9224388 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 e1a1af00fa657ecf11c17c6498c6b16a2b5b4c4c..eb4c10d6b695742c9c2fa1a78c40aeaec944cb67 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 4c9b427ef33e4fd731e80dd137ecabc3e83fb029..37f4b7004a5f0c45ae37dfe5ffee1e7ccd273078 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 02cb97b8e51673a1085baafd0330a0b44c38af8a..2a84fc5f9883f261e530e23690d4aa0c5a6cd9db 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 2d6adc3e87631e63db6731cd0999c875d5b38471..6fa6aca5ede08e2fcac86452298637ab4641d96c 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 e74a72737fba32723f341dbf412466c29a2b059b..f5985286e55ead70a328f5255c4196bd7be7a7f2 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 3c1a5750538a1dba1b6a1bb4c2f3ead1bf7c0b0b..8e7a6d4a49bf6eb91809cf41a7b3ddf232393613 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 2afc7f4824da6454b75d7a985713cf1ab2db2a6e..4e72c47f8790749b2b19bc38929fff9ed93f2d98 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 4987b29ec9012dd707c4d21c71927fee5f351533..d1a7cdac4b4b98a5d4d0d32dc7fd18fe27bc66fd 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 9ee353e58272cd304ae1d49b7b08e0c36d91e99e..19820b0d9906082d5b5242f5c0e5d02ec0f03d57 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 1bafab68fe9b28e6736f15eb0e9a2fe00874d545..bf58488842e92188938b123b0a2c07ca7e4836ae 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 14dcca15d3e24df9e9c47a2c326d78976ca225e6..0000000000000000000000000000000000000000 --- 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 a3e222a2d1512aeec9e43b6d84d7813a759b78ff..0000000000000000000000000000000000000000 --- 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 33eb42ddd45367c144739f9a84198f8150888a17..abc6d03d65c71fb144bfc1821c4cf587cc1023e0 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 7ddd324cf290f701e04731b73563bc1476d1e06a..53be90f04660c482a5e14eb6970bc278c1cf6565 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 ad26a3cfbe04e0c71cdc87dcbe1a0761d72a4a17..09f846ce177813d9082bcb3d296521c20af49563 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 044f24249374e472b0c61bba22563d3f413d8438..2a8105dbbc0d0247604f410fed9c161689cb062a 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 073464ad2cacfe2b428944304f8bfb8b688ddee3..5fa13493329773fa196b71d2cde8168771cb6d59 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 b95a1a5cb783302698812e7ca2250797fc17718f..b2dcd0b666769de486533747a0c88d84e1905e2b 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 c71ec135a764d2fabda538ce9b9ffccbeafafdb8..399a78fe54b5fa3da1b84f7fad7875e2e149e911 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 4ad70dd981e159fbad82b96942d569655c251f8b..4c72619ed7d1340e44861671e45f803b9ae3bbcb 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 b8887c22fd8f9b585961de8e78c50b8e6eb0b2e2..7f1d25e963dcc2785e035754e150b629a1992a53 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 827fb00d85242c0836e4373bc057436629e7d7bf..36e577c074d36f6e83129ba6d2f8c72a43d8fc57 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 477d99a06d943e8b43081109a3dc3017ccc66e25..1274e94172e44eba5e22c498dc6525592ab0fead 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 c8afa6a5e4c8e1554e9ae8b2507a66403246570a..d18b67c49e54b0c1e25ce71237b7441892799e53 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 a02f1f8a6a7635f65400ef2ffc74dd455c525a8f..b981abb7748dd03df7f678b7d58c38d5175ebc19 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 97b7df512a95c169e6ddf6adb6f987c0f51b21f8..84a84cd38c6fc603f9dc4ae05c2853350830ce18 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 21ff0bec3e423b91971d2381a305dddf61cf594b..0000000000000000000000000000000000000000 --- 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 c5a1b45f7acb21229d64cf7f5f9e9ebb9e7776cc..0000000000000000000000000000000000000000 --- 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 7290413ee8db3b7fe974cffc354ccd1c76882056..0000000000000000000000000000000000000000 --- 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 818cf22dce83b6878ed535a83c6c5bde4cef22ec..fcd0aa5cf65e52b9799dc69b14b12ff3b976e40e 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)