From 1f39e4aa31e3ef4b27a62070b33ec69f47b61525 Mon Sep 17 00:00:00 2001 From: jacquarg Date: Mon, 23 Sep 2024 14:42:45 +0200 Subject: [PATCH 1/5] tech:2617: localstate from sharedpref to pref DataStore --- app/build.gradle | 3 +- .../common/BootCompletedReceiver.kt | 13 +- .../e/advancedprivacy/common/WarningDialog.kt | 44 +++-- .../repositories/LocalStateRepositoryImpl.kt | 178 +++++++++++------- .../usecases/FakeLocationForAppUseCase.kt | 28 ++- .../usecases/FakeLocationStateUseCase.kt | 31 ++- .../usecases/GetQuickPrivacyStateUseCase.kt | 23 +-- .../usecases/IpScramblingStateUseCase.kt | 4 +- .../usecases/ShowFeaturesWarningUseCase.kt | 19 +- .../domain/usecases/TrackersScreenUseCase.kt | 6 +- .../widget/WidgetCommandReceiver.kt | 30 +-- core/build.gradle | 3 +- .../core/utils/BroadcastReceiverUtils.kt | 30 +++ .../core/utils/PreferenceDataStoreUtils.kt | 56 ++++++ .../repositories/LocalStateRepository.kt | 39 ++-- .../domain/usecases/VpnSupervisorUseCase.kt | 2 +- gradle/libs.versions.toml | 1 + .../service/VpnSupervisorUseCaseEos.kt | 13 +- .../VpnSupervisorUseCaseStandalone.kt | 7 +- 19 files changed, 346 insertions(+), 184 deletions(-) create mode 100644 core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt create mode 100644 core/src/main/java/foundation/e/advancedprivacy/core/utils/PreferenceDataStoreUtils.kt diff --git a/app/build.gradle b/app/build.gradle index fe8c3ce1..fb261186 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2024 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 @@ -153,6 +153,7 @@ dependencies { implementation ( libs.androidx.core.ktx, libs.androidx.appcompat, + libs.androidx.datastore.preferences, libs.androidx.fragment.ktx, libs.androidx.lifecycle.runtime, libs.androidx.lifecycle.viewmodel, diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt b/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt index 562144de..198ce818 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 -2024 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 @@ -22,18 +22,23 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import foundation.e.advancedprivacy.Notifications +import foundation.e.advancedprivacy.core.utils.goAsync import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository +import kotlinx.coroutines.CoroutineScope import org.koin.java.KoinJavaComponent.inject class BootCompletedReceiver : BroadcastReceiver() { private val localStateRepository by inject(LocalStateRepository::class.java) + private val backgroundScope by inject(CoroutineScope::class.java) override fun onReceive(context: Context, intent: Intent?) { if (intent?.action == Intent.ACTION_BOOT_COMPLETED) { - if (localStateRepository.firstBoot) { - Notifications.showFirstBootNotification(context) - localStateRepository.firstBoot = false + goAsync(backgroundScope) { + if (localStateRepository.isFirstBoot()) { + Notifications.showFirstBootNotification(context) + localStateRepository.setFirstBoot(false) + } } } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt index ecc2e4ac..ac769f22 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2024 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 @@ -29,6 +29,7 @@ import android.widget.CheckBox import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.domain.entities.MainFeatures import foundation.e.advancedprivacy.domain.entities.MainFeatures.FakeLocation @@ -38,8 +39,13 @@ import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase import foundation.e.advancedprivacy.main.MainActivity import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.koin.android.ext.android.inject import org.koin.java.KoinJavaComponent.get import timber.log.Timber @@ -63,6 +69,8 @@ class WarningDialog : AppCompatActivity() { } } + private val showFeaturesWarningUseCase: ShowFeaturesWarningUseCase by inject() + private var isWaitingForResult = false private lateinit var feature: MainFeatures @@ -115,23 +123,25 @@ class WarningDialog : AppCompatActivity() { else -> R.string.ok } ) { _, _ -> - if (checkbox.isChecked()) { - get(ShowFeaturesWarningUseCase::class.java) - .doNotShowAgain(feature) - } - - val vpnDisclaimerIntent = (feature as? MainFeatures.IpScrambling) - ?.startVpnDisclaimer - - if (vpnDisclaimerIntent != null) { - isWaitingForResult = true - launchAndroidVpnDisclaimer.launch(vpnDisclaimerIntent) - } else { - finish() + lifecycleScope.launch { + if (checkbox.isChecked()) { + withContext(Dispatchers.IO) { + showFeaturesWarningUseCase.doNotShowAgain(feature) + } + } + + val vpnDisclaimerIntent = (feature as? IpScrambling)?.startVpnDisclaimer + + if (vpnDisclaimerIntent != null) { + isWaitingForResult = true + launchAndroidVpnDisclaimer.launch(vpnDisclaimerIntent) + } else { + finish() + } } } - if (feature is MainFeatures.TrackersControl) { + if (feature is TrackersControl) { builder.setNeutralButton(R.string.warningdialog_trackers_secondary_cta) { _, _ -> MainActivity.deepLinkBuilder(this) .setDestination(R.id.trackersFragment) @@ -150,7 +160,9 @@ class WarningDialog : AppCompatActivity() { if (result.resultCode == Activity.RESULT_OK) { vpnSupervisorUseCase.startVpnService(feature) } else { - vpnSupervisorUseCase.cancelStartVpnService(feature) + runBlocking { + vpnSupervisorUseCase.cancelStartVpnService(feature) + } } finish() } diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepositoryImpl.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepositoryImpl.kt index 6c1be351..d7b716ac 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepositoryImpl.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepositoryImpl.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2023-2024 MURENA SAS - * Copyright (C) 2021 E FOUNDATION + * Copyright (C) 2021 - 2024 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 @@ -19,89 +19,111 @@ package foundation.e.advancedprivacy.data.repositories import android.content.Context -import androidx.core.content.edit +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.SharedPreferencesMigration +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.floatPreferencesKey +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.stringSetPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import foundation.e.advancedprivacy.core.utils.getValue +import foundation.e.advancedprivacy.core.utils.mapKey +import foundation.e.advancedprivacy.core.utils.setValue +import foundation.e.advancedprivacy.core.utils.toggleValue import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.entities.MainFeatures import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map class LocalStateRepositoryImpl(context: Context) : LocalStateRepository { companion object { private const val SHARED_PREFS_FILE = "localState" - private const val KEY_BLOCK_TRACKERS = "blockTrackers" - private const val KEY_IP_SCRAMBLING = "ipScrambling" - private const val KEY_FAKE_LOCATION = "fakeLocation" - private const val KEY_FAKE_LATITUDE = "fakeLatitude" - private const val KEY_FAKE_LONGITUDE = "fakeLongitude" - private const val KEY_FAKE_LOCATION_WHITELIST = "fakeLocationWhitelist" - private const val KEY_FIRST_BOOT = "firstBoot" - private const val KEY_HIDE_WARNING_TRACKERS = "hide_warning_trackers" - private const val KEY_HIDE_WARNING_LOCATION = "hide_warning_location" - private const val KEY_HIDE_WARNING_IPSCRAMBLING = "hide_warning_ipscrambling" - private const val KEY_TRACKERS_SCREEN_LAST_POSITION = "trackers_screen_last_position" + private const val PREF_DATASTORE = "localstate_datastore" } - private val sharedPref = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE) + private val blockTrackersKey = booleanPreferencesKey("blockTrackers") + private val ipScramblingKey = booleanPreferencesKey("ipScrambling") + private val fakeLocationKey = booleanPreferencesKey("fakeLocation") + private val fakeLatitudeKey = floatPreferencesKey("fakeLatitude") + private val fakeLongitudeKey = floatPreferencesKey("fakeLongitude") + private val fakeLocationWhitelistKey = stringSetPreferencesKey("fakeLocationWhitelist") + private val firstBootKey = booleanPreferencesKey("firstBoot") + private val hideWarningTrackersKey = booleanPreferencesKey("hide_warning_trackers") + private val hideWarningLocationKey = booleanPreferencesKey("hide_warning_location") + private val hideWarningIpScramblingKey = booleanPreferencesKey("hide_warning_ipscrambling") + private val trackersScreenLastPositionKey = intPreferencesKey("trackers_screen_last_position") - private val _blockTrackers = MutableStateFlow(sharedPref.getBoolean(KEY_BLOCK_TRACKERS, true)) + private val Context.dataStore: DataStore by preferencesDataStore( + name = PREF_DATASTORE, + produceMigrations = ::sharedPreferencesMigration + ) - override val blockTrackers = _blockTrackers.asStateFlow() + private val store = context.dataStore - override fun setBlockTrackers(enabled: Boolean) { - set(KEY_BLOCK_TRACKERS, enabled) - _blockTrackers.update { enabled } + private fun sharedPreferencesMigration(context: Context) = listOf(SharedPreferencesMigration(context, SHARED_PREFS_FILE)) + + override val blockTrackers: Flow = store.mapKey(blockTrackersKey, true) + + override suspend fun toggleBlockTrackers(enabled: Boolean?) { + store.toggleValue(blockTrackersKey, enabled, true) } override val areAllTrackersBlocked: MutableStateFlow = MutableStateFlow(false) - private val _fakeLocationEnabled = MutableStateFlow(sharedPref.getBoolean(KEY_FAKE_LOCATION, false)) - - override val fakeLocationEnabled = _fakeLocationEnabled.asStateFlow() + override val fakeLocationEnabled = store.mapKey(fakeLocationKey, false) - override fun setFakeLocationEnabled(enabled: Boolean) { - set(KEY_FAKE_LOCATION, enabled) - _fakeLocationEnabled.update { enabled } + override suspend fun toggleFakeLocation(enabled: Boolean?) { + store.toggleValue(fakeLocationKey, enabled, false) } - override var fakeLocation: Pair - get() = Pair( - // Initial default value is Quezon City - sharedPref.getFloat(KEY_FAKE_LATITUDE, 14.6760f), - sharedPref.getFloat(KEY_FAKE_LONGITUDE, 121.0437f) - ) + override val fakeLocation: Flow> = store.data.map { preferences -> + // Initial default value is Quezon City + val lat = preferences[fakeLatitudeKey] ?: 14.6760f + val lon = preferences[fakeLongitudeKey] ?: 121.0437f + lat to lon + } - set(value) { - sharedPref.edit() - .putFloat(KEY_FAKE_LATITUDE, value.first) - .putFloat(KEY_FAKE_LONGITUDE, value.second) - .apply() + override suspend fun getFakeLocation(): Pair = fakeLocation.first() + override suspend fun setFakeLocation(latLon: Pair) { + store.edit { preferences -> + preferences[fakeLatitudeKey] = latLon.first + preferences[fakeLongitudeKey] = latLon.second } + } + + override val fakeLocationWhitelistedApps = store.mapKey(fakeLocationWhitelistKey, emptySet()) - private val savedWhitelist = sharedPref.getStringSet(KEY_FAKE_LOCATION_WHITELIST, emptySet()) ?: emptySet() - private val _fakeLocationWhitelistedApps = MutableStateFlow>(savedWhitelist) - override val fakeLocationWhitelistedApps = _fakeLocationWhitelistedApps.asStateFlow() + override suspend fun toggleAppFakeLocationWhitelisted(app: DisplayableApp) { + store.edit { preferences -> + val whitelist = preferences[fakeLocationWhitelistKey] ?: emptySet() - override suspend fun updateFakeLocationWhitelist(whitelist: Set) { - _fakeLocationWhitelistedApps.update { - sharedPref.edit(commit = true) { - putStringSet(KEY_FAKE_LOCATION_WHITELIST, whitelist) + val apIds = app.apps.map { it.apId }.toSet() + + val appInWhitelist = apIds.any { whitelist.contains(it) } + preferences[fakeLocationWhitelistKey] = if (appInWhitelist) { + whitelist - apIds + } else { + whitelist + apIds } - whitelist } } - private val _ipScramblingSetting = MutableStateFlow(sharedPref.getBoolean(KEY_IP_SCRAMBLING, false)) - - override val ipScramblingSetting = _ipScramblingSetting.asStateFlow() + override suspend fun resetFakeLocationWhitelistedApp() { + store.setValue(fakeLocationWhitelistKey, emptySet()) + } - override fun setIpScramblingSetting(enabled: Boolean) { - set(KEY_IP_SCRAMBLING, enabled) - _ipScramblingSetting.update { enabled } + override val ipScramblingEnabled = store.mapKey(ipScramblingKey, false) + override suspend fun toggleIpScrambling(enabled: Boolean?) { + store.toggleValue(ipScramblingKey, enabled, false) } override val internetPrivacyMode: MutableStateFlow = MutableStateFlow(FeatureState.OFF) @@ -122,29 +144,45 @@ class LocalStateRepositoryImpl(context: Context) : LocalStateRepository { override val otherVpnRunning: SharedFlow = _otherVpnRunning - override var firstBoot: Boolean - get() = sharedPref.getBoolean(KEY_FIRST_BOOT, true) - set(value) = set(KEY_FIRST_BOOT, value) + override suspend fun isFirstBoot(): Boolean { + return store.getValue(firstBootKey) ?: true + } + + override suspend fun setFirstBoot(isStillFirstBoot: Boolean) { + store.setValue(firstBootKey, isStillFirstBoot) + } + + override suspend fun isHideWarningTrackers(): Boolean { + return store.getValue(hideWarningTrackersKey) ?: false + } + + override suspend fun hideWarningTrackers(hide: Boolean) { + return store.setValue(hideWarningTrackersKey, hide) + } - override var hideWarningTrackers: Boolean - get() = sharedPref.getBoolean(KEY_HIDE_WARNING_TRACKERS, false) - set(value) = set(KEY_HIDE_WARNING_TRACKERS, value) + override suspend fun isHideWarningLocation(): Boolean { + return store.getValue(hideWarningLocationKey) ?: false + } - override var hideWarningLocation: Boolean - get() = sharedPref.getBoolean(KEY_HIDE_WARNING_LOCATION, false) - set(value) = set(KEY_HIDE_WARNING_LOCATION, value) + override suspend fun hideWarningLocation(hide: Boolean) { + return store.setValue(hideWarningLocationKey, hide) + } - override var hideWarningIpScrambling: Boolean - get() = sharedPref.getBoolean(KEY_HIDE_WARNING_IPSCRAMBLING, false) - set(value) = set(KEY_HIDE_WARNING_IPSCRAMBLING, value) + override suspend fun isHideWarningIpScrambling(): Boolean { + return store.getValue(hideWarningIpScramblingKey) ?: false + } - override var trackersScreenLastPosition: Int - get() = sharedPref.getInt(KEY_TRACKERS_SCREEN_LAST_POSITION, 0) - set(value) = sharedPref.edit().putInt(KEY_TRACKERS_SCREEN_LAST_POSITION, value).apply() + override suspend fun hideWarningIpScrambling(hide: Boolean) { + return store.setValue(hideWarningIpScramblingKey, hide) + } - override var trackersScreenTabStartPosition: Int = 0 + override suspend fun getTrackersScreenLastPosition(): Int { + return store.getValue(trackersScreenLastPositionKey) ?: 0 + } - private fun set(key: String, value: Boolean) { - sharedPref.edit().putBoolean(key, value).apply() + override suspend fun setTrackersScreenLastPosition(position: Int) { + store.setValue(trackersScreenLastPositionKey, position) } + + override var trackersScreenTabStartPosition: Int = 0 } diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt index 43036bc1..c9f36d1e 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt @@ -18,11 +18,20 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.AppListRepository import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map class FakeLocationForAppUseCase( private val appListRepository: AppListRepository, - private val localStateRepository: LocalStateRepository + localStateRepository: LocalStateRepository, + backgroundScope: CoroutineScope ) { + // Cache these values to allow true sync exectution on getFakeLocationOrNull, + // which is called by the ContentProvider + private var fakeLocation: Pair? = null + private var whitelistedApp: Set = emptySet() private val nullFakeLocationPkgs = listOf( AppListRepository.PNAME_MICROG_SERVICES_CORE, @@ -30,16 +39,27 @@ class FakeLocationForAppUseCase( AppListRepository.PNAME_ANDROID_SYSTEM ) + init { + combine( + localStateRepository.fakeLocationEnabled, + localStateRepository.fakeLocation + ) { enabled, latLon -> + fakeLocation = if (enabled) latLon else null + }.launchIn(backgroundScope) + + localStateRepository.fakeLocationWhitelistedApps.map { whitelistedApp = it }.launchIn(backgroundScope) + } + fun getFakeLocationOrNull(packageName: String?, uid: Int): Pair? { - if (packageName == null || !localStateRepository.fakeLocationEnabled.value || packageName in nullFakeLocationPkgs) { + if (packageName == null || fakeLocation != null || packageName in nullFakeLocationPkgs) { return null } val app = appListRepository.getApp(uid) - return if (app?.apId != null && app.apId in localStateRepository.fakeLocationWhitelistedApps.value) { + return if (app?.apId != null && app.apId in whitelistedApp) { null } else { - localStateRepository.fakeLocation + fakeLocation } } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt index 8b96664f..47c24e8f 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationStateUseCase.kt @@ -47,7 +47,7 @@ class FakeLocationStateUseCase( init { coroutineScope.launch { localStateRepository.fakeLocationEnabled.collect { - applySettings(it, localStateRepository.fakeLocation) + applySettings(it, localStateRepository.getFakeLocation()) } } } @@ -66,43 +66,34 @@ class FakeLocationStateUseCase( return fakeLocation in citiesRepository.citiesLocationsList && !isSpecificLocation } - fun setSpecificLocation(latitude: Float, longitude: Float) { + suspend fun setSpecificLocation(latitude: Float, longitude: Float) { setFakeLocation(latitude to longitude, true) } - fun setRandomLocation() { + suspend fun setRandomLocation() { val randomIndex = Random.nextInt(citiesRepository.citiesLocationsList.size) val location = citiesRepository.citiesLocationsList[randomIndex] setFakeLocation(location) } - private fun setFakeLocation(location: Pair, isSpecificLocation: Boolean = false) { - localStateRepository.fakeLocation = location - localStateRepository.setFakeLocationEnabled(true) + private suspend fun setFakeLocation(location: Pair, isSpecificLocation: Boolean = false) { + localStateRepository.setFakeLocation(location) + localStateRepository.toggleFakeLocation(true) applySettings(true, location, isSpecificLocation) } - fun stopFakeLocation() { - localStateRepository.setFakeLocationEnabled(false) - applySettings(false, localStateRepository.fakeLocation) + suspend fun stopFakeLocation() { + localStateRepository.toggleFakeLocation(false) + applySettings(false, localStateRepository.getFakeLocation()) } suspend fun toggleBlacklist(app: DisplayableApp) { - val whitelist = localStateRepository.fakeLocationWhitelistedApps.value.toMutableSet() - val apIds = app.apps.map { it.apId }.toSet() - - if (apIds.any { whitelist.contains(it) }) { - whitelist.removeAll(apIds) - } else { - whitelist.addAll(apIds) - } - - localStateRepository.updateFakeLocationWhitelist(whitelist) + localStateRepository.toggleAppFakeLocationWhitelisted(app) } suspend fun resetBlacklist() { - localStateRepository.updateFakeLocationWhitelist(emptySet()) + localStateRepository.resetFakeLocationWhitelistedApp() } fun canResetBlacklist(): Flow = localStateRepository.fakeLocationWhitelistedApps.map { diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt index 8411904e..0a293ef2 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/GetQuickPrivacyStateUseCase.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2021 E FOUNDATION + * Copyright (C) 2021 - 2024 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 @@ -54,25 +54,16 @@ class GetQuickPrivacyStateUseCase( val ipScramblingMode: Flow = localStateRepository.internetPrivacyMode - fun toggleTrackers(enabled: Boolean?) { - val value = enabled ?: !localStateRepository.blockTrackers.value - if (value != localStateRepository.blockTrackers.value) { - localStateRepository.setBlockTrackers(value) - } + suspend fun toggleTrackers(enabled: Boolean?) { + localStateRepository.toggleBlockTrackers(enabled) } - fun toggleLocation(enabled: Boolean?) { - val value = enabled ?: !localStateRepository.fakeLocationEnabled.value - if (value != localStateRepository.fakeLocationEnabled.value) { - localStateRepository.setFakeLocationEnabled(value) - } + suspend fun toggleLocation(enabled: Boolean?) { + localStateRepository.toggleFakeLocation(enabled) } - fun toggleIpScrambling(enabled: Boolean?) { - val value = enabled ?: !localStateRepository.ipScramblingSetting.value - if (value != localStateRepository.ipScramblingSetting.value) { - localStateRepository.setIpScramblingSetting(value) - } + suspend fun toggleIpScrambling(enabled: Boolean?) { + localStateRepository.toggleIpScrambling(enabled) } val otherVpnRunning: SharedFlow = localStateRepository.otherVpnRunning diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt index dca8625a..2586f9ea 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt @@ -56,8 +56,8 @@ class IpScramblingStateUseCase( }.launchIn(backgroundScope) } - fun toggle(hideIp: Boolean) { - localStateRepository.setIpScramblingSetting(enabled = hideIp) + suspend fun toggle(hideIp: Boolean) { + localStateRepository.toggleIpScrambling(enabled = hideIp) } suspend fun getTorToggleableApp(): Flow> { diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt index f8a0986f..8c37fafc 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2024 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 @@ -25,7 +25,6 @@ import foundation.e.advancedprivacy.domain.entities.MainFeatures.TrackersControl import foundation.e.advancedprivacy.domain.repositories.LocalStateRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.dropWhile import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge @@ -36,21 +35,21 @@ class ShowFeaturesWarningUseCase( fun showWarning(): Flow { return merge( - localStateRepository.fakeLocationEnabled.drop(1).dropWhile { !it } - .filter { it && !localStateRepository.hideWarningLocation } + localStateRepository.fakeLocationEnabled.drop(1).filter { it } + .filter { it && !localStateRepository.isHideWarningLocation() } .map { FakeLocation }, localStateRepository.startVpnDisclaimer.filter { - (it is IpScrambling && !localStateRepository.hideWarningIpScrambling) || - (it is TrackersControl && !localStateRepository.hideWarningTrackers) + (it is IpScrambling && !localStateRepository.isHideWarningIpScrambling()) || + (it is TrackersControl && !localStateRepository.isHideWarningTrackers()) } ) } - fun doNotShowAgain(feature: MainFeatures) { + suspend fun doNotShowAgain(feature: MainFeatures) { when (feature) { - is TrackersControl -> localStateRepository.hideWarningTrackers = true - is FakeLocation -> localStateRepository.hideWarningLocation = true - is IpScrambling -> localStateRepository.hideWarningIpScrambling = true + is TrackersControl -> localStateRepository.hideWarningTrackers(true) + is FakeLocation -> localStateRepository.hideWarningLocation(true) + is IpScrambling -> localStateRepository.hideWarningIpScrambling(true) } } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersScreenUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersScreenUseCase.kt index 6c653b34..f0a2feff 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersScreenUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersScreenUseCase.kt @@ -28,11 +28,11 @@ class TrackersScreenUseCase( ) { suspend fun getLastPosition(): Int = withContext(backgroundDispatcher) { - localStateRepository.trackersScreenLastPosition + localStateRepository.getTrackersScreenLastPosition() } suspend fun savePosition(currentPosition: Int) = withContext(backgroundDispatcher) { - localStateRepository.trackersScreenLastPosition = currentPosition + localStateRepository.setTrackersScreenLastPosition(currentPosition) } fun getTrackerTabStartPosition(): Int { @@ -44,7 +44,7 @@ class TrackersScreenUseCase( } suspend fun preselectTab(periodPosition: Int, tabPosition: Int) = withContext(backgroundDispatcher) { - localStateRepository.trackersScreenLastPosition = periodPosition + localStateRepository.setTrackersScreenLastPosition(periodPosition) localStateRepository.trackersScreenTabStartPosition = tabPosition } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt index 6fe3ac54..8c36d6e2 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/widget/WidgetCommandReceiver.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2024 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 @@ -20,25 +20,31 @@ package foundation.e.advancedprivacy.widget import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import foundation.e.advancedprivacy.core.utils.goAsync import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase +import kotlinx.coroutines.CoroutineScope import org.koin.java.KoinJavaComponent.get class WidgetCommandReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { + val backgroundScope = get(CoroutineScope::class.java) val getQuickPrivacyStateUseCase = get(GetQuickPrivacyStateUseCase::class.java) - val featureEnabled = intent?.extras?.let { bundle -> - if (bundle.containsKey(PARAM_FEATURE_ENABLED)) { - bundle.getBoolean(PARAM_FEATURE_ENABLED) - } else { - null + goAsync(backgroundScope) { + val featureEnabled = intent?.extras?.let { bundle -> + if (bundle.containsKey(PARAM_FEATURE_ENABLED)) { + bundle.getBoolean(PARAM_FEATURE_ENABLED) + } else { + null + } + } + + when (intent?.action) { + ACTION_TOGGLE_TRACKERS -> getQuickPrivacyStateUseCase.toggleTrackers(featureEnabled) + ACTION_TOGGLE_LOCATION -> getQuickPrivacyStateUseCase.toggleLocation(featureEnabled) + ACTION_TOGGLE_IPSCRAMBLING -> getQuickPrivacyStateUseCase.toggleIpScrambling(featureEnabled) + else -> {} } - } - when (intent?.action) { - ACTION_TOGGLE_TRACKERS -> getQuickPrivacyStateUseCase.toggleTrackers(featureEnabled) - ACTION_TOGGLE_LOCATION -> getQuickPrivacyStateUseCase.toggleLocation(featureEnabled) - ACTION_TOGGLE_IPSCRAMBLING -> getQuickPrivacyStateUseCase.toggleIpScrambling(featureEnabled) - else -> {} } } diff --git a/core/build.gradle b/core/build.gradle index e9bf2d4b..aeb75ee6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 E FOUNDATION + * Copyright (C) 2022 - 2024 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 @@ -52,6 +52,7 @@ android { dependencies { implementation( libs.androidx.core.ktx, + libs.androidx.datastore.preferences, libs.bundles.koin, libs.kotlinx.coroutines ) diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt b/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt new file mode 100644 index 00000000..d3a5b8fa --- /dev/null +++ b/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 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.advancedprivacy.core.utils + +import android.content.BroadcastReceiver +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +fun BroadcastReceiver.goAsync(coroutineScope: CoroutineScope, block: suspend () -> Unit) { + val pendingResult = goAsync() + coroutineScope.launch { + block() + pendingResult.finish() + } +} diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/utils/PreferenceDataStoreUtils.kt b/core/src/main/java/foundation/e/advancedprivacy/core/utils/PreferenceDataStoreUtils.kt new file mode 100644 index 00000000..ddd0afc6 --- /dev/null +++ b/core/src/main/java/foundation/e/advancedprivacy/core/utils/PreferenceDataStoreUtils.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 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.advancedprivacy.core.utils + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map + +suspend fun DataStore.getValue(key: Preferences.Key): T? { + return data.map { it[key] }.firstOrNull() +} + +suspend fun DataStore.setValue(key: Preferences.Key, value: T) { + edit { preferences -> + preferences[key] = value + } +} + +suspend fun DataStore.removeKey(key: Preferences.Key) { + edit { preferences -> + preferences.remove(key) + } +} + +suspend fun DataStore.toggleValue(key: Preferences.Key, enabled: Boolean?, defaultValue: Boolean = true) { + edit { preferences -> + val toSet = enabled ?: !(preferences[key]?: defaultValue) + preferences[key] = toSet + } +} + +fun DataStore.mapKey(key: Preferences.Key): Flow { + return data.map { preferences -> preferences[key] }.distinctUntilChanged() +} + +fun DataStore.mapKey(key: Preferences.Key, default: T): Flow { + return data.map { preferences -> preferences[key] ?: default }.distinctUntilChanged() +} diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt index 9826155e..e8fcb540 100644 --- a/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/domain/repositories/LocalStateRepository.kt @@ -17,29 +17,33 @@ package foundation.e.advancedprivacy.domain.repositories import foundation.e.advancedprivacy.domain.entities.ApplicationDescription +import foundation.e.advancedprivacy.domain.entities.DisplayableApp import foundation.e.advancedprivacy.domain.entities.FeatureState import foundation.e.advancedprivacy.domain.entities.MainFeatures +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.StateFlow interface LocalStateRepository { - val blockTrackers: StateFlow - fun setBlockTrackers(enabled: Boolean) + val blockTrackers: Flow + suspend fun toggleBlockTrackers(enabled: Boolean?) val areAllTrackersBlocked: MutableStateFlow - val fakeLocationEnabled: StateFlow - fun setFakeLocationEnabled(enabled: Boolean) + val fakeLocationEnabled: Flow + suspend fun toggleFakeLocation(enabled: Boolean?) - var fakeLocation: Pair + val fakeLocation: Flow> + suspend fun getFakeLocation(): Pair + suspend fun setFakeLocation(latLon: Pair) - val fakeLocationWhitelistedApps: StateFlow> + val fakeLocationWhitelistedApps: Flow> - suspend fun updateFakeLocationWhitelist(whitelist: Set) + suspend fun toggleAppFakeLocationWhitelisted(app: DisplayableApp) + suspend fun resetFakeLocationWhitelistedApp() - fun setIpScramblingSetting(enabled: Boolean) - val ipScramblingSetting: StateFlow + val ipScramblingEnabled: Flow + suspend fun toggleIpScrambling(enabled: Boolean?) val internetPrivacyMode: MutableStateFlow @@ -50,15 +54,20 @@ interface LocalStateRepository { suspend fun emitOtherVpnRunning(appDesc: ApplicationDescription) val otherVpnRunning: SharedFlow - var firstBoot: Boolean + suspend fun isFirstBoot(): Boolean + suspend fun setFirstBoot(isStillFirstBoot: Boolean) - var hideWarningTrackers: Boolean + suspend fun isHideWarningTrackers(): Boolean + suspend fun hideWarningTrackers(hide: Boolean) - var hideWarningLocation: Boolean + suspend fun isHideWarningLocation(): Boolean + suspend fun hideWarningLocation(hide: Boolean) - var hideWarningIpScrambling: Boolean + suspend fun isHideWarningIpScrambling(): Boolean + suspend fun hideWarningIpScrambling(hide: Boolean) - var trackersScreenLastPosition: Int + suspend fun getTrackersScreenLastPosition(): Int + suspend fun setTrackersScreenLastPosition(position: Int) var trackersScreenTabStartPosition: Int } diff --git a/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt b/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt index fce9fd0c..da35b47b 100644 --- a/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/domain/usecases/VpnSupervisorUseCase.kt @@ -23,5 +23,5 @@ interface VpnSupervisorUseCase { fun startVpnService(feature: MainFeatures) - fun cancelStartVpnService(feature: MainFeatures) + suspend fun cancelStartVpnService(feature: MainFeatures) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cada81bb..71989aa0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ pcap4j = "1.8.2" androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version = "1.6.1" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.12.0" } +androidx-datastore-preferences = { group = "androidx.datastore", name="datastore-preferences", version = "1.1.1" } androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version = "1.6.2" } androidx-lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" } androidx-lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" } diff --git a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt index 1aa7d47a..afbf90fa 100644 --- a/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt +++ b/trackersserviceeos/src/main/java/foundation/e/advancedprivacy/trackers/service/VpnSupervisorUseCaseEos.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify @@ -29,7 +30,7 @@ import foundation.e.advancedprivacy.trackers.domain.externalinterfaces.TrackersS import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.dropWhile +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch class VpnSupervisorUseCaseEos( @@ -45,13 +46,13 @@ class VpnSupervisorUseCaseEos( trackersSupervisor.start() scope.launch(Dispatchers.IO) { - localStateRepository.ipScramblingSetting.collect { + localStateRepository.ipScramblingEnabled.collect { applySettings(it) } } scope.launch(Dispatchers.IO) { - localStateRepository.blockTrackers.drop(1).dropWhile { !it }.collect { + localStateRepository.blockTrackers.drop(1).filter { it }.collect { localStateRepository.emitStartVpnDisclaimer(TrackersControl()) } } @@ -87,7 +88,7 @@ class VpnSupervisorUseCaseEos( withIcon = false ) ) - localStateRepository.setIpScramblingSetting(enabled = false) + localStateRepository.toggleIpScrambling(enabled = false) return } } @@ -102,7 +103,7 @@ class VpnSupervisorUseCaseEos( orbotSupervisor.start() } - override fun cancelStartVpnService(feature: MainFeatures) { - localStateRepository.setIpScramblingSetting(enabled = false) + override suspend fun cancelStartVpnService(feature: MainFeatures) { + localStateRepository.toggleIpScrambling(enabled = false) } } diff --git a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt index d7bba5ee..3f86604a 100644 --- a/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt +++ b/trackersservicestandalone/src/main/java/foundation/e/advancedprivacy/trackers/service/usecases/VpnSupervisorUseCaseStandalone.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2024 E FOUNDATION * Copyright (C) 2023 MURENA SAS * * This program is free software: you can redistribute it and/or modify @@ -48,7 +49,7 @@ class VpnSupervisorUseCaseStandalone( var previousBlockTrackers: Boolean? = null localStateRepository.blockTrackers.combine( - localStateRepository.ipScramblingSetting + localStateRepository.ipScramblingEnabled ) { blockTrackers, hideIp -> applySettingJob?.cancel() applySettingJob = scope.launch { @@ -142,10 +143,10 @@ class VpnSupervisorUseCaseStandalone( getSupervisor(feature).start() } - override fun cancelStartVpnService(feature: MainFeatures) { + override suspend fun cancelStartVpnService(feature: MainFeatures) { when (feature) { is IpScrambling -> - localStateRepository.setIpScramblingSetting(enabled = false) + localStateRepository.toggleIpScrambling(enabled = false) is TrackersControl -> trackersSupervisor.stop() else -> {} -- GitLab From 0ef5c0d7ed1885be0db49994815e1f759405005d Mon Sep 17 00:00:00 2001 From: jacquarg Date: Mon, 23 Sep 2024 17:22:40 +0200 Subject: [PATCH 2/5] tech:2617: Apply coderabbit suggestions. --- .../e/advancedprivacy/common/WarningDialog.kt | 17 ++++++++--------- .../usecases/FakeLocationForAppUseCase.kt | 2 +- .../core/utils/BroadcastReceiverUtils.kt | 7 +++++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt index ac769f22..a1d900b7 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt @@ -43,7 +43,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import org.koin.android.ext.android.inject import org.koin.java.KoinJavaComponent.get @@ -154,16 +153,16 @@ class WarningDialog : AppCompatActivity() { } private val launchAndroidVpnDisclaimer = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - val vpnSupervisorUseCase = get( - VpnSupervisorUseCase::class.java - ) - if (result.resultCode == Activity.RESULT_OK) { - vpnSupervisorUseCase.startVpnService(feature) - } else { - runBlocking { + lifecycleScope.launch { + val vpnSupervisorUseCase = get( + VpnSupervisorUseCase::class.java + ) + if (result.resultCode == Activity.RESULT_OK) { + vpnSupervisorUseCase.startVpnService(feature) + } else { vpnSupervisorUseCase.cancelStartVpnService(feature) } + finish() } - finish() } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt index c9f36d1e..fd6c78d0 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/FakeLocationForAppUseCase.kt @@ -51,7 +51,7 @@ class FakeLocationForAppUseCase( } fun getFakeLocationOrNull(packageName: String?, uid: Int): Pair? { - if (packageName == null || fakeLocation != null || packageName in nullFakeLocationPkgs) { + if (packageName == null || fakeLocation == null || packageName in nullFakeLocationPkgs) { return null } diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt b/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt index d3a5b8fa..d6a5a9bd 100644 --- a/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt @@ -24,7 +24,10 @@ import kotlinx.coroutines.launch fun BroadcastReceiver.goAsync(coroutineScope: CoroutineScope, block: suspend () -> Unit) { val pendingResult = goAsync() coroutineScope.launch { - block() - pendingResult.finish() + try { + block() + } finally { + pendingResult.finish() + } } } -- GitLab From 15d8eb06455e3a79213d52a9bf1f565c76ed8fac Mon Sep 17 00:00:00 2001 From: jacquarg Date: Mon, 23 Sep 2024 17:48:47 +0200 Subject: [PATCH 3/5] feat:2617: Code rabbit fixes 2. --- .../foundation/e/advancedprivacy/common/WarningDialog.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt index a1d900b7..6075fe28 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt @@ -69,6 +69,7 @@ class WarningDialog : AppCompatActivity() { } private val showFeaturesWarningUseCase: ShowFeaturesWarningUseCase by inject() + private val vpnSupervisorUseCase: VpnSupervisorUseCase by inject() private var isWaitingForResult = false private lateinit var feature: MainFeatures @@ -123,7 +124,7 @@ class WarningDialog : AppCompatActivity() { } ) { _, _ -> lifecycleScope.launch { - if (checkbox.isChecked()) { + if (checkbox.isChecked) { withContext(Dispatchers.IO) { showFeaturesWarningUseCase.doNotShowAgain(feature) } @@ -154,9 +155,6 @@ class WarningDialog : AppCompatActivity() { private val launchAndroidVpnDisclaimer = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> lifecycleScope.launch { - val vpnSupervisorUseCase = get( - VpnSupervisorUseCase::class.java - ) if (result.resultCode == Activity.RESULT_OK) { vpnSupervisorUseCase.startVpnService(feature) } else { -- GitLab From db2c45fc432ec09334e384583c3b16b129b354d8 Mon Sep 17 00:00:00 2001 From: jacquarg Date: Tue, 1 Oct 2024 09:30:42 +0200 Subject: [PATCH 4/5] tech:2617: MR fixes, add catch block in goAsync. --- .../main/java/foundation/e/advancedprivacy/KoinModule.kt | 2 +- core/build.gradle | 3 ++- .../e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt index e0a6ba57..fa9f16b7 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt @@ -76,7 +76,7 @@ val appModule = module { SupervisorJob() + Dispatchers.IO + CoroutineExceptionHandler { _, throwable -> - Timber.e("Uncaught error in backgroundScope", throwable) + Timber.e(throwable, "Uncaught error in backgroundScope") } ) } diff --git a/core/build.gradle b/core/build.gradle index aeb75ee6..2796f299 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -54,6 +54,7 @@ dependencies { libs.androidx.core.ktx, libs.androidx.datastore.preferences, libs.bundles.koin, - libs.kotlinx.coroutines + libs.kotlinx.coroutines, + libs.timber ) } diff --git a/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt b/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt index d6a5a9bd..d72a06cb 100644 --- a/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt +++ b/core/src/main/java/foundation/e/advancedprivacy/core/utils/BroadcastReceiverUtils.kt @@ -18,14 +18,20 @@ package foundation.e.advancedprivacy.core.utils import android.content.BroadcastReceiver +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import timber.log.Timber fun BroadcastReceiver.goAsync(coroutineScope: CoroutineScope, block: suspend () -> Unit) { val pendingResult = goAsync() coroutineScope.launch { try { block() + } catch (ce: CancellationException) { + throw ce + } catch (e: Exception) { + Timber.e(e, "Uncaught exception in BroadcastReceiver.goAsync bloc") } finally { pendingResult.finish() } -- GitLab From 4cbb842bee138a773f5b1bc35212111cd2076f28 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Wed, 2 Oct 2024 07:11:03 +0000 Subject: [PATCH 5/5] tech:2617: MR fixes fix typo --- .../e/advancedprivacy/common/BootCompletedReceiver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt b/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt index 198ce818..8ddb1b38 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/BootCompletedReceiver.kt @@ -1,6 +1,6 @@ /* * Copyright (C) 2023 MURENA SAS - * Copyright (C) 2022 -2024 E FOUNDATION + * Copyright (C) 2022 - 2024 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 -- GitLab