diff options
Diffstat (limited to 'app/src/main')
8 files changed, 151 insertions, 50 deletions
diff --git a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt index 534aa6b..3fbb636 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2023 MURENA SAS + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + package foundation.e.advancedprivacy import android.content.res.Resources 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 1a83692..80fc760 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt @@ -23,23 +23,23 @@ import android.content.Context import android.content.Intent import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.View import android.widget.CheckBox +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity import foundation.e.advancedprivacy.R -import foundation.e.advancedprivacy.domain.entities.MainFeatures -import foundation.e.advancedprivacy.domain.entities.MainFeatures.FAKE_LOCATION -import foundation.e.advancedprivacy.domain.entities.MainFeatures.IP_SCRAMBLING -import foundation.e.advancedprivacy.domain.entities.MainFeatures.TRACKERS_CONTROL +import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning +import foundation.e.advancedprivacy.domain.usecases.IpScramblingStateUseCase import foundation.e.advancedprivacy.domain.usecases.ShowFeaturesWarningUseCase import foundation.e.advancedprivacy.main.MainActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import org.koin.java.KoinJavaComponent.get +import timber.log.Timber -class WarningDialog : Activity() { +class WarningDialog : AppCompatActivity() { companion object { private const val PARAM_FEATURE = "feature" @@ -57,33 +57,34 @@ class WarningDialog : Activity() { private fun createIntent( context: Context, - feature: MainFeatures, + feature: ShowFeaturesWarning, ): Intent { val intent = Intent(context, WarningDialog::class.java) - intent.putExtra(PARAM_FEATURE, feature.name) + intent.putExtra(PARAM_FEATURE, feature) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK return intent } } + private var isWaitingForResult = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - getWindow().setBackgroundDrawable(ColorDrawable(0)) + window.setBackgroundDrawable(ColorDrawable(0)) val feature = try { - MainFeatures.valueOf(intent.getStringExtra(PARAM_FEATURE) ?: "") + intent.getParcelableExtra<ShowFeaturesWarning>(PARAM_FEATURE)!! } catch (e: Exception) { - Log.e("WarningDialog", "Missing mandatory activity parameter", e) + Timber.e("Missing mandatory activity parameter", e) finish() return } - showWarningDialog(feature) } - private fun showWarningDialog(feature: MainFeatures) { + private fun showWarningDialog(feature: ShowFeaturesWarning) { val builder = AlertDialog.Builder(this) - builder.setOnDismissListener { finish() } + builder.setOnDismissListener { if (!isWaitingForResult) finish() } val content: View = layoutInflater.inflate(R.layout.alertdialog_do_not_show_again, null) val checkbox = content.findViewById<CheckBox>(R.id.checkbox) @@ -91,23 +92,23 @@ class WarningDialog : Activity() { builder.setMessage( when (feature) { - TRACKERS_CONTROL -> R.string.warningdialog_trackers_message - FAKE_LOCATION -> R.string.warningdialog_location_message - IP_SCRAMBLING -> R.string.warningdialog_ipscrambling_message + ShowFeaturesWarning.TrackersControl -> R.string.warningdialog_trackers_message + ShowFeaturesWarning.FakeLocation -> R.string.warningdialog_location_message + is ShowFeaturesWarning.IpScrambling -> R.string.warningdialog_ipscrambling_message } ) builder.setTitle( when (feature) { - TRACKERS_CONTROL -> R.string.warningdialog_trackers_title - FAKE_LOCATION -> R.string.warningdialog_location_title - IP_SCRAMBLING -> R.string.warningdialog_ipscrambling_title + ShowFeaturesWarning.TrackersControl -> R.string.warningdialog_trackers_title + ShowFeaturesWarning.FakeLocation -> R.string.warningdialog_location_title + is ShowFeaturesWarning.IpScrambling -> R.string.warningdialog_ipscrambling_title } ) builder.setPositiveButton( when (feature) { - IP_SCRAMBLING -> R.string.warningdialog_ipscrambling_cta + is ShowFeaturesWarning.IpScrambling -> R.string.warningdialog_ipscrambling_cta else -> R.string.ok } ) { _, _ -> @@ -115,10 +116,17 @@ class WarningDialog : Activity() { get<ShowFeaturesWarningUseCase>(ShowFeaturesWarningUseCase::class.java) .doNotShowAgain(feature) } - finish() + + val vpnDisclaimerIntent = (feature as? ShowFeaturesWarning.IpScrambling) + ?.startVpnDisclaimer + + if (vpnDisclaimerIntent != null) { + isWaitingForResult = true + launchAndroidVpnDisclaimer.launch(vpnDisclaimerIntent) + } else finish() } - if (feature == TRACKERS_CONTROL) { + if (feature == ShowFeaturesWarning.TrackersControl) { builder.setNeutralButton(R.string.warningdialog_trackers_secondary_cta) { _, _ -> MainActivity.deepLinkBuilder(this) .setDestination(R.id.trackersFragment) @@ -130,4 +138,14 @@ class WarningDialog : Activity() { builder.show() } + + private val launchAndroidVpnDisclaimer = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + val ipScramblingStateUseCase = get<IpScramblingStateUseCase>(IpScramblingStateUseCase::class.java) + if (result.resultCode == Activity.RESULT_OK) { + ipScramblingStateUseCase.startIpScrambling() + } else { + ipScramblingStateUseCase.toggle(false) + } + finish() + } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt index ba2836f..abc4de0 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify @@ -18,9 +19,11 @@ package foundation.e.advancedprivacy.data.repositories import android.content.Context +import android.content.Intent import foundation.e.advancedprivacy.domain.entities.ApplicationDescription import foundation.e.advancedprivacy.domain.entities.InternetPrivacyMode import foundation.e.advancedprivacy.domain.entities.LocationMode +import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow @@ -88,6 +91,12 @@ class LocalStateRepository(context: Context) { val internetPrivacyMode: MutableStateFlow<InternetPrivacyMode> = MutableStateFlow(InternetPrivacyMode.REAL_IP) + private val _startVpnDisclaimer = MutableSharedFlow<ShowFeaturesWarning.IpScrambling>() + suspend fun emitStartVpnDisclaimer(intent: Intent?) { + _startVpnDisclaimer.emit(ShowFeaturesWarning.IpScrambling(startVpnDisclaimer = intent)) + } + val startVpnDisclaimer: SharedFlow<ShowFeaturesWarning.IpScrambling> = _startVpnDisclaimer + private val _otherVpnRunning = MutableSharedFlow<ApplicationDescription>() suspend fun emitOtherVpnRunning(appDesc: ApplicationDescription) { _otherVpnRunning.emit(appDesc) diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt new file mode 100644 index 0000000..221f4e1 --- /dev/null +++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 MURENA SAS + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +package foundation.e.advancedprivacy.domain.entities + +import android.content.Intent +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +sealed class ShowFeaturesWarning : Parcelable { + object TrackersControl : ShowFeaturesWarning() + object FakeLocation : ShowFeaturesWarning() + data class IpScrambling(val startVpnDisclaimer: Intent? = null) : ShowFeaturesWarning() +} 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 a7ed660..27e7fe4 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 @@ -1,5 +1,6 @@ /* - * Copyright (C) 2021 E FOUNDATION, 2023 MURENA SAS + * Copyright (C) 2023 MURENA SAS + * 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 @@ -17,6 +18,7 @@ package foundation.e.advancedprivacy.domain.usecases +import android.content.Intent import foundation.e.advancedprivacy.data.repositories.AppListsRepository import foundation.e.advancedprivacy.data.repositories.LocalStateRepository import foundation.e.advancedprivacy.domain.entities.ApplicationDescription @@ -139,7 +141,7 @@ class IpScramblingStateUseCase( ipScramblerModule.appList = rawList } - private fun applySettings(isIpScramblingEnabled: Boolean) { + private suspend fun applySettings(isIpScramblingEnabled: Boolean) { val currentMode = localStateRepository.internetPrivacyMode.value when { isIpScramblingEnabled && currentMode in setOf(REAL_IP, REAL_IP_LOADING) -> @@ -152,23 +154,45 @@ class IpScramblingStateUseCase( } } - private fun applyStartIpScrambling() { - localStateRepository.internetPrivacyMode.value = HIDE_IP_LOADING - ipScramblerModule.prepareAndroidVpn()?.let { - permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName) - permissionsPrivacyModule.getAlwaysOnVpnPackage() - }?.let { - coroutineScope.launch { + private suspend fun applyStartIpScrambling() { + val authorizeVpnIntent = ipScramblerModule.prepareAndroidVpn() + if (authorizeVpnIntent == null) { + localStateRepository.emitStartVpnDisclaimer(null) + + startIpScrambling() + return + } + + acquireVpnAuthorization(authorizeVpnIntent) + } + + private suspend fun acquireVpnAuthorization(authorizeVpnIntent: Intent) { + val authorized = permissionsPrivacyModule.setVpnPackageAuthorization(appDesc.packageName) + val alwaysOnVpnPackage = permissionsPrivacyModule.getAlwaysOnVpnPackage() + + when { + authorized && alwaysOnVpnPackage == null -> { + localStateRepository.emitStartVpnDisclaimer(null) + startIpScrambling() + } + authorized && alwaysOnVpnPackage != null -> { localStateRepository.emitOtherVpnRunning( - permissionsPrivacyModule.getApplicationDescription(packageName = it, withIcon = false) + permissionsPrivacyModule.getApplicationDescription( + packageName = alwaysOnVpnPackage, + withIcon = false + ) ) + localStateRepository.setIpScramblingSetting(enabled = false) } - localStateRepository.setIpScramblingSetting(enabled = false) - } ?: run { - ipScramblerModule.start(enableNotification = false) + else -> localStateRepository.emitStartVpnDisclaimer(authorizeVpnIntent) } } + fun startIpScrambling() { + localStateRepository.internetPrivacyMode.value = HIDE_IP_LOADING + ipScramblerModule.start(enableNotification = false) // change the false ? + } + private fun map(status: IpScramblerModule.Status): InternetPrivacyMode { return when (status) { IpScramblerModule.Status.OFF -> REAL_IP 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 11bce86..c99d5f1 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 @@ -18,7 +18,10 @@ package foundation.e.advancedprivacy.domain.usecases import foundation.e.advancedprivacy.data.repositories.LocalStateRepository -import foundation.e.advancedprivacy.domain.entities.MainFeatures +import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning +import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning.FakeLocation +import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning.IpScrambling +import foundation.e.advancedprivacy.domain.entities.ShowFeaturesWarning.TrackersControl import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.dropWhile @@ -30,25 +33,25 @@ class ShowFeaturesWarningUseCase( private val localStateRepository: LocalStateRepository ) { - fun showWarning(): Flow<MainFeatures> { + fun showWarning(): Flow<ShowFeaturesWarning> { return merge( localStateRepository.blockTrackers.drop(1).dropWhile { !it } .filter { it && !localStateRepository.hideWarningTrackers } - .map { MainFeatures.TRACKERS_CONTROL }, + .map { TrackersControl }, localStateRepository.fakeLocationEnabled.drop(1).dropWhile { !it } .filter { it && !localStateRepository.hideWarningLocation } - .map { MainFeatures.FAKE_LOCATION }, - localStateRepository.ipScramblingSetting.drop(1).dropWhile { !it } - .filter { it && !localStateRepository.hideWarningIpScrambling } - .map { MainFeatures.IP_SCRAMBLING } + .map { FakeLocation }, + localStateRepository.startVpnDisclaimer.filter { + it.startVpnDisclaimer != null || !localStateRepository.hideWarningIpScrambling + } ) } - fun doNotShowAgain(feature: MainFeatures) { + fun doNotShowAgain(feature: ShowFeaturesWarning) { when (feature) { - MainFeatures.TRACKERS_CONTROL -> localStateRepository.hideWarningTrackers = true - MainFeatures.FAKE_LOCATION -> localStateRepository.hideWarningLocation = true - MainFeatures.IP_SCRAMBLING -> localStateRepository.hideWarningIpScrambling = true + TrackersControl -> localStateRepository.hideWarningTrackers = true + FakeLocation -> localStateRepository.hideWarningLocation = true + is IpScrambling -> localStateRepository.hideWarningIpScrambling = true } } } diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt index 5eb0bb6..999955e 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt @@ -105,7 +105,7 @@ class DashboardFragment : NavToolbarFragment(R.layout.fragment_dashboard) { } viewLifecycleOwner.lifecycleScope.launch { - viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { render(viewModel.state.value) viewModel.state.collect(::render) } diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt index 80e00bc..059e11d 100644 --- a/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt +++ b/app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt @@ -1,4 +1,5 @@ /* + * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify @@ -120,11 +121,11 @@ class InternetPrivacyViewModel( } } - private fun actionUseRealIP() { + private suspend fun actionUseRealIP() { ipScramblingStateUseCase.toggle(hideIp = false) } - private fun actionUseHiddenIP() { + private suspend fun actionUseHiddenIP() { ipScramblingStateUseCase.toggle(hideIp = true) } |