summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/build.gradle1
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt17
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/common/WarningDialog.kt64
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/data/repositories/LocalStateRepository.kt9
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/entities/ShowFeaturesWarning.kt29
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/IpScramblingStateUseCase.kt50
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/domain/usecases/ShowFeaturesWarningUseCase.kt25
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/dashboard/DashboardFragment.kt2
-rw-r--r--app/src/main/java/foundation/e/advancedprivacy/features/internetprivacy/InternetPrivacyViewModel.kt5
9 files changed, 152 insertions, 50 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 5ce72f3..ef54d45 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,6 +20,7 @@ plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
+ id 'kotlin-parcelize'
id 'androidx.navigation.safeargs.kotlin'
}
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)
}