diff options
5 files changed, 54 insertions, 24 deletions
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 index a701eec..0d25d16 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/IpScramblingStateUseCase.kt @@ -25,6 +25,7 @@ import foundation.e.privacymodules.permissions.IPermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow @@ -41,6 +42,9 @@ class IpScramblingStateUseCase( coroutineScope: CoroutineScope ) { + val _configuredMode = MutableStateFlow(localStateRepository.isIpScramblingEnabled) + val configuredMode: StateFlow<Boolean> = _configuredMode + val internetPrivacyMode: StateFlow<InternetPrivacyMode> = callbackFlow { val listener = object : IIpScramblerModule.Listener { override fun onStatusChanged(newStatus: IIpScramblerModule.Status) { @@ -78,10 +82,14 @@ class IpScramblingStateUseCase( } fun toggle(hideIp: Boolean) { - if (!localStateRepository.isQuickPrivacyEnabled) return - localStateRepository.isIpScramblingEnabled = hideIp - applySettings(true, hideIp) + _configuredMode.value = hideIp + + if (!localStateRepository.isQuickPrivacyEnabled) { + localStateRepository.setShowQuickPrivacyDisabledMessage(true) + } else { + applySettings(true, hideIp) + } } private fun getHiddenPackageNames(): List<String> { 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 eca1578..8e4318d 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 @@ -36,6 +36,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -63,7 +64,8 @@ class InternetPrivacyFeature( val bypassTorApps: Collection<String>, val selectedLocation: String, val availableLocationIds: List<String>, - val forceRedraw: Boolean = false + val forceRedraw: Boolean = false, + val showQuickPrivacyDisabledMessage: Boolean = false ) { fun getApps(): List<Pair<ApplicationDescription, Boolean>> { return availableApps.map { it to (it.packageName !in bypassTorApps) } @@ -84,6 +86,7 @@ class InternetPrivacyFeature( data class AndroidVpnActivityResultAction(val resultCode: Int) : Action() data class ToggleAppIpScrambled(val packageName: String) : Action() data class SelectLocationAction(val position: Int) : Action() + object CloseQuickPrivacyDisabledMessage : Action() } sealed class Effect { @@ -100,6 +103,7 @@ class InternetPrivacyFeature( data class LocationSelectedEffect(val locationId: String) : Effect() object WarningStartingLongEffect : Effect() data class ErrorEffect(val message: String) : Effect() + data class ShowQuickPrivacyDisabledMessageEffect(val show: Boolean) : Effect() } companion object { @@ -131,6 +135,7 @@ class InternetPrivacyFeature( ) is Effect.LocationSelectedEffect -> state.copy(selectedLocation = effect.locationId) Effect.QuickPrivacyDisabledWarningEffect -> state.copy(forceRedraw = !state.forceRedraw) + is Effect.ShowQuickPrivacyDisabledMessageEffect -> state.copy(showQuickPrivacyDisabledMessage = effect.show) else -> state } }, @@ -139,9 +144,24 @@ class InternetPrivacyFeature( action is Action.LoadInternetModeAction -> merge( getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow .map { Effect.QuickPrivacyUpdatedEffect(it) }, - ipScramblingStateUseCase.internetPrivacyMode - .map { Effect.ModeUpdatedEffect(it) } - .shareIn(scope = coroutineScope, started = SharingStarted.Lazily, replay = 0), + getQuickPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map { + Effect.ShowQuickPrivacyDisabledMessageEffect(it) + }, + getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.flatMapLatest { enabled -> + if (enabled) ipScramblingStateUseCase.internetPrivacyMode + .map { Effect.ModeUpdatedEffect(it) } + .shareIn( + scope = coroutineScope, + started = SharingStarted.Lazily, + replay = 0 + ) + else ipScramblingStateUseCase.configuredMode.map { + Effect.ModeUpdatedEffect( + if (it) InternetPrivacyMode.HIDE_IP + else InternetPrivacyMode.REAL_IP + ) + } + }, appListUseCase.getAppsUsingInternet().map { apps -> Effect.AvailableAppsListEffect( apps, @@ -175,24 +195,16 @@ class InternetPrivacyFeature( InternetPrivacyMode.HIDE_IP_LOADING, InternetPrivacyMode.REAL_IP_LOADING ) -> { - if (getQuickPrivacyStateUseCase.isQuickPrivacyEnabled) { - ipScramblingStateUseCase.toggle(hideIp = false) - flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.REAL_IP_LOADING)) - } else { - flowOf(Effect.QuickPrivacyDisabledWarningEffect) - } + ipScramblingStateUseCase.toggle(hideIp = false) + flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.REAL_IP_LOADING)) } action is Action.UseHiddenIPAction && state.mode in listOf( InternetPrivacyMode.REAL_IP, InternetPrivacyMode.REAL_IP_LOADING ) -> { - if (getQuickPrivacyStateUseCase.isQuickPrivacyEnabled) { - ipScramblingStateUseCase.toggle(hideIp = true) - flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.HIDE_IP_LOADING)) - } else { - flowOf(Effect.QuickPrivacyDisabledWarningEffect) - } + ipScramblingStateUseCase.toggle(hideIp = true) + flowOf(Effect.ModeUpdatedEffect(InternetPrivacyMode.HIDE_IP_LOADING)) } action is Action.ToggleAppIpScrambled -> { @@ -208,6 +220,10 @@ class InternetPrivacyFeature( flowOf(Effect.NoEffect) } } + action is Action.CloseQuickPrivacyDisabledMessage -> { + getQuickPrivacyStateUseCase.resetQuickPrivacyDisabledMessage() + flowOf(Effect.NoEffect) + } else -> flowOf(Effect.NoEffect) } }, @@ -216,8 +232,6 @@ class InternetPrivacyFeature( effect is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message) effect is Effect.WarningStartingLongEffect -> SingleEvent.ErrorEvent(R.string.ipscrambling_warning_starting_long) - effect is Effect.QuickPrivacyDisabledWarningEffect -> SingleEvent.ErrorEvent(error = R.string.ipscrambling_error_quickprivacy_disabled) - action is Action.UseHiddenIPAction && effect is Effect.ShowAndroidVpnDisclaimerEffect -> SingleEvent.StartAndroidVpnActivityEvent(effect.intent) 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 f49399f..2452d33 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 @@ -26,12 +26,14 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.snackbar.Snackbar 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.initQuickPrivacySnackbar import foundation.e.privacycentralapp.databinding.FragmentInternetActivityPolicyBinding import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode import foundation.e.privacycentralapp.extensions.toText @@ -56,6 +58,8 @@ class InternetPrivacyFragment : private lateinit var binding: FragmentInternetActivityPolicyBinding + private var qpDisabledSnackbar: Snackbar? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launchWhenStarted { @@ -132,12 +136,19 @@ class InternetPrivacyFragment : } } + qpDisabledSnackbar = initQuickPrivacySnackbar(binding.root) { + viewModel.submitAction(InternetPrivacyFeature.Action.CloseQuickPrivacyDisabledMessage) + } + binding.executePendingBindings() } override fun getTitle(): String = getString(R.string.ipscrambling_title) override fun render(state: InternetPrivacyFeature.State) { + if (state.showQuickPrivacyDisabledMessage) qpDisabledSnackbar?.show() + else qpDisabledSnackbar?.dismiss() + binding.radioUseHiddenIp.radiobutton.apply { isChecked = state.mode in listOf( InternetPrivacyMode.HIDE_IP, diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt index faac5a4..6ca93d3 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt @@ -109,7 +109,6 @@ class FakeLocationFeature( when (action) { is Action.Init -> { fakeLocationStateUseCase.startListeningLocation() - merge( getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.map { Effect.QuickPrivacyUpdatedEffect(it) }, fakeLocationStateUseCase.configuredLocationMode.map { (mode, lat, lon) -> diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml index d2cb53c..d79dea1 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -13,7 +13,6 @@ android:layout_width="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" > - <LinearLayout android:background="@color/background" android:gravity="center_horizontal" @@ -200,7 +199,6 @@ android:text="@string/dashboard_state_ipaddress_off" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/graph" /> - <View android:id="@+id/graph_legend_blocked_icon" android:layout_width="16dp" |