summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt9
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/LocalStateRepository.kt19
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt5
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/GetQuickPrivacyStateUseCase.kt3
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt116
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt30
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt21
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardViewModel.kt12
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt77
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFragment.kt41
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyViewModel.kt16
-rw-r--r--app/src/main/res/layout/fragment_dashboard.xml9
-rw-r--r--app/src/main/res/layout/fragment_internet_activity_policy.xml4
14 files changed, 283 insertions, 85 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index 17967db..094ec3e 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -22,6 +22,7 @@ import android.content.Context
import android.os.Process
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacycentralapp.features.dashboard.DashBoardViewModelFactory
import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyViewModelFactory
import foundation.e.privacycentralapp.features.location.FakeLocationViewModelFactory
@@ -32,6 +33,7 @@ import foundation.e.privacymodules.location.FakeLocation
import foundation.e.privacymodules.location.IFakeLocation
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import kotlinx.coroutines.GlobalScope
import lineageos.blockers.BlockerInterface
/**
@@ -68,9 +70,12 @@ class DependencyContainer constructor(val app: Application) {
private val getQuickPrivacyStateUseCase by lazy {
GetQuickPrivacyStateUseCase(localStateRepository)
}
+ private val ipScramblingStateUseCase by lazy {
+ IpScramblingStateUseCase(ipScramblerModule, localStateRepository, GlobalScope)
+ }
val dashBoardViewModelFactory by lazy {
- DashBoardViewModelFactory(getQuickPrivacyStateUseCase)
+ DashBoardViewModelFactory(getQuickPrivacyStateUseCase, ipScramblingStateUseCase)
}
val fakeLocationViewModelFactory by lazy {
@@ -80,6 +85,6 @@ class DependencyContainer constructor(val app: Application) {
val blockerService = BlockerInterface.getInstance(context)
val internetPrivacyViewModelFactory by lazy {
- InternetPrivacyViewModelFactory(ipScramblerModule, permissionsModule)
+ InternetPrivacyViewModelFactory(ipScramblerModule, permissionsModule, getQuickPrivacyStateUseCase, ipScramblingStateUseCase)
}
}
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 3cabae7..5f22c96 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
@@ -18,18 +18,31 @@
package foundation.e.privacycentralapp.data.repositories
import android.content.Context
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
class LocalStateRepository(context: Context) {
companion object {
private const val SHARED_PREFS_FILE = "localState"
private const val KEY_QUICK_PRIVACY = "quickPrivacy"
+ private const val KEY_IP_SCRAMBLING = "ipScrambling"
}
- val sharedPref = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
+ private val sharedPref = context.getSharedPreferences(SHARED_PREFS_FILE, Context.MODE_PRIVATE)
+ private val quickPrivacyEnabledMutableFlow = MutableStateFlow<Boolean>(sharedPref.getBoolean(KEY_QUICK_PRIVACY, false))
var isQuickPrivacyEnabled: Boolean
- get() = sharedPref.getBoolean(KEY_QUICK_PRIVACY, false)
- set(value) = set(KEY_QUICK_PRIVACY, value)
+ get() = quickPrivacyEnabledMutableFlow.value
+ set(value) {
+ set(KEY_QUICK_PRIVACY, value)
+ quickPrivacyEnabledMutableFlow.value = value
+ }
+
+ var quickPrivacyEnabledFlow: Flow<Boolean> = quickPrivacyEnabledMutableFlow
+
+ var isIpScramblingEnabled: Boolean
+ get() = sharedPref.getBoolean(KEY_IP_SCRAMBLING, false)
+ set(value) = set(KEY_IP_SCRAMBLING, value)
private fun set(key: String, value: Boolean) {
sharedPref.edit().putBoolean(key, value).commit()
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt
index 879c435..534bb2f 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt
@@ -18,5 +18,8 @@
package foundation.e.privacycentralapp.domain.entities
enum class InternetPrivacyMode {
- REAL_IP, HIDE_IP
+ REAL_IP,
+ HIDE_IP,
+ HIDE_IP_LOADING,
+ REAL_IP_LOADING
}
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 20ac0d9..db6f312 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
@@ -20,7 +20,8 @@ package foundation.e.privacycentralapp.domain.usecases
import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
class GetQuickPrivacyStateUseCase(private val localStateRepository: LocalStateRepository) {
- val isQuickPrivacyEnabled = localStateRepository.isQuickPrivacyEnabled
+ val quickPrivacyEnabledFlow = localStateRepository.quickPrivacyEnabledFlow
+ val isQuickPrivacyEnabled get() = localStateRepository.isQuickPrivacyEnabled
fun toggle(): Boolean {
val newState = !localStateRepository.isQuickPrivacyEnabled
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
new file mode 100644
index 0000000..a6bf50b
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt
@@ -0,0 +1,116 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+package foundation.e.privacycentralapp.domain.usecases
+
+import android.content.Intent
+import android.util.Log
+import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
+import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
+import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+class IpScramblingStateUseCase(
+ private val ipScramblerModule: IIpScramblerModule,
+ private val localStateRepository: LocalStateRepository,
+ private val coroutineScope: CoroutineScope
+) {
+
+ // private val internetPrivacyModeMutableFlow = MutableStateFlow(InternetPrivacyMode.REAL_IP)
+ val internetPrivacyMode: StateFlow<InternetPrivacyMode> = callbackFlow {
+ val listener = object : IIpScramblerModule.Listener {
+ override fun onStatusChanged(newStatus: IIpScramblerModule.Status) {
+ offer(map(newStatus))
+ }
+
+ override fun log(message: String) {}
+ override fun onTrafficUpdate(
+ upload: Long,
+ download: Long,
+ read: Long,
+ write: Long
+ ) {
+ }
+ }
+ ipScramblerModule.addListener(listener)
+ ipScramblerModule.requestStatus()
+ awaitClose { ipScramblerModule.removeListener(listener) }
+ }.stateIn(
+ scope = coroutineScope,
+ started = SharingStarted.Eagerly,
+ initialValue = InternetPrivacyMode.REAL_IP
+ )
+
+ init {
+ coroutineScope.launch {
+ localStateRepository.quickPrivacyEnabledFlow.collect {
+ Log.d("testQPFlow", "QuickPrivacy enabled: $it")
+ applySettings(it, localStateRepository.isIpScramblingEnabled)
+ }
+ }
+ }
+
+ fun toggle(hideIp: Boolean): Intent? {
+ if (!localStateRepository.isQuickPrivacyEnabled) return null
+
+ localStateRepository.isIpScramblingEnabled = hideIp
+ return applySettings(true, hideIp)
+ }
+
+ private fun applySettings(isQuickPrivacyEnabled: Boolean, isIpScramblingEnabled: Boolean): Intent? {
+ when {
+ isQuickPrivacyEnabled && isIpScramblingEnabled -> when (internetPrivacyMode.value) {
+ InternetPrivacyMode.REAL_IP, InternetPrivacyMode.REAL_IP_LOADING -> {
+ val intent = ipScramblerModule.prepareAndroidVpn()
+ if (intent != null) {
+ return intent
+ } else {
+ ipScramblerModule.start()
+ }
+ }
+ else -> {
+ Log.d("testQPFlow", "Not starting tor, already in started state")
+ }
+ }
+ else -> when (internetPrivacyMode.value) {
+ InternetPrivacyMode.HIDE_IP, InternetPrivacyMode.HIDE_IP_LOADING -> ipScramblerModule.stop()
+
+ else -> {
+ Log.d("testQPFlow", "Not stoping tor, already in stop or stoping state")
+ }
+ }
+ }
+ return null
+ }
+
+ private fun map(status: IIpScramblerModule.Status): InternetPrivacyMode {
+ return when (status) {
+ IIpScramblerModule.Status.OFF -> InternetPrivacyMode.REAL_IP
+ IIpScramblerModule.Status.ON -> InternetPrivacyMode.HIDE_IP
+ IIpScramblerModule.Status.STARTING -> InternetPrivacyMode.HIDE_IP_LOADING
+ IIpScramblerModule.Status.STOPPING,
+ IIpScramblerModule.Status.START_DISABLED -> InternetPrivacyMode.REAL_IP_LOADING
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
index 91a52ca..04ee5bf 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt
@@ -19,7 +19,6 @@ package foundation.e.privacycentralapp.dummy
import android.content.Context
import foundation.e.privacycentralapp.R
-import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
import foundation.e.privacycentralapp.domain.entities.LocationMode
fun LocationMode.mapToString(context: Context): String = when (this) {
@@ -27,8 +26,3 @@ fun LocationMode.mapToString(context: Context): String = when (this) {
LocationMode.RANDOM_LOCATION -> context.getString(R.string.random_location_mode)
LocationMode.CUSTOM_LOCATION -> context.getString(R.string.fake_location_mode)
}
-
-fun InternetPrivacyMode.mapToString(context: Context): String = when (this) {
- InternetPrivacyMode.REAL_IP -> context.getString(R.string.i_am_exposing)
- InternetPrivacyMode.HIDE_IP -> context.getString(R.string.i_am_anonymous)
-}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
index a461b65..9d439ec 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
@@ -25,8 +25,11 @@ import foundation.e.flowmvi.feature.BaseFeature
import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
import foundation.e.privacycentralapp.domain.entities.LocationMode
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
// Define a state machine for Dashboard Feature
class DashboardFeature(
@@ -70,6 +73,8 @@ class DashboardFeature(
}
sealed class Action {
+ object InitAction : Action()
+
object TogglePrivacyAction : Action()
// object ShowQuickPrivacyProtectionInfoAction : Action()
// object ObserveDashboardAction : Action()
@@ -81,7 +86,9 @@ class DashboardFeature(
}
sealed class Effect {
+ object NoEffect : Effect()
data class UpdateStateEffect(val isEnabled: Boolean) : Effect()
+ data class IpScramblingModeUpdatedEffect(val mode: InternetPrivacyMode) : Effect()
object OpenQuickPrivacyProtectionEffect : Effect()
data class OpenDashboardEffect(
@@ -110,7 +117,8 @@ class DashboardFeature(
companion object {
fun create(
coroutineScope: CoroutineScope,
- getPrivacyStateUseCase: GetQuickPrivacyStateUseCase
+ getPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ ipScramblingStateUseCase: IpScramblingStateUseCase
): DashboardFeature =
DashboardFeature(
initialState = State.DisabledState(),
@@ -138,6 +146,8 @@ class DashboardFeature(
)
else -> state
}
+ is Effect.IpScramblingModeUpdatedEffect -> if (state is State.EnabledState) state.copy(internetPrivacyMode = effect.mode)
+ else state
/*is Effect.OpenDashboardEffect -> State.DashboardState(
effect.trackersCount,
@@ -191,13 +201,23 @@ class DashboardFeature(
when (action) {
Action.TogglePrivacyAction -> {
if (state != State.LoadingState) {
- flowOf(Effect.UpdateStateEffect(getPrivacyStateUseCase.toggle()))
- } else {
- flowOf(Effect.UpdateStateEffect(getPrivacyStateUseCase.isQuickPrivacyEnabled))
+ getPrivacyStateUseCase.toggle()
}
+ flowOf(Effect.NoEffect)
}
- /*Action.ObserveDashboardAction -> merge(
+ Action.InitAction -> merge(
+ getPrivacyStateUseCase.quickPrivacyEnabledFlow.map {
+
+ Effect.UpdateStateEffect(it)
+ },
+ ipScramblingStateUseCase.internetPrivacyMode.map {
+ Effect.IpScramblingModeUpdatedEffect(it)
+ }
+ )
+ /*
+ Action.ObserveDashboardAction -> {
+ merge(
TrackersDataSource.trackers.map {
var activeTrackersCount: Int = 0
outer@ for (tracker in it) {
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 3e47a18..fade14b 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
@@ -105,10 +105,11 @@ class DashboardFragment :
}
}
}
- // lifecycleScope.launchWhenStarted {
- // viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction)
- // viewModel.submitAction(DashboardFeature.Action.ObserveDashboardAction)
- // }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(DashboardFeature.Action.InitAction)
+ // viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction)
+ // viewModel.submitAction(DashboardFeature.Action.ObserveDashboardAction)
+ }
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -195,10 +196,18 @@ class DashboardFragment :
)
val ipAddressEnabled = state is EnabledState && state.internetPrivacyMode != InternetPrivacyMode.REAL_IP
+ val isLoading = state is EnabledState && state.internetPrivacyMode in listOf(
+ InternetPrivacyMode.HIDE_IP_LOADING,
+ InternetPrivacyMode.REAL_IP_LOADING
+ )
binding.stateIpAddress.text = getString(
if (ipAddressEnabled) R.string.dashboard_state_ipaddress_on
else R.string.dashboard_state_ipaddress_off
)
+
+ binding.stateIpAddressLoader.visibility = if (isLoading) View.VISIBLE else View.GONE
+ binding.stateIpAddress.visibility = if (!isLoading) View.VISIBLE else View.GONE
+
binding.stateIpAddress.setTextColor(
getColor(
requireContext(),
@@ -233,11 +242,11 @@ class DashboardFragment :
)
binding.internetActivityPrivacy.subtitle.text = getString(
- if (state is DashboardFeature.State.EnabledState &&
+ if (state is EnabledState &&
state.internetPrivacyMode != InternetPrivacyMode.REAL_IP
)
R.string.dashboard_internet_activity_privacy_subtitle_on
- else R.string.dashboard_internet_activity_privacy_subtitle_on
+ else R.string.dashboard_internet_activity_privacy_subtitle_off
)
}
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 c25f215..67801eb 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
@@ -21,12 +21,14 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.privacycentralapp.common.Factory
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
class DashboardViewModel(
- private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase
+ private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ private val ipScramblingStateUseCase: IpScramblingStateUseCase
) : ViewModel() {
private val _actions = MutableSharedFlow<DashboardFeature.Action>()
@@ -35,7 +37,8 @@ class DashboardViewModel(
val dashboardFeature: DashboardFeature by lazy {
DashboardFeature.create(
coroutineScope = viewModelScope,
- getPrivacyStateUseCase = getPrivacyStateUseCase
+ getPrivacyStateUseCase = getPrivacyStateUseCase,
+ ipScramblingStateUseCase = ipScramblingStateUseCase
)
}
@@ -47,9 +50,10 @@ class DashboardViewModel(
}
class DashBoardViewModelFactory(
- private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase
+ private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ private val ipScramblingStateUseCase: IpScramblingStateUseCase
) : Factory<DashboardViewModel> {
override fun create(): DashboardViewModel {
- return DashboardViewModel(getPrivacyStateUseCase)
+ return DashboardViewModel(getPrivacyStateUseCase, ipScramblingStateUseCase)
}
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
index cbe0a04..d8254b8 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/internetprivacy/InternetPrivacyFeature.kt
@@ -25,16 +25,18 @@ import foundation.e.flowmvi.Actor
import foundation.e.flowmvi.Reducer
import foundation.e.flowmvi.SingleEventProducer
import foundation.e.flowmvi.feature.BaseFeature
+import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
+import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import foundation.e.privacymodules.permissions.data.ApplicationDescription
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
// Define a state machine for Internet privacy feature
@@ -53,11 +55,12 @@ class InternetPrivacyFeature(
singleEventProducer
) {
data class State(
- val mode: IIpScramblerModule.Status,
+ val mode: InternetPrivacyMode,
val availableApps: List<ApplicationDescription>,
val ipScrambledApps: Collection<String>,
val selectedLocation: String,
- val availableLocationIds: List<String>
+ val availableLocationIds: List<String>,
+ val forceRedraw: Boolean = false
) {
val isAllAppsScrambled get() = ipScrambledApps.isEmpty()
@@ -93,8 +96,10 @@ class InternetPrivacyFeature(
}
sealed class Effect {
- data class ModeUpdatedEffect(val mode: IIpScramblerModule.Status) : Effect()
object NoEffect : Effect()
+ data class ModeUpdatedEffect(val mode: InternetPrivacyMode) : Effect()
+ data class QuickPrivacyUpdatedEffect(val enabled: Boolean) : Effect()
+ object QuickPrivacyDisabledWarningEffect : Effect()
data class ShowAndroidVpnDisclaimerEffect(val intent: Intent) : Effect()
data class IpScrambledAppsUpdatedEffect(val ipScrambledApps: Collection<String>) : Effect()
data class AvailableAppsListEffect(val apps: List<ApplicationDescription>) : Effect()
@@ -106,7 +111,7 @@ class InternetPrivacyFeature(
companion object {
fun create(
initialState: State = State(
- IIpScramblerModule.Status.STOPPING,
+ mode = InternetPrivacyMode.REAL_IP,
availableApps = emptyList(),
ipScrambledApps = emptyList(),
availableLocationIds = emptyList(),
@@ -114,7 +119,9 @@ class InternetPrivacyFeature(
),
coroutineScope: CoroutineScope,
ipScramblerModule: IIpScramblerModule,
- permissionsModule: PermissionsPrivacyModule
+ permissionsModule: PermissionsPrivacyModule,
+ getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ ipScramblingStateUseCase: IpScramblingStateUseCase
) = InternetPrivacyFeature(
initialState, coroutineScope,
reducer = { state, effect ->
@@ -124,25 +131,15 @@ class InternetPrivacyFeature(
is Effect.AvailableAppsListEffect -> state.copy(availableApps = effect.apps)
is Effect.AvailableCountriesEffect -> state.copy(availableLocationIds = effect.availableLocationsIds)
is Effect.LocationSelectedEffect -> state.copy(selectedLocation = effect.locationId)
+ Effect.QuickPrivacyDisabledWarningEffect -> state.copy(forceRedraw = !state.forceRedraw)
else -> state
}
},
actor = { state, action ->
when {
action is Action.LoadInternetModeAction -> merge(
- callbackFlow {
- val listener = object : IIpScramblerModule.Listener {
- override fun onStatusChanged(newStatus: IIpScramblerModule.Status) {
- offer(Effect.ModeUpdatedEffect(newStatus))
- }
-
- override fun log(message: String) {}
- override fun onTrafficUpdate(upload: Long, download: Long, read: Long, write: Long) {}
- }
- ipScramblerModule.addListener(listener)
- ipScramblerModule.requestStatus()
- awaitClose { ipScramblerModule.removeListener(listener) }
- },
+ getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.map { Effect.QuickPrivacyUpdatedEffect(it) },
+ ipScramblingStateUseCase.internetPrivacyMode.map { Effect.ModeUpdatedEffect(it) },
flow {
// TODO: filter deactivated apps"
val apps = permissionsModule.getInstalledApplications()
@@ -179,12 +176,12 @@ class InternetPrivacyFeature(
action is Action.AndroidVpnActivityResultAction ->
if (action.resultCode == Activity.RESULT_OK) {
if (state.mode in listOf(
- IIpScramblerModule.Status.OFF,
- IIpScramblerModule.Status.STOPPING
+ InternetPrivacyMode.REAL_IP,
+ InternetPrivacyMode.REAL_IP_LOADING
)
) {
- ipScramblerModule.start()
- flowOf(Effect.ModeUpdatedEffect(IIpScramblerModule.Status.STARTING))
+ ipScramblingStateUseCase.toggle(hideIp = true)
+ flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.HIDE_IP_LOADING))
} else {
flowOf(Effect.ErrorEffect("Vpn already started"))
}
@@ -193,23 +190,30 @@ class InternetPrivacyFeature(
}
action is Action.UseRealIPAction && state.mode in listOf(
- IIpScramblerModule.Status.ON,
- IIpScramblerModule.Status.STARTING,
- IIpScramblerModule.Status.STOPPING
+ InternetPrivacyMode.HIDE_IP,
+ InternetPrivacyMode.HIDE_IP_LOADING,
+ InternetPrivacyMode.REAL_IP_LOADING
) -> {
- ipScramblerModule.stop()
- flowOf(Effect.ModeUpdatedEffect(IIpScramblerModule.Status.STOPPING))
+ if (getQuickPrivacyStateUseCase.isQuickPrivacyEnabled) {
+ ipScramblingStateUseCase.toggle(hideIp = false)
+ flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.REAL_IP_LOADING))
+ } else {
+ flowOf(Effect.QuickPrivacyDisabledWarningEffect)
+ }
}
action is Action.UseHiddenIPAction
&& state.mode in listOf(
- IIpScramblerModule.Status.OFF,
- IIpScramblerModule.Status.STOPPING
+ InternetPrivacyMode.REAL_IP,
+ InternetPrivacyMode.REAL_IP_LOADING
) -> {
- ipScramblerModule.prepareAndroidVpn()?.let {
- flowOf(Effect.ShowAndroidVpnDisclaimerEffect(it))
- } ?: run {
- ipScramblerModule.start()
- flowOf(Effect.ModeUpdatedEffect(IIpScramblerModule.Status.STARTING))
+ if (getQuickPrivacyStateUseCase.isQuickPrivacyEnabled) {
+ ipScramblingStateUseCase.toggle(hideIp = true)?.let {
+ flowOf(Effect.ShowAndroidVpnDisclaimerEffect(it))
+ } ?: run {
+ flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.HIDE_IP_LOADING))
+ }
+ } else {
+ flowOf(Effect.QuickPrivacyDisabledWarningEffect)
}
}
@@ -235,6 +239,7 @@ class InternetPrivacyFeature(
singleEventProducer = { _, action, effect ->
when {
effect is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
+ effect == Effect.QuickPrivacyDisabledWarningEffect -> SingleEvent.ErrorEvent("Enabled Quick Privacy to use functionalities")
action is Action.UseHiddenIPAction
&& effect is Effect.ShowAndroidVpnDisclaimerEffect ->
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 c2be7b1..3799349 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
@@ -18,6 +18,7 @@
package foundation.e.privacycentralapp.features.internetprivacy
import android.os.Bundle
+import android.util.Log
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
@@ -35,16 +36,17 @@ import foundation.e.flowmvi.MVIView
import foundation.e.privacycentralapp.DependencyContainer
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.ToolbarFragment
+import foundation.e.privacycentralapp.databinding.FragmentInternetActivityPolicyBinding
+import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode
import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf
-import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import java.util.Locale
class InternetPrivacyFragment :
- ToolbarFragment(R.layout.fragment_internet_activity_policy),
+ NavToolbarFragment(R.layout.fragment_internet_activity_policy),
MVIView<InternetPrivacyFeature.State, InternetPrivacyFeature.Action> {
private val dependencyContainer: DependencyContainer by lazy {
@@ -55,6 +57,8 @@ class InternetPrivacyFragment :
viewModelProviderFactoryOf { dependencyContainer.internetPrivacyViewModelFactory.create() }
}
+ private lateinit var binding: FragmentInternetActivityPolicyBinding
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launchWhenStarted {
@@ -63,9 +67,15 @@ class InternetPrivacyFragment :
lifecycleScope.launchWhenStarted {
viewModel.internetPrivacyFeature.singleEvents.collect { event ->
when (event) {
- is InternetPrivacyFeature.SingleEvent.ErrorEvent -> displayToast(event.error)
- is InternetPrivacyFeature.SingleEvent.StartAndroidVpnActivityEvent ->
+ is InternetPrivacyFeature.SingleEvent.ErrorEvent -> {
+ displayToast(event.error)
+ viewModel
+ }
+ is InternetPrivacyFeature.SingleEvent.StartAndroidVpnActivityEvent -> {
+ Log.d("TestsVPN", event.intent.toString())
+ Log.d("TestsVPN", event.intent.action.toString())
launchAndroidVpnDisclaimer.launch(event.intent)
+ }
InternetPrivacyFeature.SingleEvent.HiddenIPSelectedEvent -> displayToast("Your IP is hidden")
InternetPrivacyFeature.SingleEvent.RealIPSelectedEvent -> displayToast("Your IP is visible to internet")
}
@@ -87,6 +97,7 @@ class InternetPrivacyFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ binding = FragmentInternetActivityPolicyBinding.bind(view)
listOf(R.id.recycler_view_scrambled, R.id.recycler_view_to_select).forEach { viewId ->
view.findViewById<RecyclerView>(viewId)?.apply {
@@ -125,26 +136,26 @@ class InternetPrivacyFragment :
view?.let {
it.findViewById<RadioButton>(R.id.radio_use_hidden_ip).apply {
isChecked = state.mode in listOf(
- IIpScramblerModule.Status.ON,
- IIpScramblerModule.Status.STARTING
+ InternetPrivacyMode.HIDE_IP,
+ InternetPrivacyMode.HIDE_IP_LOADING
)
- isEnabled = state.mode != IIpScramblerModule.Status.STARTING
+ isEnabled = state.mode != InternetPrivacyMode.HIDE_IP_LOADING
}
it.findViewById<RadioButton>(R.id.radio_use_real_ip)?.apply {
isChecked =
state.mode in listOf(
- IIpScramblerModule.Status.OFF,
- IIpScramblerModule.Status.STOPPING
+ InternetPrivacyMode.REAL_IP,
+ InternetPrivacyMode.REAL_IP_LOADING
)
- isEnabled = state.mode != IIpScramblerModule.Status.STOPPING
+ isEnabled = state.mode != InternetPrivacyMode.REAL_IP_LOADING
}
it.findViewById<TextView>(R.id.ipscrambling_tor_status)?.apply {
when (state.mode) {
- IIpScramblerModule.Status.STARTING -> {
+ InternetPrivacyMode.HIDE_IP_LOADING -> {
text = getString(R.string.ipscrambling_is_starting)
visibility = View.VISIBLE
}
- IIpScramblerModule.Status.STOPPING -> {
+ InternetPrivacyMode.REAL_IP_LOADING -> {
text = getString(R.string.ipscrambling_is_stopping)
visibility = View.VISIBLE
}
@@ -205,8 +216,8 @@ class InternetPrivacyFragment :
when {
state.mode in listOf(
- IIpScramblerModule.Status.STARTING,
- IIpScramblerModule.Status.STOPPING
+ InternetPrivacyMode.HIDE_IP_LOADING,
+ InternetPrivacyMode.REAL_IP_LOADING
)
|| state.availableApps.isEmpty() -> {
progressBar?.visibility = View.VISIBLE
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 a6455ee..6f3c200 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
@@ -20,6 +20,8 @@ package foundation.e.privacycentralapp.features.internetprivacy
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import foundation.e.privacycentralapp.common.Factory
+import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -28,7 +30,9 @@ import kotlinx.coroutines.launch
class InternetPrivacyViewModel(
private val ipScramblerModule: IIpScramblerModule,
- private val permissionsModule: PermissionsPrivacyModule
+ private val permissionsModule: PermissionsPrivacyModule,
+ private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ private val ipScramblingStateUseCase: IpScramblingStateUseCase
) : ViewModel() {
private val _actions = MutableSharedFlow<InternetPrivacyFeature.Action>()
@@ -38,7 +42,9 @@ class InternetPrivacyViewModel(
InternetPrivacyFeature.create(
coroutineScope = viewModelScope,
ipScramblerModule = ipScramblerModule,
- permissionsModule = permissionsModule
+ permissionsModule = permissionsModule,
+ getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase,
+ ipScramblingStateUseCase = ipScramblingStateUseCase
)
}
@@ -51,10 +57,12 @@ class InternetPrivacyViewModel(
class InternetPrivacyViewModelFactory(
private val ipScramblerModule: IIpScramblerModule,
- private val permissionsModule: PermissionsPrivacyModule
+ private val permissionsModule: PermissionsPrivacyModule,
+ private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
+ private val ipScramblingStateUseCase: IpScramblingStateUseCase
) :
Factory<InternetPrivacyViewModel> {
override fun create(): InternetPrivacyViewModel {
- return InternetPrivacyViewModel(ipScramblerModule, permissionsModule)
+ return InternetPrivacyViewModel(ipScramblerModule, permissionsModule, getQuickPrivacyStateUseCase, ipScramblingStateUseCase)
}
}
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index 077eaf1..c97d1a5 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -123,11 +123,18 @@
android:id="@+id/state_ip_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/dashboard_state_ipaddress_off"
+android:text="@string/dashboard_state_ipaddress_off"
android:textSize="12sp"
android:textColor="@color/black_text"
android:textAllCaps="true"
+ android:visibility="gone"
/>
+ <ProgressBar
+ android:id="@+id/state_ip_address_loader"
+ android:layout_width="16dp"
+ android:layout_height="16dp"
+ android:indeterminate="true"
+ android:visibility="visible"/>
</LinearLayout>
</LinearLayout>
diff --git a/app/src/main/res/layout/fragment_internet_activity_policy.xml b/app/src/main/res/layout/fragment_internet_activity_policy.xml
index 2c7e102..982b7c4 100644
--- a/app/src/main/res/layout/fragment_internet_activity_policy.xml
+++ b/app/src/main/res/layout/fragment_internet_activity_policy.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+<layout>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -179,4 +180,5 @@
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
-</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
+</layout> \ No newline at end of file