From 66baccf4d687926e315bd7371ee523dd44519c32 Mon Sep 17 00:00:00 2001 From: jacquarg Date: Sat, 23 Apr 2022 16:29:10 +0200 Subject: 5290 : allow settings while main toggle off. --- app/src/main/res/layout/fragment_fake_location.xml | 3 +-- app/src/main/res/values/strings.xml | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'app/src/main/res') diff --git a/app/src/main/res/layout/fragment_fake_location.xml b/app/src/main/res/layout/fragment_fake_location.xml index 64432cb..47b86bf 100644 --- a/app/src/main/res/layout/fragment_fake_location.xml +++ b/app/src/main/res/layout/fragment_fake_location.xml @@ -9,6 +9,7 @@ android:layout_width="match_parent" > + - - Blocked leaks Allowed leaks Tap on the bars for more information. - + Changes will only be effective when privacy protection toggle is enabled. + Close @string/app_name -- cgit v1.2.3 From c3672a504341dad34ddf1c3d981b478b721baa30 Mon Sep 17 00:00:00 2001 From: jacquarg Date: Mon, 25 Apr 2022 20:18:25 +0200 Subject: Allow on ipscrambling feature. --- .../domain/usecases/IpScramblingStateUseCase.kt | 14 ++++-- .../internetprivacy/InternetPrivacyFeature.kt | 50 ++++++++++++++-------- .../internetprivacy/InternetPrivacyFragment.kt | 11 +++++ .../features/location/FakeLocationFeature.kt | 1 - app/src/main/res/layout/fragment_dashboard.xml | 2 - 5 files changed, 54 insertions(+), 24 deletions(-) (limited to 'app/src/main/res') 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 = _configuredMode + val internetPrivacyMode: StateFlow = 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 { 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, val selectedLocation: String, val availableLocationIds: List, - val forceRedraw: Boolean = false + val forceRedraw: Boolean = false, + val showQuickPrivacyDisabledMessage: Boolean = false ) { fun getApps(): List> { 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" > - - Date: Tue, 26 Apr 2022 09:06:46 +0200 Subject: Snackbar on each dashboard and trackers --- .../e/privacycentralapp/DependencyContainer.kt | 2 +- .../domain/usecases/TrackersStateUseCase.kt | 7 ++++++ .../features/dashboard/DashboardFeature.kt | 16 +++++++++--- .../features/dashboard/DashboardFragment.kt | 21 +++++++++++++--- .../features/trackers/TrackersFeature.kt | 18 +++++++++++--- .../features/trackers/TrackersFragment.kt | 10 ++++++++ .../features/trackers/TrackersViewModel.kt | 10 ++++++-- .../trackers/apptrackers/AppTrackersFeature.kt | 27 +++++++++++--------- .../trackers/apptrackers/AppTrackersFragment.kt | 19 ++++++++------ .../trackers/apptrackers/ToggleTrackersAdapter.kt | 6 ----- app/src/main/res/layout/apptrackers_fragment.xml | 29 ++++------------------ .../res/layout/apptrackers_item_tracker_toggle.xml | 18 -------------- 12 files changed, 103 insertions(+), 80 deletions(-) (limited to 'app/src/main/res') diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 0a06708..727d00d 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -119,7 +119,7 @@ class DependencyContainer(val app: Application) { } val trackersViewModelFactory by lazy { - TrackersViewModelFactory(trackersStatisticsUseCase) + TrackersViewModelFactory(getQuickPrivacyStateUseCase, trackersStatisticsUseCase) } val appTrackersViewModelFactory by lazy { diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt index 5263559..3319eb0 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt @@ -79,6 +79,10 @@ class TrackersStateUseCase( } fun toggleAppWhitelist(appUid: Int, isWhitelisted: Boolean) { + if (!localStateRepository.isQuickPrivacyEnabled) { + localStateRepository.setShowQuickPrivacyDisabledMessage(true) + } + if (appUid == appListsRepository.dummySystemApp.uid) { appListsRepository.getHiddenSystemApps().forEach { blockTrackersPrivacyModule.setWhiteListed(it.uid, isWhitelisted) @@ -89,6 +93,9 @@ class TrackersStateUseCase( } fun blockTracker(appUid: Int, tracker: Tracker, isBlocked: Boolean) { + if (!localStateRepository.isQuickPrivacyEnabled) { + localStateRepository.setShowQuickPrivacyDisabledMessage(true) + } if (appUid == appListsRepository.dummySystemApp.uid) { appListsRepository.getHiddenSystemApps().forEach { blockTrackersPrivacyModule.setWhiteListed(tracker, it.uid, !isBlocked) 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 7270c32..3ed3168 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 @@ -57,7 +57,8 @@ class DashboardFeature( val trackersCount: Int? = null, val allowedTrackersCount: Int? = null, val dayStatistics: List>? = null, - val dayLabels: List? = null + val dayLabels: List? = null, + val showQuickPrivacyDisabledMessage: Boolean = false ) sealed class SingleEvent { @@ -77,6 +78,7 @@ class DashboardFeature( object ShowAppsPermissions : Action() object ShowTrackers : Action() object FetchStatistics : Action() + object CloseQuickPrivacyDisabledMessage : Action() } sealed class Effect { @@ -99,6 +101,7 @@ class DashboardFeature( object NewStatisticsAvailablesEffect : Effect() object FirstIPTrackerActivationEffect : Effect() data class LocationHiddenUpdatedEffect(val isLocationHidden: Boolean) : Effect() + data class ShowQuickPrivacyDisabledMessageEffect(val show: Boolean) : Effect() } companion object { @@ -129,7 +132,7 @@ class DashboardFeature( isLocationHidden = effect.isLocationHidden ) is Effect.UpdateLocationModeEffect -> state.copy(locationMode = effect.mode) - + is Effect.ShowQuickPrivacyDisabledMessageEffect -> state.copy(showQuickPrivacyDisabledMessage = effect.show) else -> state } }, @@ -161,7 +164,10 @@ class DashboardFeature( }, getPrivacyStateUseCase.locationMode.map { Effect.UpdateLocationModeEffect(it) - } + }, + getPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map { + Effect.ShowQuickPrivacyDisabledMessageEffect(it) + }, ) Action.ShowFakeMyLocationAction -> flowOf(Effect.OpenFakeMyLocationEffect) Action.ShowAppsPermissions -> flowOf(Effect.OpenAppsPermissionsEffect) @@ -183,6 +189,10 @@ class DashboardFeature( ) } } + is Action.CloseQuickPrivacyDisabledMessage -> { + getPrivacyStateUseCase.resetQuickPrivacyDisabledMessage() + flowOf(Effect.NoEffect) + } } }, singleEventProducer = { _, _, effect -> 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 ea470a2..32549c9 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 @@ -26,15 +26,17 @@ import android.widget.Toast import androidx.core.content.ContextCompat.getColor import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels -import androidx.fragment.app.add import androidx.fragment.app.commit +import androidx.fragment.app.replace import androidx.lifecycle.lifecycleScope +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.GraphHolder import foundation.e.privacycentralapp.common.NavToolbarFragment +import foundation.e.privacycentralapp.common.initQuickPrivacySnackbar import foundation.e.privacycentralapp.databinding.FragmentDashboardBinding import foundation.e.privacycentralapp.domain.entities.LocationMode import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState @@ -65,6 +67,8 @@ class DashboardFragment : private lateinit var graphHolder: GraphHolder private lateinit var binding: FragmentDashboardBinding + private var qpDisabledSnackbar: Snackbar? = null + private var updateUIJob: Job? = null override fun onCreate(savedInstanceState: Bundle?) { @@ -79,14 +83,14 @@ class DashboardFragment : when (event) { is DashboardFeature.SingleEvent.NavigateToLocationSingleEvent -> { requireActivity().supportFragmentManager.commit { - add(R.id.container) + replace(R.id.container) setReorderingAllowed(true) addToBackStack("dashboard") } } is DashboardFeature.SingleEvent.NavigateToInternetActivityPrivacySingleEvent -> { requireActivity().supportFragmentManager.commit { - add(R.id.container) + replace(R.id.container) setReorderingAllowed(true) addToBackStack("dashboard") } @@ -97,7 +101,7 @@ class DashboardFragment : } DashboardFeature.SingleEvent.NavigateToTrackersSingleEvent -> { requireActivity().supportFragmentManager.commit { - add(R.id.container) + replace(R.id.container) setReorderingAllowed(true) addToBackStack("dashboard") } @@ -138,6 +142,10 @@ class DashboardFragment : binding.amITracked.container.setOnClickListener { viewModel.submitAction(DashboardFeature.Action.ShowTrackers) } + + qpDisabledSnackbar = initQuickPrivacySnackbar(binding.root) { + viewModel.submitAction(DashboardFeature.Action.CloseQuickPrivacyDisabledMessage) + } } override fun onResume() { @@ -149,6 +157,8 @@ class DashboardFragment : } } + render(viewModel.dashboardFeature.state.value) + viewModel.submitAction(DashboardFeature.Action.FetchStatistics) } @@ -162,6 +172,9 @@ class DashboardFragment : } override fun render(state: State) { + if (state.showQuickPrivacyDisabledMessage) qpDisabledSnackbar?.show() + else qpDisabledSnackbar?.dismiss() + binding.stateLabel.text = getString( when (state.quickPrivacyState) { QuickPrivacyState.DISABLED -> R.string.dashboard_state_title_off diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt index a606e49..34ddfbe 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt @@ -24,6 +24,7 @@ import foundation.e.flowmvi.SingleEventProducer import foundation.e.flowmvi.feature.BaseFeature import foundation.e.privacycentralapp.domain.entities.AppWithCounts import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics +import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.flow @@ -51,6 +52,7 @@ class TrackersFeature( val monthStatistics: TrackersPeriodicStatistics? = null, val yearStatistics: TrackersPeriodicStatistics? = null, val apps: List? = null, + val showQuickPrivacyDisabledMessage: Boolean = false ) sealed class SingleEvent { @@ -63,9 +65,11 @@ class TrackersFeature( object InitAction : Action() data class ClickAppAction(val packageName: String) : Action() object FetchStatistics : Action() + object CloseQuickPrivacyDisabledMessage : Action() } sealed class Effect { + object NoEffect : Effect() data class TrackersStatisticsLoadedEffect( val dayStatistics: TrackersPeriodicStatistics? = null, val monthStatistics: TrackersPeriodicStatistics? = null, @@ -75,14 +79,15 @@ class TrackersFeature( val apps: List ) : Effect() data class OpenAppDetailsEffect(val appDesc: AppWithCounts) : Effect() - object QuickPrivacyDisabledWarningEffect : Effect() data class ErrorEffect(val message: String) : Effect() object NewStatisticsAvailablesEffect : Effect() + data class ShowQuickPrivacyDisabledMessageEffect(val show: Boolean) : Effect() } companion object { fun create( initialState: State = State(), + getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, coroutineScope: CoroutineScope, trackersStatisticsUseCase: TrackersStatisticsUseCase ) = TrackersFeature( @@ -97,6 +102,7 @@ class TrackersFeature( is Effect.AvailableAppsListEffect -> state.copy(apps = effect.apps) is Effect.ErrorEffect -> state + is Effect.ShowQuickPrivacyDisabledMessageEffect -> state.copy(showQuickPrivacyDisabledMessage = effect.show) else -> state } }, @@ -106,7 +112,10 @@ class TrackersFeature( flowOf(Effect.NewStatisticsAvailablesEffect), trackersStatisticsUseCase.listenUpdates().map { Effect.NewStatisticsAvailablesEffect - } + }, + getQuickPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map { + Effect.ShowQuickPrivacyDisabledMessageEffect(it) + }, ) is Action.ClickAppAction -> flowOf( @@ -131,13 +140,16 @@ class TrackersFeature( Effect.AvailableAppsListEffect(it) } ) + is Action.CloseQuickPrivacyDisabledMessage -> { + getQuickPrivacyStateUseCase.resetQuickPrivacyDisabledMessage() + flowOf(Effect.NoEffect) + } } }, singleEventProducer = { _, _, effect -> when (effect) { is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message) is Effect.OpenAppDetailsEffect -> SingleEvent.OpenAppDetailsEvent(effect.appDesc) - is Effect.QuickPrivacyDisabledWarningEffect -> SingleEvent.ErrorEvent("Enabled Quick Privacy to use functionalities") is Effect.NewStatisticsAvailablesEffect -> SingleEvent.NewStatisticsAvailableSingleEvent else -> null } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt index 893f4ba..21a90bc 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt @@ -26,6 +26,7 @@ import androidx.fragment.app.replace 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 @@ -33,6 +34,7 @@ import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.AppsAdapter import foundation.e.privacycentralapp.common.GraphHolder import foundation.e.privacycentralapp.common.NavToolbarFragment +import foundation.e.privacycentralapp.common.initQuickPrivacySnackbar import foundation.e.privacycentralapp.databinding.FragmentTrackersBinding import foundation.e.privacycentralapp.databinding.TrackersItemGraphBinding import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics @@ -57,6 +59,7 @@ class TrackersFragment : private lateinit var dayGraphHolder: GraphHolder private lateinit var monthGraphHolder: GraphHolder private lateinit var yearGraphHolder: GraphHolder + private var qpDisabledSnackbar: Snackbar? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -111,6 +114,10 @@ class TrackersFragment : ) } } + + qpDisabledSnackbar = initQuickPrivacySnackbar(binding.root) { + viewModel.submitAction(TrackersFeature.Action.CloseQuickPrivacyDisabledMessage) + } } override fun onResume() { @@ -121,6 +128,9 @@ class TrackersFragment : override fun getTitle() = getString(R.string.trackers_title) override fun render(state: TrackersFeature.State) { + if (state.showQuickPrivacyDisabledMessage) qpDisabledSnackbar?.show() + else qpDisabledSnackbar?.dismiss() + state.dayStatistics?.let { renderGraph(it, dayGraphHolder, binding.graphDay) } state.monthStatistics?.let { renderGraph(it, monthGraphHolder, binding.graphMonth) } state.yearStatistics?.let { renderGraph(it, yearGraphHolder, binding.graphYear) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt index c2a1822..4140381 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt @@ -20,12 +20,14 @@ package foundation.e.privacycentralapp.features.trackers 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.TrackersStatisticsUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch class TrackersViewModel( + private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, private val trackersStatisticsUseCase: TrackersStatisticsUseCase ) : ViewModel() { @@ -35,7 +37,7 @@ class TrackersViewModel( val trackersFeature: TrackersFeature by lazy { TrackersFeature.create( coroutineScope = viewModelScope, - + getQuickPrivacyStateUseCase = getQuickPrivacyStateUseCase, trackersStatisticsUseCase = trackersStatisticsUseCase ) } @@ -48,10 +50,14 @@ class TrackersViewModel( } class TrackersViewModelFactory( + private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase, private val trackersStatisticsUseCase: TrackersStatisticsUseCase ) : Factory { override fun create(): TrackersViewModel { - return TrackersViewModel(trackersStatisticsUseCase) + return TrackersViewModel( + getQuickPrivacyStateUseCase, + trackersStatisticsUseCase + ) } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt index c1eef47..ad82337 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt @@ -55,7 +55,8 @@ class AppTrackersFeature( val isBlockingActivated: Boolean = false, val trackers: List? = null, val whitelist: List? = null, - val isQuickPrivacyEnabled: Boolean = false + val isQuickPrivacyEnabled: Boolean = false, + val showQuickPrivacyDisabledMessage: Boolean = false ) { fun getTrackersStatus(): List>? { if (trackers != null && whitelist != null) { @@ -83,6 +84,7 @@ class AppTrackersFeature( data class ToggleTrackerAction(val tracker: Tracker, val isBlocked: Boolean) : Action() data class ClickTracker(val tracker: Tracker) : Action() object FetchStatistics : Action() + object CloseQuickPrivacyDisabledMessage : Action() } sealed class Effect { @@ -94,8 +96,8 @@ class AppTrackersFeature( data class TrackersWhitelistUpdateEffect(val whitelist: List) : Effect() object NewStatisticsAvailablesEffect : Effect() data class QuickPrivacyUpdatedEffect(val enabled: Boolean) : Effect() - object QuickPrivacyDisabledWarningEffect : Effect() data class OpenUrlEffect(val url: Uri) : Effect() + data class ShowQuickPrivacyDisabledMessageEffect(val show: Boolean) : Effect() } companion object { @@ -121,6 +123,7 @@ class AppTrackersFeature( state.copy(whitelist = effect.whitelist) is Effect.QuickPrivacyUpdatedEffect -> state.copy(isQuickPrivacyEnabled = effect.enabled) + is Effect.ShowQuickPrivacyDisabledMessageEffect -> state.copy(showQuickPrivacyDisabledMessage = effect.show) is Effect.ErrorEffect -> state else -> state } @@ -155,14 +158,15 @@ class AppTrackersFeature( }, getQuickPrivacyStateUseCase.quickPrivacyEnabledFlow.map { Effect.QuickPrivacyUpdatedEffect(it) - } + }, + getQuickPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map { + Effect.ShowQuickPrivacyDisabledMessageEffect(it) + }, ) } ?: flowOf(Effect.ErrorEffect(R.string.apptrackers_error_no_app)) is Action.BlockAllToggleAction -> - if (!state.isQuickPrivacyEnabled) { - flowOf(Effect.QuickPrivacyDisabledWarningEffect) - } else state.appDesc?.uid?.let { appUid -> + state.appDesc?.uid?.let { appUid -> flow { trackersStateUseCase.toggleAppWhitelist(appUid, !action.isBlocked) @@ -174,9 +178,7 @@ class AppTrackersFeature( } } ?: run { flowOf(Effect.ErrorEffect("No appDesc.")) } is Action.ToggleTrackerAction -> { - if (!state.isQuickPrivacyEnabled) { - flowOf(Effect.QuickPrivacyDisabledWarningEffect) - } else if (state.isBlockingActivated) { + if (state.isBlockingActivated) { state.appDesc?.uid?.let { appUid -> flow { trackersStateUseCase.blockTracker( @@ -210,15 +212,16 @@ class AppTrackersFeature( trackers = trackersStatisticsUseCase.getTrackers(it) ) } ?: Effect.ErrorEffect("No appDesc.") - ) + is Action.CloseQuickPrivacyDisabledMessage -> { + getQuickPrivacyStateUseCase.resetQuickPrivacyDisabledMessage() + flowOf(Effect.NoEffect) + } } }, singleEventProducer = { _, _, effect -> when (effect) { is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message) - is Effect.QuickPrivacyDisabledWarningEffect -> - SingleEvent.ErrorEvent(R.string.apptrackers_error_quickprivacy_disabled) is Effect.NewStatisticsAvailablesEffect -> SingleEvent.NewStatisticsAvailableSingleEvent is Effect.OpenUrlEffect -> diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt index d6edee6..7e606af 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt @@ -27,11 +27,13 @@ import androidx.core.view.isVisible 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.initQuickPrivacySnackbar import foundation.e.privacycentralapp.databinding.ApptrackersFragmentBinding import foundation.e.privacycentralapp.extensions.toText import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf @@ -65,6 +67,8 @@ class AppTrackersFragment : private lateinit var binding: ApptrackersFragmentBinding + private var qpDisabledSnackbar: Snackbar? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleScope.launchWhenStarted { @@ -109,10 +113,6 @@ class AppTrackersFragment : viewModel.submitAction(Action.BlockAllToggleAction(binding.blockAllToggle.isChecked)) } - binding.blockAllToggleClicker.setOnClickListener { - viewModel.submitAction(Action.BlockAllToggleAction(false)) - } - binding.trackers.apply { layoutManager = LinearLayoutManager(requireContext()) setHasFixedSize(true) @@ -124,6 +124,10 @@ class AppTrackersFragment : onClickTitle = { viewModel.submitAction(Action.ClickTracker(it)) } ) } + + qpDisabledSnackbar = initQuickPrivacySnackbar(binding.root) { + viewModel.submitAction(Action.CloseQuickPrivacyDisabledMessage) + } } override fun onResume() { @@ -132,6 +136,9 @@ class AppTrackersFragment : } override fun render(state: State) { + if (state.showQuickPrivacyDisabledMessage) qpDisabledSnackbar?.show() + else qpDisabledSnackbar?.dismiss() + binding.trackersCountSummary.text = if (state.getTrackersCount() == 0) "" else getString( R.string.apptrackers_trackers_count_summary, @@ -140,8 +147,6 @@ class AppTrackersFragment : ) binding.blockAllToggle.isChecked = state.isBlockingActivated - binding.blockAllToggle.isEnabled = state.isQuickPrivacyEnabled - binding.blockAllToggleClicker.isVisible = !state.isQuickPrivacyEnabled binding.trackersListTitle.isVisible = state.isBlockingActivated @@ -151,7 +156,7 @@ class AppTrackersFragment : binding.trackers.post { (binding.trackers.adapter as ToggleTrackersAdapter?)?.updateDataSet( trackersStatus, - state.isBlockingActivated && state.isQuickPrivacyEnabled + state.isBlockingActivated ) } binding.noTrackersYet.isVisible = false diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt index b9beccf..02a274a 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt @@ -25,7 +25,6 @@ import android.view.ViewGroup import android.widget.Switch import android.widget.TextView import androidx.core.content.ContextCompat -import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import foundation.e.privacycentralapp.R import foundation.e.privacymodules.trackers.Tracker @@ -46,7 +45,6 @@ class ToggleTrackersAdapter( val title: TextView = view.findViewById(R.id.title) val toggle: Switch = view.findViewById(R.id.toggle) - val toggleOverlay: View = view.findViewById(R.id.toggle_clicker) fun bind(item: Pair, isEnabled: Boolean) { val text = item.first.label @@ -62,14 +60,10 @@ class ToggleTrackersAdapter( toggle.isChecked = item.second toggle.isEnabled = isEnabled - toggleOverlay.isVisible = !isEnabled toggle.setOnClickListener { onToggleSwitch(item.first, toggle.isChecked) } - toggleOverlay.setOnClickListener { - onToggleSwitch(item.first, false) - } title.setOnClickListener { onClickTitle(item.first) } } diff --git a/app/src/main/res/layout/apptrackers_fragment.xml b/app/src/main/res/layout/apptrackers_fragment.xml index 3eb9168..e6e226f 100644 --- a/app/src/main/res/layout/apptrackers_fragment.xml +++ b/app/src/main/res/layout/apptrackers_fragment.xml @@ -60,31 +60,12 @@ android:layout_weight="1" android:text="@string/apptrackers_block_all_toggle" /> - - - - - + android:layout_height="24dp" + android:checked="true" + /> - - - -- cgit v1.2.3