From 74fb672978043886e261eb66c47658caf05812bb Mon Sep 17 00:00:00 2001 From: jacquarg Date: Fri, 29 Oct 2021 11:16:29 +0200 Subject: Centralise activate IPScrambling funcitonnality --- .../e/privacycentralapp/DependencyContainer.kt | 9 +- .../data/repositories/LocalStateRepository.kt | 19 +++- .../domain/entities/InternetPrivacyMode.kt | 5 +- .../domain/usecases/GetQuickPrivacyStateUseCase.kt | 3 +- .../domain/usecases/IpScramblingStateUseCase.kt | 116 +++++++++++++++++++++ .../e/privacycentralapp/dummy/Extensions.kt | 6 -- .../features/dashboard/DashboardFeature.kt | 30 +++++- .../features/dashboard/DashboardFragment.kt | 21 ++-- .../features/dashboard/DashboardViewModel.kt | 12 ++- .../internetprivacy/InternetPrivacyFeature.kt | 77 +++++++------- .../internetprivacy/InternetPrivacyFragment.kt | 41 +++++--- .../internetprivacy/InternetPrivacyViewModel.kt | 16 ++- app/src/main/res/layout/fragment_dashboard.xml | 9 +- .../layout/fragment_internet_activity_policy.xml | 4 +- 14 files changed, 283 insertions(+), 85 deletions(-) create mode 100644 app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt 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(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 = 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 . + */ + +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 = 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() @@ -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 { 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, val ipScrambledApps: Collection, val selectedLocation: String, - val availableLocationIds: List + val availableLocationIds: List, + 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) : Effect() data class AvailableAppsListEffect(val apps: List) : 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 { 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(viewId)?.apply { @@ -125,26 +136,26 @@ class InternetPrivacyFragment : view?.let { it.findViewById(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(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(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() @@ -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 { 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" /> + 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 @@ + - \ No newline at end of file + + \ No newline at end of file -- cgit v1.2.1