diff --git a/app/build.gradle b/app/build.gradle index 42bf90d84d3e2ef93077f2943d88e5dbd928e902..6b5700286dd97fd91bca5a7238bec8a20d49a47e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -115,6 +115,10 @@ android { dataBinding true viewBinding true } + + lintOptions { + disable 'MissingTranslation' + } } dependencies { @@ -130,6 +134,8 @@ dependencies { implementation 'foundation.e:privacymodule.tor:1.2.0-orbot-16.6.2' + implementation 'foundation.e:elib:0.0.1-alpha11' + implementation ( Libs.Kotlin.stdlib, Libs.AndroidX.coreKtx, @@ -150,9 +156,6 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' testImplementation 'junit:junit:4.+' - androidTestImplementation 'androidx.test.ext:junit:1.1.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - implementation 'foundation.e:elib:0.0.1-alpha11' } static def log(Object val) { diff --git a/app/src/androidTest/java/foundation/e/privacycentralapp/ExampleInstrumentedTest.kt b/app/src/androidTest/java/foundation/e/privacycentralapp/ExampleInstrumentedTest.kt deleted file mode 100644 index f42f093e637dc17b32c8bbf027cc42311564b940..0000000000000000000000000000000000000000 --- a/app/src/androidTest/java/foundation/e/privacycentralapp/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2021 E FOUNDATION - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package foundation.e.privacycentralapp - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - Assert.assertEquals("foundation.e.privacycentralapp", appContext.packageName) - } -} diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 2e24d4cb33f9c2820e73a21089b32e71278e3c2d..670b81ee8c0c36872c7ff3a13e1b02199816dd2e 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -106,15 +106,17 @@ class DependencyContainer(val app: Application) { ) } - val viewModelsFactory by lazy { ViewModelsFactory( - getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase, - trackersStatisticsUseCase = trackersStatisticsUseCase, - trackersStateUseCase = trackersStateUseCase, - fakeLocationStateUseCase = fakeLocationStateUseCase, - ipScramblerModule = ipScramblerModule, - ipScramblingStateUseCase = ipScramblingStateUseCase, - appListUseCase = appListUseCase - ) } + val viewModelsFactory by lazy { + ViewModelsFactory( + getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase, + trackersStatisticsUseCase = trackersStatisticsUseCase, + trackersStateUseCase = trackersStateUseCase, + fakeLocationStateUseCase = fakeLocationStateUseCase, + ipScramblerModule = ipScramblerModule, + ipScramblingStateUseCase = ipScramblingStateUseCase, + appListUseCase = appListUseCase + ) + } // Background fun initBackgroundSingletons() { @@ -140,15 +142,15 @@ class ViewModelsFactory( private val ipScramblerModule: IIpScramblerModule, private val ipScramblingStateUseCase: IpScramblingStateUseCase, private val appListUseCase: AppListUseCase -): ViewModelProvider.Factory { +) : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun create(modelClass: Class, extras: CreationExtras): T { return when (modelClass) { AppTrackersViewModel::class.java -> { val fallbackUid = android.os.Process.myPid() - val appUid = extras[DEFAULT_ARGS_KEY]?. - getInt(AppTrackersFragment.PARAM_APP_UID, fallbackUid)?: fallbackUid + val appUid = extras[DEFAULT_ARGS_KEY] + ?.getInt(AppTrackersFragment.PARAM_APP_UID, fallbackUid) ?: fallbackUid AppTrackersViewModel( appUid = appUid, diff --git a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt index b23be3d097e823be327fde2641fac5f6ed5db962..28e96e0c92c37fbf850e5793484ed8962a4bb0be 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt @@ -19,14 +19,12 @@ package foundation.e.privacycentralapp import android.app.Application import com.mapbox.mapboxsdk.Mapbox -import kotlinx.coroutines.FlowPreview class PrivacyCentralApplication : Application() { // Initialize the dependency container. val dependencyContainer: DependencyContainer by lazy { DependencyContainer(this) } - override fun onCreate() { super.onCreate() Mapbox.getTelemetry()?.setUserTelemetryRequestState(false) diff --git a/app/src/main/java/foundation/e/privacycentralapp/UpdateTrackersWorker.kt b/app/src/main/java/foundation/e/privacycentralapp/UpdateTrackersWorker.kt index c83d8f921d91c68255ed71c37b4c750df5f9c3f5..13511da5277f497930bc0989e9bdcf143bf9b569 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/UpdateTrackersWorker.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/UpdateTrackersWorker.kt @@ -45,8 +45,9 @@ class UpdateTrackersWorker(appContext: Context, workerParams: WorkerParameters) fun periodicUpdate(context: Context) { val request = PeriodicWorkRequestBuilder( - 7, TimeUnit.DAYS) - .setConstraints(constraints).build() + 7, TimeUnit.DAYS + ) + .setConstraints(constraints).build() WorkManager.getInstance(context).enqueueUniquePeriodicWork( UpdateTrackersWorker::class.qualifiedName ?: "", diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/TextViewHelpers.kt b/app/src/main/java/foundation/e/privacycentralapp/common/TextViewHelpers.kt index 25a36335fc4b925c33ad352991378a9f0da9f58d..d85f4a7c25b73e21e2de8a82684fc37d2f4a59f3 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/TextViewHelpers.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/TextViewHelpers.kt @@ -60,4 +60,4 @@ private fun asteriskAsInfoIconSpannable( } } return spannable -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/extensions/AnyExtension.kt b/app/src/main/java/foundation/e/privacycentralapp/common/extensions/AnyExtension.kt index 5c73df933255d1a79f5eb09f1aa6080da6eefd33..71de99ab34b137e543fecbbc1c0ad9018b44fee0 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/extensions/AnyExtension.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/extensions/AnyExtension.kt @@ -19,10 +19,4 @@ package foundation.e.privacycentralapp.common.extensions import android.content.Context -fun Any.toText(context: Context) = when (this) { - is Int -> context.getString(this) - is String -> this - else -> this.toString() -} - fun Int.dpToPxF(context: Context): Float = this.toFloat() * context.resources.displayMetrics.density diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt index 672f2603301422c5e56259dcf9704f2b7f37274d..d39ee43f20da6687a608cc3824b0ad5b805b0932 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt @@ -20,6 +20,7 @@ package foundation.e.privacycentralapp.data.repositories import android.content.Context import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode import foundation.e.privacycentralapp.domain.entities.LocationMode +import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -51,12 +52,12 @@ class LocalStateRepository(context: Context) { return isFirstActivation } - private val _otherVpnRunning = MutableSharedFlow() - suspend fun emitOtherVpnRunning() { - _otherVpnRunning.emit(true) + private val _otherVpnRunning = MutableSharedFlow() + suspend fun emitOtherVpnRunning(appDesc: ApplicationDescription) { + _otherVpnRunning.emit(appDesc) } - val otherVpnRunning: SharedFlow = _otherVpnRunning + val otherVpnRunning: SharedFlow = _otherVpnRunning var quickPrivacyEnabledFlow: StateFlow = quickPrivacyEnabledMutableFlow diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackerMode.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackerMode.kt index ae70ba37fd6b79ce002bb7263a67adc0064c0fc2..9f057be076b00e9d3886d2b63b0d9cb6d1300e5c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackerMode.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackerMode.kt @@ -19,4 +19,4 @@ package foundation.e.privacycentralapp.domain.entities enum class TrackerMode { DENIED, CUSTOM, VULNERABLE -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt index 142689156b65efb826880a521985f161cb6960d7..e9da8555bc618df559b8c97da74178bb68b635fa 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt @@ -67,8 +67,8 @@ class FakeLocationStateUseCase( get() = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager private fun hasAcquireLocationPermission(): Boolean { - return (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) - || permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) + return (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) || + permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) } private fun applySettings(isQuickPrivacyEnabled: Boolean, fakeLocation: Pair?, isSpecificLocation: Boolean = false) { @@ -85,8 +85,8 @@ class FakeLocationStateUseCase( } private fun hasAcquireMockLocationPermission(): Boolean { - return (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED) - || permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) + return (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED) || + permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) } fun setSpecificLocation(latitude: Float, longitude: Float) { @@ -140,7 +140,8 @@ class FakeLocationStateUseCase( override fun onLocationChanged(location: Location) { currentLocation.update { previous -> if ((previous?.time ?: 0) + 1800 < location.time || - (previous?.accuracy ?: Float.MAX_VALUE) > location.accuracy) { + (previous?.accuracy ?: Float.MAX_VALUE) > location.accuracy + ) { location } else { previous @@ -193,10 +194,10 @@ class FakeLocationStateUseCase( ) - locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER)?: - locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)?.let { - localListener.onLocationChanged(it) - } + locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) + ?: locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)?.let { + localListener.onLocationChanged(it) + } } catch (se: SecurityException) { Log.e(TAG, "Missing permission", se) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/GetQuickPrivacyStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/GetQuickPrivacyStateUseCase.kt index e1f773f683156ee967c25ff6e6a062567ef209cd..46e054e678056430fa274734ee2c111a8773eb11 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/GetQuickPrivacyStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/GetQuickPrivacyStateUseCase.kt @@ -22,6 +22,7 @@ import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode import foundation.e.privacycentralapp.domain.entities.LocationMode import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState import foundation.e.privacycentralapp.domain.entities.TrackerMode +import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow @@ -106,6 +107,5 @@ class GetQuickPrivacyStateUseCase( localStateRepository.setShowQuickPrivacyDisabledMessage(false) } - val otherVpnRunning: SharedFlow = localStateRepository.otherVpnRunning - + val otherVpnRunning: SharedFlow = localStateRepository.otherVpnRunning } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt index 3320721ab7d6e6f2db4ad16bbfc9ee18f338d863..cb9fcd58351ad2b74a34d45f2553a3411db0f8ab 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt @@ -20,6 +20,10 @@ package foundation.e.privacycentralapp.domain.usecases import foundation.e.privacycentralapp.data.repositories.AppListsRepository import foundation.e.privacycentralapp.data.repositories.LocalStateRepository import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode.HIDE_IP +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode.HIDE_IP_LOADING +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode.REAL_IP +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode.REAL_IP_LOADING import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule import foundation.e.privacymodules.permissions.IPermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription @@ -126,30 +130,33 @@ class IpScramblingStateUseCase( } private fun applySettings(isQuickPrivacyEnabled: Boolean, isIpScramblingEnabled: Boolean) { + val settingEnabled = isQuickPrivacyEnabled && isIpScramblingEnabled + val currentMode = localStateRepository.internetPrivacyMode.value + when { - isQuickPrivacyEnabled && isIpScramblingEnabled -> when (localStateRepository.internetPrivacyMode.value) { - InternetPrivacyMode.REAL_IP, InternetPrivacyMode.REAL_IP_LOADING -> { - var intent = ipScramblerModule.prepareAndroidVpn() - if (intent != null) { - permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName) - intent = ipScramblerModule.prepareAndroidVpn() - } - - if (intent != null) { - coroutineScope.launch { - localStateRepository.emitOtherVpnRunning() - } - localStateRepository.setIpScramblingSetting(enabled = false) - } else { - ipScramblerModule.start(enableNotification = false) - } - } - else -> {} - } - else -> when (localStateRepository.internetPrivacyMode.value) { - InternetPrivacyMode.HIDE_IP, InternetPrivacyMode.HIDE_IP_LOADING -> ipScramblerModule.stop() - else -> {} + settingEnabled && currentMode in setOf(REAL_IP, REAL_IP_LOADING) -> + applyStartIpScrambling() + + !settingEnabled && currentMode in setOf(HIDE_IP, HIDE_IP_LOADING) -> + ipScramblerModule.stop() + + else -> {} + } + } + + private fun applyStartIpScrambling() { + ipScramblerModule.prepareAndroidVpn()?.let { + permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName) + permissionsPrivacyModule.getAlwaysOnVpnPackage() + }?.let { + coroutineScope.launch { + localStateRepository.emitOtherVpnRunning( + permissionsPrivacyModule.getApplicationDescription(packageName = it, withIcon = false) + ) } + localStateRepository.setIpScramblingSetting(enabled = false) + } ?: run { + ipScramblerModule.start(enableNotification = false) } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt index 10c1ad0de44857e90ec7ae0c3e2e8bb7dba48eef..17e5096b054cd74c365fb1cdaf1911d4a8227266 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt @@ -20,7 +20,6 @@ package foundation.e.privacycentralapp.domain.usecases import foundation.e.privacycentralapp.data.repositories.AppListsRepository import foundation.e.privacycentralapp.data.repositories.LocalStateRepository import foundation.e.privacycentralapp.data.repositories.TrackersRepository -import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.IBlockTrackersPrivacyModule import foundation.e.privacymodules.trackers.api.ITrackTrackersPrivacyModule @@ -63,8 +62,9 @@ class TrackersStateUseCase( return appListsRepository.getApplicationDescription(appUid) } - fun isWhitelisted(appUid: Int) - = isWhitelisted(appUid, appListsRepository, blockTrackersPrivacyModule) + fun isWhitelisted(appUid: Int): Boolean { + return isWhitelisted(appUid, appListsRepository, blockTrackersPrivacyModule) + } fun getTrackersWhitelistIds(appUid: Int): List { return if (appUid == appListsRepository.dummySystemApp.uid) { @@ -108,7 +108,6 @@ class TrackersStateUseCase( } } - fun isWhitelisted( appUid: Int, appListsRepository: AppListsRepository, diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt index 0a47bc545d137d4cd90526ac71ea8c473ec0fba3..57ab1a4bfff29ee452c18930042e33d94476b44e 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt @@ -166,17 +166,15 @@ class TrackersStatisticsUseCase( acc.addAll(blockTrackersPrivacyModule.getWhiteList(app.uid).map { it.id }) acc } - } else { trackers = trackTrackersPrivacyModule.getTrackersForApp(appUid) whiteListedTrackersIds = blockTrackersPrivacyModule.getWhiteList(appUid) .map { it.id }.toSet() } - return trackers.sortedBy { it.label.lowercase() }.map { tracker -> tracker to whiteListedTrackersIds.any { tracker.id == it }} + return trackers.sortedBy { it.label.lowercase() }.map { tracker -> tracker to whiteListedTrackersIds.any { tracker.id == it } } } - fun getCalls(appUid: Int): Pair { return if (appUid == appListsRepository.dummySystemApp.uid) { appListsRepository.getHiddenSystemApps().map { @@ -211,10 +209,12 @@ class TrackersStatisticsUseCase( blockTrackersPrivacyModule.getWhiteList(app.uid).size }, blockedLeaks = appListsRepository.foldForHiddenSystemApp(app.uid) { - appUid -> callsByApp.getOrDefault(appUid, 0 to 0).first + appUid -> + callsByApp.getOrDefault(appUid, 0 to 0).first }, leaks = appListsRepository.foldForHiddenSystemApp(app.uid) { - appUid -> callsByApp.getOrDefault(appUid, 0 to 0).second + appUid -> + callsByApp.getOrDefault(appUid, 0 to 0).second } ) }.sortedWith(mostLeakedAppsComparator) diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt index 4d38ec8a193646d92b28077e7479a22cb796a0ec..6cd259e527b68c300a9192490ada98ec90c94b03 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt @@ -163,8 +163,11 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) { } } is SingleEvent.ToastMessageSingleEvent -> - Toast.makeText(requireContext(), event.message, Toast.LENGTH_LONG) - .show() + Toast.makeText( + requireContext(), + getString(event.message, *event.args.toTypedArray()), + Toast.LENGTH_LONG + ).show() } } } @@ -200,11 +203,13 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) { binding.togglePrivacyCentral.isChecked = state.quickPrivacyState.isEnabled() - binding.stateTrackers.text = getString(when(state.trackerMode) { - TrackerMode.DENIED -> R.string.dashboard_state_trackers_on - TrackerMode.VULNERABLE -> R.string.dashboard_state_trackers_off - TrackerMode.CUSTOM -> R.string.dashboard_state_trackers_custom - }) + binding.stateTrackers.text = getString( + when (state.trackerMode) { + TrackerMode.DENIED -> R.string.dashboard_state_trackers_on + TrackerMode.VULNERABLE -> R.string.dashboard_state_trackers_off + TrackerMode.CUSTOM -> R.string.dashboard_state_trackers_custom + } + ) binding.stateTrackers.setTextColor( getColor( requireContext(), diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardState.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardState.kt index fb00f07062676a04dad2a0a3a52f70d072fc897d..04b7ae858139ed008d65710e0e76a9ddfe2a63e1 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardState.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardState.kt @@ -33,4 +33,4 @@ data class DashboardState( val dayStatistics: List>? = null, val dayLabels: List? = null, val showQuickPrivacyDisabledMessage: Boolean = false -) \ No newline at end of file +) diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt index cd7e414eb1f39320ea91b77075c18faed3cf53e0..d7d74c6b830bad2f6f57960d472ebdb4971c7a3c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt @@ -17,6 +17,7 @@ package foundation.e.privacycentralapp.features.dashboard +import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.R @@ -76,9 +77,12 @@ class DashboardViewModel( _state.update { s -> s.copy(showQuickPrivacyDisabledMessage = it) } }, getPrivacyStateUseCase.otherVpnRunning.map { - _singleEvents.emit(SingleEvent.ToastMessageSingleEvent( - R.string.ipscrambling_error_always_on_vpn_already_running - )) + _singleEvents.emit( + SingleEvent.ToastMessageSingleEvent( + R.string.ipscrambling_error_always_on_vpn_already_running, + listOf(it.label ?: "") + ) + ) } ).collect {} } @@ -120,9 +124,11 @@ class DashboardViewModel( val isFirstActivation = getPrivacyStateUseCase.toggleReturnIsFirstActivation() fetchStatistics().first() - if (isFirstActivation) _singleEvents.emit(SingleEvent.ToastMessageSingleEvent( - message = R.string.dashboard_first_ipscrambling_activation - )) + if (isFirstActivation) _singleEvents.emit( + SingleEvent.ToastMessageSingleEvent( + message = R.string.dashboard_first_ipscrambling_activation + ) + ) } private suspend fun actionShowMostLeakedApp() = withContext(Dispatchers.IO) { @@ -139,7 +145,10 @@ class DashboardViewModel( object NavigateToLocationSingleEvent : SingleEvent() object NavigateToPermissionsSingleEvent : SingleEvent() data class NavigateToAppDetailsEvent(val appDesc: ApplicationDescription) : SingleEvent() - data class ToastMessageSingleEvent(val message: Int) : SingleEvent() + data class ToastMessageSingleEvent( + @StringRes val message: Int, + val args: List = emptyList() + ) : SingleEvent() } sealed class Action { diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt index 749fae4014a499494a6946de6da95d1e8e02bf61..99aa2170d461a50cb23ee8c469d61d8ac82a490b 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt @@ -33,7 +33,6 @@ import foundation.e.privacycentralapp.PrivacyCentralApplication import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.NavToolbarFragment import foundation.e.privacycentralapp.common.ToggleAppsAdapter -import foundation.e.privacycentralapp.common.extensions.toText import foundation.e.privacycentralapp.common.initQuickPrivacySnackbar import foundation.e.privacycentralapp.common.setToolTipForAsterisk import foundation.e.privacycentralapp.databinding.FragmentInternetActivityPolicyBinding @@ -137,7 +136,7 @@ class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_ac viewModel.singleEvents.collect { event -> when (event) { is InternetPrivacyViewModel.SingleEvent.ErrorEvent -> { - displayToast(event.error.toText(requireContext())) + displayToast(getString(event.errorResId, *event.args.toTypedArray())) } } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyState.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyState.kt index 25e911f952591313364818e69437e4bdda5f3606..6991196834d978943759def38277aa749954db87 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyState.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyState.kt @@ -34,4 +34,4 @@ data class InternetPrivacyState( } val selectedLocationPosition get() = availableLocationIds.indexOf(selectedLocation) -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt index ab5e24d9da359de99a05715a6f49c1eb95f5ff19..be6cd4d290163a51d14dcca9368c44bc7a2e25d6 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt @@ -17,6 +17,7 @@ package foundation.e.privacycentralapp.features.internetprivacy +import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.R @@ -54,20 +55,20 @@ class InternetPrivacyViewModel( private val _singleEvents = MutableSharedFlow() val singleEvents = _singleEvents.asSharedFlow() - - val availablesLocationsIds = listOf("", *ipScramblerModule.getAvailablesLocations().sorted().toTypedArray()) init { viewModelScope.launch(Dispatchers.IO) { - _state.update { it.copy( - mode = ipScramblingStateUseCase.internetPrivacyMode.value, - availableLocationIds = availablesLocationsIds, - selectedLocation = ipScramblerModule.exitCountry) } + _state.update { + it.copy( + mode = ipScramblingStateUseCase.internetPrivacyMode.value, + availableLocationIds = availablesLocationsIds, + selectedLocation = ipScramblerModule.exitCountry + ) + } } } - @OptIn(FlowPreview::class) suspend fun doOnStartedState() = withContext(Dispatchers.IO) { launch { @@ -76,23 +77,26 @@ class InternetPrivacyViewModel( _state.update { s -> s.copy(showQuickPrivacyDisabledMessage = it) } }, appListUseCase.getAppsUsingInternet().map { apps -> - _state.update { s -> s.copy( - availableApps = apps, - bypassTorApps = ipScramblingStateUseCase.bypassTorApps - ) } + _state.update { s -> + s.copy( + availableApps = apps, + bypassTorApps = ipScramblingStateUseCase.bypassTorApps + ) + } }, if (getQuickPrivacyStateUseCase.isQuickPrivacyEnabled) ipScramblingStateUseCase.internetPrivacyMode.map { _state.update { s -> s.copy(mode = it) } } else ipScramblingStateUseCase.configuredMode.map { - _state.update { s -> s.copy( - mode = if (it) InternetPrivacyMode.HIDE_IP - else InternetPrivacyMode.REAL_IP - ) } + _state.update { s -> + s.copy( + mode = if (it) InternetPrivacyMode.HIDE_IP + else InternetPrivacyMode.REAL_IP + ) + } } ).collect {} - } launch { @@ -108,9 +112,13 @@ class InternetPrivacyViewModel( launch { getQuickPrivacyStateUseCase.otherVpnRunning.collect { - _singleEvents.emit(SingleEvent.ErrorEvent(R.string.ipscrambling_error_always_on_vpn_already_running)) - _state.update { it.copy(forceRedraw = !it.forceRedraw)} - + _singleEvents.emit( + SingleEvent.ErrorEvent( + R.string.ipscrambling_error_always_on_vpn_already_running, + listOf(it.label ?: "") + ) + ) + _state.update { it.copy(forceRedraw = !it.forceRedraw) } } } } @@ -148,7 +156,10 @@ class InternetPrivacyViewModel( } sealed class SingleEvent { - data class ErrorEvent(val error: Any) : SingleEvent() + data class ErrorEvent( + @StringRes val errorResId: Int, + val args: List = emptyList() + ) : SingleEvent() } sealed class Action { diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt index 2e014e242aa40e84ac11d2e1dd260a0b0fd02f68..537d0b6e47d9b68b2f450663c71be48e494e3d1c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt @@ -86,8 +86,8 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) private val locationPermissionRequest = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> - if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) - || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) + if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) || + permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) ) { viewModel.submitAction(Action.StartListeningLocation) } // TODO: else. @@ -167,10 +167,12 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) } is FakeLocationViewModel.SingleEvent.RequestLocationPermission -> { // TODO for standalone: rationale dialog - locationPermissionRequest.launch(arrayOf( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - )) + locationPermissionRequest.launch( + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) } is FakeLocationViewModel.SingleEvent.LocationUpdatedEvent -> { // Nothing here, another collect linked to mapbox view. diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationState.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationState.kt index c7bcd988a5d925a05c10d535bb07baf2b3ebbcaf..9513f7774a21ee81a869f7bd8de6b874894ff810 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationState.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationState.kt @@ -27,4 +27,4 @@ data class FakeLocationState( val specificLongitude: Float? = null, val forceRefresh: Boolean = false, val showQuickPrivacyDisabledMessage: Boolean = false -) \ No newline at end of file +) diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt index afba3d0f6429b57eaf8b6abadae9c0ee74eabebe..8db35377a2d0ea656667690dcc1053cac4c63759 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt @@ -58,28 +58,32 @@ class FakeLocationViewModel( launch { merge( fakeLocationStateUseCase.configuredLocationMode.map { (mode, lat, lon) -> - _state.update { s -> s.copy( - mode = mode, - specificLatitude = lat, - specificLongitude = lon - ) } + _state.update { s -> + s.copy( + mode = mode, + specificLatitude = lat, + specificLongitude = lon + ) + } }, getQuickPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map { _state.update { s -> s.copy(showQuickPrivacyDisabledMessage = it) } }, specificLocationInputFlow .debounce(SET_SPECIFIC_LOCATION_DELAY).map { action -> - fakeLocationStateUseCase.setSpecificLocation(action.latitude, action.longitude) - } + fakeLocationStateUseCase.setSpecificLocation(action.latitude, action.longitude) + } ).collect {} } launch { fakeLocationStateUseCase.currentLocation.collect { location -> - _singleEvents.emit(SingleEvent.LocationUpdatedEvent( - mode = _state.value.mode, - location = location - )) + _singleEvents.emit( + SingleEvent.LocationUpdatedEvent( + mode = _state.value.mode, + location = location + ) + ) } } } @@ -110,7 +114,7 @@ class FakeLocationViewModel( sealed class SingleEvent { data class LocationUpdatedEvent(val mode: LocationMode, val location: Location?) : SingleEvent() - object RequestLocationPermission: SingleEvent() + object RequestLocationPermission : SingleEvent() data class ErrorEvent(val error: String) : SingleEvent() } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersState.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersState.kt index f51ff189ff20d099f4dea271310ac7bfc21e12e8..2437366bab1e528ba6c7a07a249b03e2e50e851a 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersState.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersState.kt @@ -26,4 +26,4 @@ data class TrackersState( val yearStatistics: TrackersPeriodicStatistics? = null, val apps: List? = null, val showQuickPrivacyDisabledMessage: Boolean = false -) \ No newline at end of file +) diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt index f49152e97a8f0f0630253696c851239708ca3aa6..3869c3948b84625fb92f2e7198075e25611ecf26 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt @@ -52,11 +52,13 @@ class TrackersViewModel( trackersStatisticsUseCase.listenUpdates().map { trackersStatisticsUseCase.getDayMonthYearStatistics() .let { (day, month, year) -> - _state.update { s -> s.copy( + _state.update { s -> + s.copy( dayStatistics = day, monthStatistics = month, yearStatistics = year - ) } + ) + } } }, trackersStatisticsUseCase.getAppsWithCounts().map { @@ -74,7 +76,7 @@ class TrackersViewModel( } } - suspend private fun actionClickApp(action: Action.ClickAppAction) { + private suspend fun actionClickApp(action: Action.ClickAppAction) { state.value.apps?.find { it.packageName == action.packageName }?.let { _singleEvents.emit(SingleEvent.OpenAppDetailsEvent(it)) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt index ae169b41910407763c0874ba6173f782f1e2f2ac..cd4f6b27243310bcaebd1e9036a9f7b13349166d 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt @@ -36,7 +36,6 @@ import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.NavToolbarFragment import foundation.e.privacycentralapp.common.initQuickPrivacySnackbar import foundation.e.privacycentralapp.databinding.ApptrackersFragmentBinding -import foundation.e.privacycentralapp.common.extensions.toText import kotlinx.coroutines.launch class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { @@ -69,7 +68,8 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (arguments == null || - requireArguments().getInt(PARAM_APP_UID, Int.MIN_VALUE) == Int.MIN_VALUE) { + requireArguments().getInt(PARAM_APP_UID, Int.MIN_VALUE) == Int.MIN_VALUE + ) { activity?.supportFragmentManager?.popBackStack() } } @@ -110,7 +110,7 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { viewModel.singleEvents.collect { event -> when (event) { is AppTrackersViewModel.SingleEvent.ErrorEvent -> - displayToast(event.error.toText(requireContext())) + displayToast(getString(event.errorResId)) is AppTrackersViewModel.SingleEvent.OpenUrl -> try { startActivity(Intent(Intent.ACTION_VIEW, event.url)) @@ -151,7 +151,6 @@ class AppTrackersFragment : NavToolbarFragment(R.layout.apptrackers_fragment) { binding.blockAllToggle.isChecked = state.isBlockingActivated - val trackersStatus = state.getTrackersStatus() if (!trackersStatus.isNullOrEmpty()) { binding.trackersListTitle.isVisible = state.isBlockingActivated diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt index ffa1f362fb93ee1e04e96baaeb6dd8f4086d7ecc..d6d0858a46463ecd505f3ea27cab7d6cb5bd3096 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersState.kt @@ -29,11 +29,12 @@ data class AppTrackersState( val isQuickPrivacyEnabled: Boolean = false, val showQuickPrivacyDisabledMessage: Boolean = false, ) { - fun getTrackersStatus(): List>? - = trackersWithWhiteList?.map { it.first to !it.second } + fun getTrackersStatus(): List>? { + return trackersWithWhiteList?.map { it.first to !it.second } + } fun getTrackersCount() = trackersWithWhiteList?.size ?: 0 fun getBlockedTrackersCount(): Int = if (isQuickPrivacyEnabled && isBlockingActivated) - trackersWithWhiteList?.count { !it.second }?: 0 + trackersWithWhiteList?.count { !it.second } ?: 0 else 0 -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt index faa4e6ba5afca8e67c557cc69f7c57c0bfcf2328..52ef2c43f9d7b0e8e9ca1b93339897d5963a1431 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt @@ -18,6 +18,7 @@ package foundation.e.privacycentralapp.features.trackers.apptrackers import android.net.Uri +import androidx.annotation.StringRes import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase @@ -53,11 +54,13 @@ class AppTrackersViewModel( init { viewModelScope.launch(Dispatchers.IO) { - _state.update { it.copy( - appDesc = trackersStateUseCase.getApplicationDescription(appUid), - isBlockingActivated = !trackersStateUseCase.isWhitelisted(appUid), - trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid), - ) } + _state.update { + it.copy( + appDesc = trackersStateUseCase.getApplicationDescription(appUid), + isBlockingActivated = !trackersStateUseCase.isWhitelisted(appUid), + trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid), + ) + } } } @@ -74,56 +77,69 @@ class AppTrackersViewModel( } fun submitAction(action: Action) = viewModelScope.launch { - when (action) { - is Action.BlockAllToggleAction -> blockAllToggleAction(action) - is Action.ToggleTrackerAction -> toggleTrackerAction(action) - is Action.ClickTracker ->actionClickTracker(action) - is Action.CloseQuickPrivacyDisabledMessage -> - getQuickPrivacyStateUseCase.resetQuickPrivacyDisabledMessage() - } + when (action) { + is Action.BlockAllToggleAction -> blockAllToggleAction(action) + is Action.ToggleTrackerAction -> toggleTrackerAction(action) + is Action.ClickTracker -> actionClickTracker(action) + is Action.CloseQuickPrivacyDisabledMessage -> + getQuickPrivacyStateUseCase.resetQuickPrivacyDisabledMessage() + } } - private suspend fun blockAllToggleAction(action: Action.BlockAllToggleAction) - = withContext(Dispatchers.IO) { - trackersStateUseCase.toggleAppWhitelist(appUid, !action.isBlocked) - _state.update { it.copy( - isBlockingActivated = !trackersStateUseCase.isWhitelisted(appUid) - ) } + private suspend fun blockAllToggleAction(action: Action.BlockAllToggleAction) { + withContext(Dispatchers.IO) { + trackersStateUseCase.toggleAppWhitelist(appUid, !action.isBlocked) + _state.update { + it.copy( + isBlockingActivated = !trackersStateUseCase.isWhitelisted(appUid) + ) + } + } } - private suspend fun toggleTrackerAction(action: Action.ToggleTrackerAction) - = withContext(Dispatchers.IO) { - if (state.value.isBlockingActivated) { - trackersStateUseCase.blockTracker(appUid, action.tracker, action.isBlocked) - _state.update { it.copy( - trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid) - ) } + private suspend fun toggleTrackerAction(action: Action.ToggleTrackerAction) { + withContext(Dispatchers.IO) { + if (state.value.isBlockingActivated) { + trackersStateUseCase.blockTracker(appUid, action.tracker, action.isBlocked) + _state.update { + it.copy( + trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList( + appUid + ) + ) + } + } } } - private suspend fun actionClickTracker(action: Action.ClickTracker) - = withContext(Dispatchers.IO) { - action.tracker.exodusId?.let { - try { - _singleEvents.emit(SingleEvent.OpenUrl( - Uri.parse(exodusBaseUrl + it) - )) - } catch (e: Exception) {} + private suspend fun actionClickTracker(action: Action.ClickTracker) { + withContext(Dispatchers.IO) { + action.tracker.exodusId?.let { + try { + _singleEvents.emit( + SingleEvent.OpenUrl( + Uri.parse(exodusBaseUrl + it) + ) + ) + } catch (e: Exception) { + } + } } } private fun fetchStatistics() { val (blocked, leaked) = trackersStatisticsUseCase.getCalls(appUid) - return _state.update { s -> s.copy( - trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid), - leaked = leaked, - blocked = blocked, - ) } + return _state.update { s -> + s.copy( + trackersWithWhiteList = trackersStatisticsUseCase.getTrackersWithWhiteList(appUid), + leaked = leaked, + blocked = blocked, + ) + } } - sealed class SingleEvent { - data class ErrorEvent(val error: Any) : SingleEvent() + data class ErrorEvent(@StringRes val errorResId: Int) : SingleEvent() data class OpenUrl(val url: Uri) : SingleEvent() } diff --git a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt index 63ec27f2cc894a099a84aabeff264748345d179a..58ac797685f220a2d2f52848c7bc385b22184098 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt @@ -28,7 +28,6 @@ import foundation.e.privacycentralapp.PrivacyCentralApplication import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.features.dashboard.DashboardFragment import foundation.e.privacycentralapp.features.trackers.TrackersFragment -import kotlinx.coroutines.FlowPreview open class MainActivity : FragmentActivity(R.layout.activity_main) { override fun onPostCreate(savedInstanceState: Bundle?) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt index e886cd88cf7400b2c807229a1a7299da223ca198..ddfcc2e0e87d5e73aa011b660da02f89dadf0975 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt @@ -141,7 +141,6 @@ class Widget : AppWidgetProvider() { } } - override fun onAppWidgetOptionsChanged( context: Context, appWidgetManager: AppWidgetManager, diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt index 27589a8e766977e3a3069036a938cf3b7ffe2f11..682e5cc44710200053320c685f7125fac1b6372b 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt @@ -29,8 +29,8 @@ import android.widget.RemoteViews import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.Widget import foundation.e.privacycentralapp.Widget.Companion.isDarkText -import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState import foundation.e.privacycentralapp.common.extensions.dpToPxF +import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState import foundation.e.privacycentralapp.domain.entities.TrackerMode import foundation.e.privacycentralapp.main.MainActivity import foundation.e.privacycentralapp.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_PRIVACY @@ -44,7 +44,6 @@ data class State( val activeTrackersCount: Int = 0, ) - fun render( context: Context, state: State, @@ -93,7 +92,7 @@ fun render( setTextViewText( R.id.state_trackers, context.getString( - when(state.trackerMode) { + when (state.trackerMode) { TrackerMode.DENIED -> R.string.widget_state_trackers_on TrackerMode.VULNERABLE -> R.string.widget_state_trackers_off TrackerMode.CUSTOM -> R.string.widget_state_trackers_custom @@ -277,7 +276,7 @@ fun applyDarkText(context: Context, state: State, views: RemoteViews) { R.id.graph_legend_blocked, R.id.graph_legend_allowed, - ) + ) .forEach { setTextColor( it, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d3157d55e10eca461359519ecdd2c17ec2eedcc5..8705ac5065a560328a7a8fa8c64c2b10f2fa2a49 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -54,7 +54,7 @@ Apply this setting to all selected applications * : Only apps with Internet permission are listed. Enabled Quick Privacy to use functionalities - Please disable any 3rd-party VPN in order for Advanced Privacy to hide your real IP address. + Please disable the 3rd-party VPN %s in order for Advanced Privacy to hide your real IP address. Our scrambling IP service is taking time to launch. It can take a few minutes. Leaving the screen won\'t interrupt the process. Manage my location diff --git a/fakelocation/fakelocationdemo/build.gradle b/fakelocation/fakelocationdemo/build.gradle index c182b2f8b977e84dc1f08cf54164a188ccfbe2c8..7e05b8a48fbafaba5cb5551f1a2f8c2f6873ed70 100644 --- a/fakelocation/fakelocationdemo/build.gradle +++ b/fakelocation/fakelocationdemo/build.gradle @@ -68,4 +68,4 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file +} diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt index 1b0a35bef7ab10e3d2c178c56e4e20f4934526a7..42cf658aba11cdc3e6025483691e6318628cae3c 100644 --- a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt +++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt @@ -58,7 +58,6 @@ class MainActivity : AppCompatActivity() { ) } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) @@ -79,7 +78,7 @@ class MainActivity : AppCompatActivity() { binding.mockedLocation = mockedLocation } - private val listener = object: LocationListener { + private val listener = object : LocationListener { override fun onLocationChanged(location: Location) { binding.currentLocation = "lat: ${location.latitude} - lon: ${location.longitude}" } @@ -130,16 +129,19 @@ class MainActivity : AppCompatActivity() { private val locationPermissionRequest = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> - if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) - || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) + if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) || + permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) ) { startLocationUpdates() } } private fun requireLocationPermissions() { - if (ContextCompat.checkSelfPermission(this, - Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { startLocationUpdates() } else { // Before you perform the actual permission request, check whether your app @@ -152,19 +154,20 @@ class MainActivity : AppCompatActivity() { ) ) } - } @Suppress("UNUSED_PARAMETER") fun onClickPermission(view: View?) { - val isGranted = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, - AppOpModes.ALLOWED) + val isGranted = permissionsModule.setAppOpMode( + appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, + AppOpModes.ALLOWED + ) if (isGranted) { updateData("") return } - //dev mode disabled + // dev mode disabled val alertDialog = AlertDialog.Builder(this) alertDialog .setTitle("Mock location disabled") @@ -177,7 +180,7 @@ class MainActivity : AppCompatActivity() { fun onClickReset(view: View?) { try { fakeLocationModule.stopFakeLocation() - } catch(e: Exception) { + } catch (e: Exception) { Log.e(TAG, "Can't stop FakeLocation", e) } } @@ -185,11 +188,11 @@ class MainActivity : AppCompatActivity() { private fun setFakeLocation(latitude: Double, longitude: Double) { try { fakeLocationModule.startFakeLocation() - } catch(e: Exception) { + } catch (e: Exception) { Log.e(TAG, "Can't startFakeLocation", e) } fakeLocationModule.setFakeLocation(latitude, longitude) - updateData("lat: ${latitude} - lon: ${longitude}") + updateData("lat: $latitude - lon: $longitude") } @Suppress("UNUSED_PARAMETER") diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt index 709963d253cecd2be35f4e0eaaee77af47eedf5e..424583614e2ac18f2c805db10cf399a8adb048a9 100644 --- a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt +++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt @@ -34,7 +34,7 @@ import android.util.Log * * @param context an Android context, to retrieve system services for example. */ -class FakeLocationModule(private val context: Context): IFakeLocationModule { +class FakeLocationModule(private val context: Context) : IFakeLocationModule { companion object { private const val TAG = "FakeLocationModule" } @@ -60,11 +60,10 @@ class FakeLocationModule(private val context: Context): IFakeLocationModule { providers.forEach { provider -> try { locationManager.removeTestProvider(provider) - } catch(e: Exception) { + } catch (e: Exception) { Log.w(TAG, "Test provider $provider already removed.") } - locationManager.addTestProvider( provider, false, @@ -75,12 +74,13 @@ class FakeLocationModule(private val context: Context): IFakeLocationModule { true, true, ProviderProperties.POWER_USAGE_LOW, - ProviderProperties.ACCURACY_FINE) + ProviderProperties.ACCURACY_FINE + ) try { locationManager.setTestProviderEnabled(provider, true) } catch (e: Exception) { - Log.e(TAG, "Can't enable test $provider", e) - } + Log.e(TAG, "Can't enable test $provider", e) + } } } diff --git a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt index 1337ddd0f741d74e0873b9dbfc32973aea91392a..34620feb9db3505d66dcefafa27f27e53699b56a 100644 --- a/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt +++ b/fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt @@ -17,7 +17,6 @@ package foundation.e.privacymodules.fakelocation - import android.app.Service import android.content.Context import android.content.Intent @@ -25,7 +24,7 @@ import android.os.CountDownTimer import android.os.IBinder import android.util.Log -class FakeLocationService: Service() { +class FakeLocationService : Service() { enum class Actions { START_FAKE_LOCATION @@ -83,10 +82,9 @@ class FakeLocationService: Service() { super.onDestroy() } - private fun initTimer() { countDownTimer?.cancel() - countDownTimer = object: CountDownTimer(PERIOD_UPDATES_SERIE, PERIOD_LOCATION_UPDATE) { + countDownTimer = object : CountDownTimer(PERIOD_UPDATES_SERIE, PERIOD_LOCATION_UPDATE) { override fun onTick(millisUntilFinished: Long) { fakeLocation?.let { try { diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java index d7f6ccf901f53dbcc60676e6925c1fdab6e832ec..53440e0c9934c35f6010472a478cf51e0ce3b032 100644 --- a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java +++ b/permissionse/libs/hidden-apis-stub/src/main/java/android/net/IConnectivityManager.java @@ -32,7 +32,7 @@ public interface IConnectivityManager { @TargetApi(29) @DeprecatedSinceApi( api = 31, - message = "TODO" + message = "Moved to android.net.VpnManager" ) boolean prepareVpn(String oldPackage, String newPackage, int userId) throws RemoteException; @@ -53,9 +53,9 @@ public interface IConnectivityManager { @TargetApi(29) @DeprecatedSinceApi( api = 31, - message = "TODO" + message = "Moved to android.net.VpnManager" ) - String getAlwaysOnVpnPackage(int userId) throws RemoteException; + public String getAlwaysOnVpnPackage(int userId) throws RemoteException; public abstract static class Stub extends Binder implements IConnectivityManager { public static IConnectivityManager asInterface(IBinder obj) { diff --git a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java b/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java index 7cc9b6a1f268cb36c42dfefa2a4cba64b41c30c9..bdb9e956e4cd4de63939341c0460e5af4e5a847d 100644 --- a/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java +++ b/permissionse/libs/hidden-apis-stub/src/main/java/android/net/VpnManager.java @@ -21,6 +21,7 @@ import android.annotation.TargetApi; import androidx.annotation.DeprecatedSinceApi; import androidx.annotation.Nullable; +import androidx.annotation.RequiresPermission; // Stub based on: // https://gitlab.e.foundation/e/os/android_frameworks_base/-/blob/[SDK_VERSION]/core/java/android/net/VpnManager.java @@ -50,4 +51,14 @@ public class VpnManager { int userId, int vpnType ) {} + + @TargetApi(31) + @DeprecatedSinceApi( + api = 33, + message = "Check disponibility in SDK33" + ) + @RequiresPermission("android.permission.CONTROL_ALWAYS_ON_VPN") + public String getAlwaysOnVpnPackageForUser(int userId) { + return null; + } } diff --git a/permissionse/src/main/AndroidManifest.xml b/permissionse/src/main/AndroidManifest.xml index 6a7e416c95903ade451c0c419a8896f390b64cf3..428a6127bf5049a8f7f05ba7e34a11b2b7b15c9a 100644 --- a/permissionse/src/main/AndroidManifest.xml +++ b/permissionse/src/main/AndroidManifest.xml @@ -33,4 +33,6 @@ /> + diff --git a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt index fe5b7ac47e0605b750df8dabab2f3ec58eaa10a2..83711dd079c728372cd160b21e8e8102dcf9d95f 100644 --- a/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionse/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package foundation.e.privacymodules.permissions import android.annotation.TargetApi @@ -18,10 +35,10 @@ import foundation.e.privacymodules.permissions.data.ApplicationDescription /** * Implements [IPermissionsPrivacyModule] with all privileges of a system app. */ -class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(context) { +class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(context) { - private val appOpsManager: AppOpsManager get() - = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager + private val appOpsManager: AppOpsManager + get() = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager /** * @see IPermissionsPrivacyModule.toggleDangerousPermission @@ -67,7 +84,7 @@ class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(cont } override fun setVpnPackageAuthorization(packageName: String): Boolean { - return when(Build.VERSION.SDK_INT) { + return when (Build.VERSION.SDK_INT) { 29 -> setVpnPackageAuthorizationSDK29(packageName) 30 -> setVpnPackageAuthorizationSDK30(packageName) 31, 32 -> setVpnPackageAuthorizationSDK32(packageName) @@ -81,7 +98,8 @@ class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(cont @TargetApi(29) private fun setVpnPackageAuthorizationSDK29(packageName: String): Boolean { val service: IConnectivityManager = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)) + ServiceManager.getService(Context.CONNECTIVITY_SERVICE) + ) try { if (service.prepareVpn(null, packageName, UserHandle.myUserId())) { @@ -101,7 +119,8 @@ class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(cont @TargetApi(30) private fun setVpnPackageAuthorizationSDK30(packageName: String): Boolean { val service: IConnectivityManager = IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)) + ServiceManager.getService(Context.CONNECTIVITY_SERVICE) + ) try { if (service.prepareVpn(null, packageName, UserHandle.myUserId())) { @@ -136,4 +155,40 @@ class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(cont } return false } + + override fun getAlwaysOnVpnPackage(): String? { + return when (Build.VERSION.SDK_INT) { + 29, 30 -> getAlwaysOnVpnPackageSDK29() + 31, 32 -> getAlwaysOnVpnPackageSDK32() + else -> { + Log.e("Permissions-e", "Bad android sdk version") + null + } + } + } + + @TargetApi(29) + private fun getAlwaysOnVpnPackageSDK29(): String? { + val service: IConnectivityManager = IConnectivityManager.Stub.asInterface( + ServiceManager.getService(Context.CONNECTIVITY_SERVICE) + ) + + return try { + service.getAlwaysOnVpnPackage(UserHandle.myUserId()) + } catch (e: java.lang.Exception) { + Log.e("Permissions-e", "Bad android sdk version ", e) + return null + } + } + + @TargetApi(31) + private fun getAlwaysOnVpnPackageSDK32(): String? { + val vpnManager = context.getSystemService(Context.VPN_MANAGEMENT_SERVICE) as VpnManager + return try { + vpnManager.getAlwaysOnVpnPackageForUser(UserHandle.myUserId()) + } catch (e: java.lang.Exception) { + Log.e("Permissions-e", "Bad android sdk version ", e) + return null + } + } } diff --git a/permissionsstandalone/build.gradle b/permissionsstandalone/build.gradle index ef03f2bf6045ac575ee4bc1792b5fbc8aee83d5e..8d2def87229619b7de14db563f474de8f97a1f38 100644 --- a/permissionsstandalone/build.gradle +++ b/permissionsstandalone/build.gradle @@ -57,4 +57,4 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' -} \ No newline at end of file +} diff --git a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt b/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt index d32cadad706526569e3c5689533d5dc1a39ae1f1..98ebdc3ba2ec80d1fca349db891eb795e9c72646 100644 --- a/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt +++ b/permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt @@ -18,15 +18,13 @@ package foundation.e.privacymodules.permissions import android.content.Context -import android.os.Build -import android.provider.Settings import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription /** * Implements [IPermissionsPrivacyModule] using only API authorized on the PlayStore. */ -class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(context) { +class PermissionsPrivacyModule(context: Context) : APermissionsPrivacyModule(context) { /** * @see IPermissionsPrivacyModule.toggleDangerousPermission * Return an ManualAction to go toggle manually the permission in the ap page of the settings. @@ -34,8 +32,8 @@ class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(cont override fun toggleDangerousPermission( appDesc: ApplicationDescription, permissionName: String, - grant: Boolean): Boolean = false - + grant: Boolean + ): Boolean = false override fun setAppOpMode( appDesc: ApplicationDescription, @@ -46,4 +44,8 @@ class PermissionsPrivacyModule(context: Context): APermissionsPrivacyModule(cont override fun setVpnPackageAuthorization(packageName: String): Boolean { return false } + + override fun getAlwaysOnVpnPackage(): String? { + return null + } } diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt index bcf82d2c511094710dbae2c58f7474741251ad82..9bf8abacfffe81fa249ed6dae8fd6370ba58ee54 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt @@ -26,7 +26,6 @@ object DependencyInjector { this.dnsBlocker = dnsBlocker } - lateinit var dnsBlocker: IDNSBlocker private set -} \ No newline at end of file +} diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt index 68f7ee1d572f46e3ca212c82dbdbfa48d188664a..9d7e675907015f4e58b2d30509ac9f3a0ebda061 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package foundation.e.privacymodules.permissions import android.app.AppOpsManager @@ -34,7 +35,7 @@ import foundation.e.privacymodules.permissions.data.PermissionDescription * versions of the module. * @param context an Android context, to retrieve packageManager for example. */ -abstract class APermissionsPrivacyModule(protected val context: Context): IPermissionsPrivacyModule { +abstract class APermissionsPrivacyModule(protected val context: Context) : IPermissionsPrivacyModule { companion object { private const val TAG = "PermissionsModule" @@ -59,9 +60,11 @@ abstract class APermissionsPrivacyModule(protected val context: Context): IPermi /** * @see IPermissionsPrivacyModule.getInstalledApplications */ - override fun getApplicationDescription(packageName: String): ApplicationDescription { + override fun getApplicationDescription(packageName: String, withIcon: Boolean): ApplicationDescription { val appDesc = buildApplicationDescription(context.packageManager.getApplicationInfo(packageName, 0), false) - appDesc.icon = getApplicationIcon(appDesc.packageName) + if (withIcon) { + appDesc.icon = getApplicationIcon(appDesc.packageName) + } return appDesc } @@ -102,13 +105,16 @@ abstract class APermissionsPrivacyModule(protected val context: Context): IPermi val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager val mode = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - appOps.checkOpNoThrow(appOpPermissionName, + appOps.checkOpNoThrow( + appOpPermissionName, - appDesc.uid, appDesc.packageName) + appDesc.uid, appDesc.packageName + ) } else { appOps.unsafeCheckOpNoThrow( appOpPermissionName, - appDesc.uid, appDesc.packageName) + appDesc.uid, appDesc.packageName + ) } return AppOpModes.getByModeValue(mode) @@ -138,15 +144,15 @@ abstract class APermissionsPrivacyModule(protected val context: Context): IPermi } } - override fun buildApplicationDescription(appInfo: ApplicationInfo, withIcon: Boolean) - : ApplicationDescription { - return ApplicationDescription( - packageName = appInfo.packageName, - uid = appInfo.uid, - label = getAppLabel(appInfo), - icon = if (withIcon) getApplicationIcon(appInfo.packageName) else null - ) - } + override fun buildApplicationDescription(appInfo: ApplicationInfo, withIcon: Boolean): + ApplicationDescription { + return ApplicationDescription( + packageName = appInfo.packageName, + uid = appInfo.uid, + label = getAppLabel(appInfo), + icon = if (withIcon) getApplicationIcon(appInfo.packageName) else null + ) + } private fun getAppLabel(appInfo: ApplicationInfo): CharSequence { return context.packageManager.getApplicationLabel(appInfo) diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt index ba85f1336c649418df0efebd6104387a204b3e83..6624798e079a63ba218a71c8848302e5ce9187d9 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt @@ -28,7 +28,10 @@ import foundation.e.privacymodules.permissions.data.PermissionDescription */ interface IPermissionsPrivacyModule { - fun buildApplicationDescription(appInfo: ApplicationInfo, withIcon: Boolean = true): ApplicationDescription + fun buildApplicationDescription( + appInfo: ApplicationInfo, + withIcon: Boolean = true + ): ApplicationDescription /** * List the installed application on the device which have not the FLAGS_SYSTEM. @@ -51,13 +54,12 @@ interface IPermissionsPrivacyModule { fun getPermissionDescription(permissionName: String): PermissionDescription - /** * Get the filled up [ApplicationDescription] for the app specified by its [packageName] * @param packageName the appId of the app * @return the informations about the app. */ - fun getApplicationDescription(packageName: String): ApplicationDescription + fun getApplicationDescription(packageName: String, withIcon: Boolean = true): ApplicationDescription /** * Check if the current runtime permission is granted for the specified app. @@ -68,7 +70,6 @@ interface IPermissionsPrivacyModule { */ fun isDangerousPermissionGranted(packageName: String, permissionName: String): Boolean - /** * Get the appOps mode for the specified [appOpPermissionName] of the specified application. * @@ -94,7 +95,6 @@ interface IPermissionsPrivacyModule { grant: Boolean ): Boolean - /** * Change the appOp Mode for the specified appOpPermission and application. * @param appDesc the application @@ -124,4 +124,8 @@ interface IPermissionsPrivacyModule { */ fun setVpnPackageAuthorization(packageName: String): Boolean -} \ No newline at end of file + /** + * Returns the package name of the currently set always-on VPN application, or null. + */ + fun getAlwaysOnVpnPackage(): String? +} diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt index 367645dd0e322c9bc4d8a0548ead10ecc687d448..47645961c31e05a5867817bff72704ab59670f3a 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt @@ -17,7 +17,11 @@ package foundation.e.privacymodules.permissions.data -import android.app.AppOpsManager.* +import android.app.AppOpsManager.MODE_ALLOWED +import android.app.AppOpsManager.MODE_DEFAULT +import android.app.AppOpsManager.MODE_ERRORED +import android.app.AppOpsManager.MODE_FOREGROUND +import android.app.AppOpsManager.MODE_IGNORED import android.os.Build enum class AppOpModes(val modeValue: Int) { @@ -34,7 +38,7 @@ enum class AppOpModes(val modeValue: Int) { IGNORED.modeValue to IGNORED, ERRORED.modeValue to ERRORED, DEFAULT.modeValue to DEFAULT, - ) + ) fun getByModeValue(modeValue: Int): AppOpModes { return byMode.get(modeValue) ?: DEFAULT diff --git a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt index 9ed297d3283522a066e89461a398929e2fa405b0..127192b90f1405b9096880be410bb67d3bb71ab8 100644 --- a/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt +++ b/privacymodule-api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt @@ -23,4 +23,4 @@ data class PermissionDescription( val group: String?, var label: CharSequence?, var description: CharSequence? -) \ No newline at end of file +) diff --git a/trackers/build.gradle b/trackers/build.gradle index 409996a529f0ff0c26a407ad1bf18481765e7aaf..f888acfc347bbe7ef7aae00ae5abc3f1a9b09966 100644 --- a/trackers/build.gradle +++ b/trackers/build.gradle @@ -1,51 +1,51 @@ -/* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - */ - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion buildConfig.compileSdk - - defaultConfig { - minSdkVersion buildConfig.minSdk - targetSdkVersion buildConfig.targetSdk - } - - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation project(':privacymodule-api') - implementation( - Libs.Kotlin.stdlib, - Libs.AndroidX.coreKtx, - Libs.Coroutines.core - ) -} +/* + Copyright (C) 2022 ECORP + + 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 2 + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion buildConfig.compileSdk + + defaultConfig { + minSdkVersion buildConfig.minSdk + targetSdkVersion buildConfig.targetSdk + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation project(':privacymodule-api') + implementation( + Libs.Kotlin.stdlib, + Libs.AndroidX.coreKtx, + Libs.Coroutines.core + ) +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt index 01ae5b74a9a3e626b7f401f6bbac3524e473871c..737aa4aeb1e477a98795e899c791b792af7c6225 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.kt @@ -1,164 +1,141 @@ -/* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - */ -/* - PersonalDNSFilter 1.5 - Copyright (C) 2017 Ingo Zenz - Copyright (C) 2021 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - */ -package foundation.e.privacymodules.trackers - -import android.content.Context -import android.content.pm.PackageManager -import android.net.LocalServerSocket -import android.system.ErrnoException -import android.system.Os -import android.system.OsConstants -import android.util.Log -import foundation.e.privacymodules.trackers.data.TrackersRepository -import foundation.e.privacymodules.trackers.data.WhitelistRepository -import java.io.BufferedReader -import java.io.IOException -import java.io.InputStreamReader -import java.io.PrintWriter - -class DNSBlockerRunnable( - ct: Context, - private val trackersLogger: TrackersLogger, - private val trackersRepository: TrackersRepository, - private val whitelistRepository: WhitelistRepository -) : Runnable { - var resolverReceiver: LocalServerSocket? = null - var stopped = false - private var eBrowserAppUid = -1 - - companion object { - private const val SOCKET_NAME = "foundation.e.advancedprivacy" - private const val E_BROWSER_DOT_SERVER = "chrome.cloudflare-dns.com" - private const val TAG = "DNSBlockerRunnable" - } - - init { - initEBrowserDoTFix(ct) - } - - @Synchronized - fun stop() { - stopped = true - closeSocket() - } - - private fun closeSocket() { - // Known bug and workaround that LocalServerSocket::close is not working well - // https://issuetracker.google.com/issues/36945762 - if (resolverReceiver != null) { - try { - Os.shutdown(resolverReceiver!!.fileDescriptor, OsConstants.SHUT_RDWR) - resolverReceiver!!.close() - resolverReceiver = null - } catch (e: ErrnoException) { - if (e.errno != OsConstants.EBADF) { - Log.w(TAG, "Socket already closed") - } else { - Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e) - } - } catch (e: Exception) { - Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e) - } - } - } - - override fun run() { - val resolverReceiver = try { - LocalServerSocket(SOCKET_NAME) - } catch (eio: IOException) { - Log.e(TAG, "Exception:Cannot open DNS port $SOCKET_NAME !", eio) - return - } - - this.resolverReceiver = resolverReceiver - Log.d(TAG, "DNSFilterProxy running on port $SOCKET_NAME !") - - while (!stopped) { - try { - val socket = resolverReceiver.accept() - val reader = BufferedReader(InputStreamReader(socket.inputStream)) - val line = reader.readLine() - val params = line.split(",").toTypedArray() - val output = socket.outputStream - val writer = PrintWriter(output, true) - val domainName = params[0] - val appUid = params[1].toInt() - var isBlocked = false - if (isEBrowserDoTBlockFix(appUid, domainName)) { - isBlocked = true - } else if (trackersRepository.isTracker(domainName)) { - val trackerId = trackersRepository.getTrackerId(domainName) - if (shouldBlock(appUid, trackerId)) { - writer.println("block") - isBlocked = true - } - trackersLogger.logAccess(trackerId, appUid, isBlocked) - } - if (!isBlocked) { - writer.println("pass") - } - socket.close() - // Printing bufferedreader data - } catch (e: IOException) { - Log.w(TAG, "Exception while listening DNS resolver", e) - } - } - } - - private fun initEBrowserDoTFix(context: Context) { - try { - eBrowserAppUid = - context.packageManager.getApplicationInfo("foundation.e.browser", 0).uid - } catch (e: PackageManager.NameNotFoundException) { - Log.i(TAG, "no E Browser package found.") - } - } - - private fun isEBrowserDoTBlockFix(appUid: Int, hostname: String): Boolean { - return appUid == eBrowserAppUid && E_BROWSER_DOT_SERVER == hostname - } - - private fun shouldBlock(appUid: Int, trackerId: String?): Boolean { - return whitelistRepository.isBlockingEnabled && - !whitelistRepository.isAppWhiteListed(appUid) && - !whitelistRepository.isTrackerWhiteListedForApp(trackerId, appUid) - } - - -} \ No newline at end of file +/* + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.trackers + +import android.content.Context +import android.content.pm.PackageManager +import android.net.LocalServerSocket +import android.system.ErrnoException +import android.system.Os +import android.system.OsConstants +import android.util.Log +import foundation.e.privacymodules.trackers.data.TrackersRepository +import foundation.e.privacymodules.trackers.data.WhitelistRepository +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.io.PrintWriter + +class DNSBlockerRunnable( + context: Context, + private val trackersLogger: TrackersLogger, + private val trackersRepository: TrackersRepository, + private val whitelistRepository: WhitelistRepository +) : Runnable { + var resolverReceiver: LocalServerSocket? = null + var stopped = false + private var eBrowserAppUid = -1 + + companion object { + private const val SOCKET_NAME = "foundation.e.advancedprivacy" + private const val E_BROWSER_DOT_SERVER = "chrome.cloudflare-dns.com" + private const val TAG = "DNSBlockerRunnable" + } + + init { + initEBrowserDoTFix(context) + } + + @Synchronized + fun stop() { + stopped = true + closeSocket() + } + + private fun closeSocket() { + // Known bug and workaround that LocalServerSocket::close is not working well + // https://issuetracker.google.com/issues/36945762 + if (resolverReceiver != null) { + try { + Os.shutdown(resolverReceiver!!.fileDescriptor, OsConstants.SHUT_RDWR) + resolverReceiver!!.close() + resolverReceiver = null + } catch (e: ErrnoException) { + if (e.errno != OsConstants.EBADF) { + Log.w(TAG, "Socket already closed") + } else { + Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e) + } + } catch (e: Exception) { + Log.e(TAG, "Exception: cannot close DNS port on stop $SOCKET_NAME !", e) + } + } + } + + override fun run() { + val resolverReceiver = try { + LocalServerSocket(SOCKET_NAME) + } catch (eio: IOException) { + Log.e(TAG, "Exception:Cannot open DNS port $SOCKET_NAME !", eio) + return + } + + this.resolverReceiver = resolverReceiver + Log.d(TAG, "DNSFilterProxy running on port $SOCKET_NAME !") + + while (!stopped) { + try { + val socket = resolverReceiver.accept() + val reader = BufferedReader(InputStreamReader(socket.inputStream)) + val line = reader.readLine() + val params = line.split(",").toTypedArray() + val output = socket.outputStream + val writer = PrintWriter(output, true) + val domainName = params[0] + val appUid = params[1].toInt() + var isBlocked = false + if (isEBrowserDoTBlockFix(appUid, domainName)) { + isBlocked = true + } else if (trackersRepository.isTracker(domainName)) { + val trackerId = trackersRepository.getTrackerId(domainName) + if (shouldBlock(appUid, trackerId)) { + writer.println("block") + isBlocked = true + } + trackersLogger.logAccess(trackerId, appUid, isBlocked) + } + if (!isBlocked) { + writer.println("pass") + } + socket.close() + // Printing bufferedreader data + } catch (e: IOException) { + Log.w(TAG, "Exception while listening DNS resolver", e) + } + } + } + + private fun initEBrowserDoTFix(context: Context) { + try { + eBrowserAppUid = + context.packageManager.getApplicationInfo("foundation.e.browser", 0).uid + } catch (e: PackageManager.NameNotFoundException) { + Log.i(TAG, "no E Browser package found.") + } + } + + private fun isEBrowserDoTBlockFix(appUid: Int, hostname: String): Boolean { + return appUid == eBrowserAppUid && E_BROWSER_DOT_SERVER == hostname + } + + private fun shouldBlock(appUid: Int, trackerId: String?): Boolean { + return whitelistRepository.isBlockingEnabled && + !whitelistRepository.isAppWhiteListed(appUid) && + !whitelistRepository.isTrackerWhiteListedForApp(trackerId, appUid) + } +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt index 3162422a20a827a196b1cd05f94b14bdc1eae035..97a0fda77af87b6a9836ebd0c823bd1000c7fc4b 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2021 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers import android.app.Service diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt index 30bba7b63ebe966469d381aa1e922ebc9e0a9e0d..69b4f288f3c846d747a9f32fac4a0058f472f9ff 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2021 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers import android.app.Notification @@ -43,4 +42,4 @@ object ForegroundStarter { service.startForeground(1337, notification) } } -} \ No newline at end of file +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt index 6d2abec12075e85a273fc50b12807ab97c775b4a..99e21482cd76ffe9eb5492821802d6e43e610a54 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers import android.content.Context @@ -66,4 +65,4 @@ class TrackersLogger(context: Context) { } inner class DetectedTracker(var trackerId: String?, var appUid: Int, var wasBlocked: Boolean) -} \ No newline at end of file +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt index 46729fdcf4fcb9a716eec055706efabcdcac8f82..25f0f2a8aa41e979a539e9c95971b5a59cae60b5 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.kt @@ -1,26 +1,25 @@ /* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers.api -import foundation.e.privacymodules.trackers.data.WhitelistRepository import android.content.Context import foundation.e.privacymodules.trackers.data.TrackersRepository +import foundation.e.privacymodules.trackers.data.WhitelistRepository class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule { private val mListeners = mutableListOf() @@ -31,7 +30,7 @@ class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule private var instance: BlockTrackersPrivacyModule? = null fun getInstance(context: Context): BlockTrackersPrivacyModule { - return instance?: BlockTrackersPrivacyModule(context).apply { instance = this } + return instance ?: BlockTrackersPrivacyModule(context).apply { instance = this } } } @@ -86,6 +85,4 @@ class BlockTrackersPrivacyModule(context: Context) : IBlockTrackersPrivacyModule override fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) { whitelistRepository.setWhiteListed(appUid, isWhiteListed) } - - -} \ No newline at end of file +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt index b07e210a05979d3abae18b430aa18b269dcaf146..9e1a0411886daefccfc2034d425bac39022ed9fb 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/IBlockTrackersPrivacyModule.kt @@ -22,7 +22,6 @@ package foundation.e.privacymodules.trackers.api */ interface IBlockTrackersPrivacyModule { - /** * Get the state of the blockin module * @return true when blocking is enabled, false otherwise. @@ -69,7 +68,6 @@ interface IBlockTrackersPrivacyModule { */ fun isWhitelisted(appUid: Int): Boolean - /** * List the white listed trackers for an App specified by it uid */ diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt index 5f1fa92b0f199285cfd93d2eb62db9bf4a6dde1a..264f247f1fe1a066ea884839921139caa0c8aa20 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/ITrackTrackersPrivacyModule.kt @@ -61,7 +61,6 @@ interface ITrackTrackersPrivacyModule { */ fun getPastYearTrackersCount(): Int - /** * Return number of trackers calls by hours, for the last 24hours. * @return list of 24 numbers of trackers calls by hours @@ -100,4 +99,4 @@ interface ITrackTrackersPrivacyModule { fun removeListener(listener: Listener) fun clearListeners() -} \ No newline at end of file +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt index d8f75aab0c65366f13fba9bde26abdf5c21b2836..18c56c9dce7f2fa262c7858f00ef3ae7d3f3a51a 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2021 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers.api import android.content.Context @@ -33,7 +32,7 @@ class TrackTrackersPrivacyModule(private val context: Context) : ITrackTrackersP private var instance: TrackTrackersPrivacyModule? = null fun getInstance(context: Context): TrackTrackersPrivacyModule { - return instance?: TrackTrackersPrivacyModule(context).apply { instance = this } + return instance ?: TrackTrackersPrivacyModule(context).apply { instance = this } } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt index c93fe906164040ca773d2e7867648f0657b00b15..21edb56f9cb286267535de7881a3244ae6b9dadf 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers.data import android.content.ContentValues @@ -97,10 +96,12 @@ class StatsDatabase(context: Context) : val selection = "$COLUMN_NAME_TIMESTAMP >= ?" val selectionArg = arrayOf("" + minTimestamp) - val projection = ("$COLUMN_NAME_TIMESTAMP, " + - "STRFTIME('${sqlitePeriodFormat}', DATETIME($COLUMN_NAME_TIMESTAMP, 'unixepoch', 'localtime')) $PROJECTION_NAME_PERIOD," + - "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM, " + - "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM") + val projection = ( + "$COLUMN_NAME_TIMESTAMP, " + + "STRFTIME('$sqlitePeriodFormat', DATETIME($COLUMN_NAME_TIMESTAMP, 'unixepoch', 'localtime')) $PROJECTION_NAME_PERIOD," + + "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM, " + + "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM" + ) val cursor = db.rawQuery( "SELECT $projection FROM $TABLE_NAME WHERE $selection" + @@ -208,7 +209,7 @@ class StatsDatabase(context: Context) : val db = readableDatabase val projection = "$COLUMN_NAME_APP_UID, $COLUMN_NAME_TRACKER" val cursor = db.rawQuery( - "SELECT DISTINCT $projection FROM $TABLE_NAME", //+ + "SELECT DISTINCT $projection FROM $TABLE_NAME", // + arrayOf() ) val countByApp = mutableMapOf() @@ -260,7 +261,7 @@ class StatsDatabase(context: Context) : val selectionArg = arrayOf("" + appUid, "" + minTimestamp) val projection = "SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM," + - "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM" + "SUM($COLUMN_NAME_NUMBER_BLOCKED) $PROJECTION_NAME_BLOCKED_SUM" val cursor = db.rawQuery( "SELECT $projection FROM $TABLE_NAME WHERE $selection", selectionArg @@ -363,7 +364,6 @@ class StatsDatabase(context: Context) : return entry } - fun getTrackers(appUids: List?): List { synchronized(lock) { val columns = arrayOf(COLUMN_NAME_TRACKER, COLUMN_NAME_APP_UID) @@ -444,5 +444,4 @@ class StatsDatabase(context: Context) : val columnIndex = getColumnIndex(columnName) return if (columnIndex >= 0) getString(columnIndex) else "" } - -} \ No newline at end of file +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt index f5d217a5d1ac3153ec348ec577ea4b5658fd3711..16d8ec674f47857334b5fae15666b4ef032b6992 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers.data import android.content.Context @@ -80,4 +79,4 @@ class StatsRepository private constructor(context: Context) { fun getMostLeakedApp(periodCount: Int, periodUnit: TemporalUnit): Int { return database.getMostLeakedApp(periodCount, periodUnit) } -} \ No newline at end of file +} diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt index bc4d50b68cfe687032e929316f0f697b7c3036bc..994bccff531bcdbb38b24b4c6d28024c46f4e6d4 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers.data import foundation.e.privacymodules.trackers.api.Tracker @@ -27,7 +26,7 @@ class TrackersRepository private constructor() { companion object { private var instance: TrackersRepository? = null fun getInstance(): TrackersRepository { - return instance?: TrackersRepository().apply { instance = this } + return instance ?: TrackersRepository().apply { instance = this } } } diff --git a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt index 65a8c39a61b19f667ab9f9c03a10a4de38d45ef9..e9f049da4f02dc898280c0146fc581ed813981a3 100644 --- a/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt +++ b/trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.kt @@ -1,21 +1,20 @@ /* - Copyright (C) 2022 ECORP - - 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 2 - 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, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + package foundation.e.privacymodules.trackers.data import android.content.Context @@ -34,7 +33,7 @@ class WhitelistRepository private constructor(context: Context) { private const val KEY_APP_TRACKERS_WHITELIST_PREFIX = "app_trackers_whitelist_" private var instance: WhitelistRepository? = null fun getInstance(context: Context): WhitelistRepository { - return instance?: WhitelistRepository(context).apply { instance = this } + return instance ?: WhitelistRepository(context).apply { instance = this } } } @@ -51,13 +50,13 @@ class WhitelistRepository private constructor(context: Context) { private fun reloadAppsWhiteList() { appsWhitelist = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.mapNotNull { - try { it.toInt() } catch(e: Exception) { null } - }?.toHashSet()?: HashSet() + try { it.toInt() } catch (e: Exception) { null } + }?.toHashSet() ?: HashSet() } private fun reloadAppTrackersWhiteList(appUid: Int) { val key = buildAppTrackersKey(appUid) - trackersWhitelistByApp[appUid] = prefs.getStringSet(key, HashSet())?: HashSet() + trackersWhitelistByApp[appUid] = prefs.getStringSet(key, HashSet()) ?: HashSet() } private fun reloadAllAppTrackersWhiteList() { @@ -70,8 +69,6 @@ class WhitelistRepository private constructor(context: Context) { } } - - var isBlockingEnabled: Boolean = false get() = field set(enabled) { @@ -79,9 +76,8 @@ class WhitelistRepository private constructor(context: Context) { field = enabled } - fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) { - val current = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.toHashSet()?: HashSet() + val current = prefs.getStringSet(KEY_APPS_WHITELIST, HashSet())?.toHashSet() ?: HashSet() if (isWhiteListed) { current.add("" + appUid) @@ -123,6 +119,6 @@ class WhitelistRepository private constructor(context: Context) { val whiteListedApp: List get() = appsWhitelist.toList() fun getWhiteListForApp(appUid: Int): List { - return trackersWhitelistByApp[appUid]?.toList()?: emptyList() + return trackersWhitelistByApp[appUid]?.toList() ?: emptyList() } -} \ No newline at end of file +}