diff options
20 files changed, 302 insertions, 162 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index fa4a3e3..4a790c6 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -104,7 +104,7 @@ class DependencyContainer(val app: Application) { // ViewModelFactories val dashBoardViewModelFactory by lazy { - DashBoardViewModelFactory(getQuickPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase, fakeLocationStateUseCase) + DashBoardViewModelFactory(getQuickPrivacyStateUseCase, trackersStatisticsUseCase) } val fakeLocationViewModelFactory by lazy { @@ -138,10 +138,7 @@ class DependencyContainer(val app: Application) { Widget.startListening( context, getQuickPrivacyStateUseCase, - ipScramblingStateUseCase, trackersStatisticsUseCase, - trackersStateUseCase, - fakeLocationStateUseCase ) } } 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 9a7fd15..136b20f 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,6 +18,8 @@ package foundation.e.privacycentralapp.data.repositories import android.content.Context +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode +import foundation.e.privacycentralapp.domain.entities.LocationMode import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -26,6 +28,7 @@ class LocalStateRepository(context: Context) { private const val SHARED_PREFS_FILE = "localState" private const val KEY_QUICK_PRIVACY = "quickPrivacy" private const val KEY_IP_SCRAMBLING = "ipScrambling" + private const val KEY_FAKE_LOCATION = "fakeLocation" private const val KEY_FAKE_LATITUDE = "fakeLatitude" private const val KEY_FAKE_LONGITUDE = "fakeLongitude" private const val KEY_FIRST_BOOT = "firstBoot" @@ -35,43 +38,52 @@ class LocalStateRepository(context: Context) { private val quickPrivacyEnabledMutableFlow = MutableStateFlow<Boolean>(sharedPref.getBoolean(KEY_QUICK_PRIVACY, false)) - var isQuickPrivacyEnabled: Boolean - get() = quickPrivacyEnabledMutableFlow.value - set(value) { - set(KEY_QUICK_PRIVACY, value) - quickPrivacyEnabledMutableFlow.value = value - } + val isQuickPrivacyEnabled: Boolean get() = quickPrivacyEnabledMutableFlow.value + + fun setQuickPrivacyReturnIsFirstActivation(value: Boolean): Boolean { + val isFirstActivation = value && !sharedPref.contains(KEY_QUICK_PRIVACY) + set(KEY_QUICK_PRIVACY, value) + quickPrivacyEnabledMutableFlow.value = value + return isFirstActivation + } var quickPrivacyEnabledFlow: Flow<Boolean> = quickPrivacyEnabledMutableFlow + val areAllTrackersBlocked: MutableStateFlow<Boolean> = MutableStateFlow(false) + var fakeLocation: Pair<Float, Float>? - get() = if (sharedPref.contains(KEY_FAKE_LATITUDE) && sharedPref.contains( - KEY_FAKE_LONGITUDE - ) - ) + get() = if (sharedPref.getBoolean(KEY_FAKE_LOCATION, true)) Pair( - sharedPref.getFloat(KEY_FAKE_LATITUDE, 0f), - sharedPref.getFloat(KEY_FAKE_LONGITUDE, 0f) + // Initial default value is Quezon City + sharedPref.getFloat(KEY_FAKE_LATITUDE, 14.6760f), + sharedPref.getFloat(KEY_FAKE_LONGITUDE, 121.0437f) ) else null + set(value) { if (value == null) { sharedPref.edit() + .putBoolean(KEY_FAKE_LOCATION, false) .remove(KEY_FAKE_LATITUDE) .remove(KEY_FAKE_LONGITUDE) .commit() } else { sharedPref.edit() + .putBoolean(KEY_FAKE_LOCATION, true) .putFloat(KEY_FAKE_LATITUDE, value.first) .putFloat(KEY_FAKE_LONGITUDE, value.second) .commit() } } + val locationMode: MutableStateFlow<LocationMode> = MutableStateFlow(LocationMode.REAL_LOCATION) + var isIpScramblingEnabled: Boolean - get() = sharedPref.getBoolean(KEY_IP_SCRAMBLING, false) + get() = sharedPref.getBoolean(KEY_IP_SCRAMBLING, true) set(value) = set(KEY_IP_SCRAMBLING, value) + val internetPrivacyMode: MutableStateFlow<InternetPrivacyMode> = MutableStateFlow(InternetPrivacyMode.REAL_IP) + var firstBoot: Boolean get() = sharedPref.getBoolean(KEY_FIRST_BOOT, true) set(value) = set(KEY_FIRST_BOOT, value) diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/QuickPrivacyState.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/QuickPrivacyState.kt new file mode 100644 index 0000000..3257402 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/QuickPrivacyState.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.domain.entities + +enum class QuickPrivacyState { + DISABLED, ENABLED, FULL_ENABLED; + + fun isEnabled(): Boolean = this != DISABLED +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt index 32affe0..c07657a 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt @@ -33,7 +33,6 @@ import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlin.random.Random @@ -47,8 +46,8 @@ class FakeLocationStateUseCase( private val appContext: Context, private val coroutineScope: CoroutineScope ) { - private val _locationMode = MutableStateFlow(LocationMode.REAL_LOCATION) - val locationMode: StateFlow<LocationMode> = _locationMode + // private val _locationMode = MutableStateFlow(LocationMode.REAL_LOCATION) + // val locationMode: StateFlow<LocationMode> = _locationMode init { coroutineScope.launch { @@ -63,14 +62,14 @@ class FakeLocationStateUseCase( fun getLocationMode(): Triple<LocationMode, Float?, Float?> { val fakeLocation = localStateRepository.fakeLocation - return if (fakeLocation != null && _locationMode.value == LocationMode.SPECIFIC_LOCATION) { + return if (fakeLocation != null && localStateRepository.locationMode.value == LocationMode.SPECIFIC_LOCATION) { Triple( LocationMode.SPECIFIC_LOCATION, fakeLocation.first, fakeLocation.second ) } else { - Triple(_locationMode.value, null, null) + Triple(localStateRepository.locationMode.value, null, null) } } @@ -97,11 +96,11 @@ class FakeLocationStateUseCase( } fakeLocationModule.startFakeLocation() fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble()) - _locationMode.value = if (fakeLocation in citiesRepository.citiesLocationsList) LocationMode.RANDOM_LOCATION + localStateRepository.locationMode.value = if (fakeLocation in citiesRepository.citiesLocationsList) LocationMode.RANDOM_LOCATION else LocationMode.SPECIFIC_LOCATION } else { fakeLocationModule.stopFakeLocation() - _locationMode.value = LocationMode.REAL_LOCATION + localStateRepository.locationMode.value = LocationMode.REAL_LOCATION } } 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 db6f312..fd9430c 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 @@ -18,14 +18,63 @@ package foundation.e.privacycentralapp.domain.usecases import foundation.e.privacycentralapp.data.repositories.LocalStateRepository +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode +import foundation.e.privacycentralapp.domain.entities.LocationMode +import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine class GetQuickPrivacyStateUseCase(private val localStateRepository: LocalStateRepository) { val quickPrivacyEnabledFlow = localStateRepository.quickPrivacyEnabledFlow val isQuickPrivacyEnabled get() = localStateRepository.isQuickPrivacyEnabled - fun toggle(): Boolean { + val quickPrivacyState = combine( + localStateRepository.quickPrivacyEnabledFlow, + localStateRepository.areAllTrackersBlocked, + localStateRepository.locationMode, + localStateRepository.internetPrivacyMode + ) { isQuickPrivacyEnabled, isAllTrackersBlocked, locationMode, internetPrivacyMode -> + when { + !isQuickPrivacyEnabled -> QuickPrivacyState.DISABLED + isAllTrackersBlocked && + locationMode != LocationMode.REAL_LOCATION && + internetPrivacyMode in listOf( + InternetPrivacyMode.HIDE_IP, + InternetPrivacyMode.HIDE_IP_LOADING + ) -> QuickPrivacyState.FULL_ENABLED + else -> QuickPrivacyState.ENABLED + } + } + + val isTrackersDenied = combine( + localStateRepository.quickPrivacyEnabledFlow, + localStateRepository.areAllTrackersBlocked + ) { isQuickPrivacyEnabled, isAllTrackersBlocked -> + isQuickPrivacyEnabled && isAllTrackersBlocked + } + + val isLocationHidden = combine( + localStateRepository.quickPrivacyEnabledFlow, + localStateRepository.locationMode + ) { isQuickPrivacyEnabled, locationMode -> + isQuickPrivacyEnabled && locationMode != LocationMode.REAL_LOCATION + } + + val locationMode: StateFlow<LocationMode> = localStateRepository.locationMode + + val isIpHidden = combine( + localStateRepository.quickPrivacyEnabledFlow, + localStateRepository.internetPrivacyMode + ) { isQuickPrivacyEnabled, internetPrivacyMode -> + when { + !isQuickPrivacyEnabled || internetPrivacyMode == InternetPrivacyMode.REAL_IP -> false + internetPrivacyMode == InternetPrivacyMode.HIDE_IP -> true + else -> null + } + } + + fun toggleReturnIsFirstActivation(): Boolean { val newState = !localStateRepository.isQuickPrivacyEnabled - localStateRepository.isQuickPrivacyEnabled = newState - return newState + return localStateRepository.setQuickPrivacyReturnIsFirstActivation(newState) } } 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 6cc8e4a..a701eec 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 @@ -71,6 +71,10 @@ class IpScramblingStateUseCase( applySettings(it, localStateRepository.isIpScramblingEnabled) } } + + coroutineScope.launch { + internetPrivacyMode.collect { localStateRepository.internetPrivacyMode.value = it } + } } fun toggle(hideIp: Boolean) { @@ -119,7 +123,7 @@ class IpScramblingStateUseCase( private fun applySettings(isQuickPrivacyEnabled: Boolean, isIpScramblingEnabled: Boolean) { when { - isQuickPrivacyEnabled && isIpScramblingEnabled -> when (internetPrivacyMode.value) { + isQuickPrivacyEnabled && isIpScramblingEnabled -> when (localStateRepository.internetPrivacyMode.value) { InternetPrivacyMode.REAL_IP, InternetPrivacyMode.REAL_IP_LOADING -> { val intent = ipScramblerModule.prepareAndroidVpn() if (intent != null) { @@ -129,7 +133,7 @@ class IpScramblingStateUseCase( } else -> {} } - else -> when (internetPrivacyMode.value) { + else -> when (localStateRepository.internetPrivacyMode.value) { InternetPrivacyMode.HIDE_IP, InternetPrivacyMode.HIDE_IP_LOADING -> ipScramblerModule.stop() else -> {} } 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 a589509..5263559 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 @@ -26,9 +26,6 @@ import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule import foundation.e.privacymodules.trackers.Tracker import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -41,13 +38,6 @@ class TrackersStateUseCase( private val appListsRepository: AppListsRepository, private val coroutineScope: CoroutineScope ) { - - private val _areAllTrackersBlocked = MutableStateFlow( - blockTrackersPrivacyModule.isBlockingEnabled() && - blockTrackersPrivacyModule.isWhiteListEmpty() - ) - val areAllTrackersBlocked: StateFlow<Boolean> = _areAllTrackersBlocked - init { trackersPrivacyModule.start(trackersRepository.trackers, enableNotification = false) coroutineScope.launch { @@ -63,7 +53,7 @@ class TrackersStateUseCase( } private fun updateAllTrackersBlockedState() { - _areAllTrackersBlocked.value = blockTrackersPrivacyModule.isBlockingEnabled() && + localStateRepository.areAllTrackersBlocked.value = blockTrackersPrivacyModule.isBlockingEnabled() && blockTrackersPrivacyModule.isWhiteListEmpty() } 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 ca45393..95726db 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 @@ -22,14 +22,13 @@ 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.R import foundation.e.privacycentralapp.domain.entities.LocationMode -import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase +import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase -import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase -import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge @@ -49,10 +48,11 @@ class DashboardFeature( singleEventProducer ) { data class State( - val isQuickPrivacyEnabled: Boolean = false, - val isAllTrackersBlocked: Boolean = false, + val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED, + val isTrackersDenied: Boolean = false, + val isLocationHidden: Boolean = false, + val isIpHidden: Boolean? = false, val locationMode: LocationMode = LocationMode.REAL_LOCATION, - val internetPrivacyMode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP, val leakedTrackersCount: Int? = null, val trackersCount: Int? = null, val activeTrackersCount: Int? = null, @@ -66,6 +66,7 @@ class DashboardFeature( object NavigateToLocationSingleEvent : SingleEvent() object NavigateToPermissionsSingleEvent : SingleEvent() object NewStatisticsAvailableSingleEvent : SingleEvent() + data class ToastMessageSingleEvent(val message: Int) : SingleEvent() } sealed class Action { @@ -80,8 +81,8 @@ class DashboardFeature( sealed class Effect { object NoEffect : Effect() - data class UpdateStateEffect(val isEnabled: Boolean) : Effect() - data class IpScramblingModeUpdatedEffect(val mode: InternetPrivacyMode) : Effect() + data class UpdateStateEffect(val state: QuickPrivacyState) : Effect() + data class IpScramblingModeUpdatedEffect(val isIpHidden: Boolean?) : Effect() data class TrackersStatisticsUpdatedEffect( val dayStatistics: List<Pair<Int, Int>>, val dayLabels: List<String>, @@ -96,24 +97,23 @@ class DashboardFeature( object OpenAppsPermissionsEffect : Effect() object OpenTrackersEffect : Effect() object NewStatisticsAvailablesEffect : Effect() + object FirstIPTrackerActivationEffect : Effect() + data class LocationHiddenUpdatedEffect(val isLocationHidden: Boolean) : Effect() } companion object { fun create( coroutineScope: CoroutineScope, getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - ipScramblingStateUseCase: IpScramblingStateUseCase, trackersStatisticsUseCase: TrackersStatisticsUseCase, - trackersStateUseCase: TrackersStateUseCase, - fakeLocationStateUseCase: FakeLocationStateUseCase ): DashboardFeature = DashboardFeature( initialState = State(), coroutineScope, reducer = { state, effect -> when (effect) { - is Effect.UpdateStateEffect -> state.copy(isQuickPrivacyEnabled = effect.isEnabled) - is Effect.IpScramblingModeUpdatedEffect -> state.copy(internetPrivacyMode = effect.mode) + is Effect.UpdateStateEffect -> state.copy(quickPrivacyState = effect.state) + is Effect.IpScramblingModeUpdatedEffect -> state.copy(isIpHidden = effect.isIpHidden) is Effect.TrackersStatisticsUpdatedEffect -> state.copy( dayStatistics = effect.dayStatistics, dayLabels = effect.dayLabels, @@ -123,7 +123,10 @@ class DashboardFeature( ) is Effect.TrackersBlockedUpdatedEffect -> state.copy( - isAllTrackersBlocked = effect.areAllTrackersBlocked + isTrackersDenied = effect.areAllTrackersBlocked + ) + is Effect.LocationHiddenUpdatedEffect -> state.copy( + isLocationHidden = effect.isLocationHidden ) is Effect.UpdateLocationModeEffect -> state.copy(locationMode = effect.mode) @@ -133,24 +136,30 @@ class DashboardFeature( actor = { _: State, action: Action -> when (action) { Action.TogglePrivacyAction -> { - getPrivacyStateUseCase.toggle() - flowOf(Effect.NewStatisticsAvailablesEffect) + val isFirstActivation = getPrivacyStateUseCase.toggleReturnIsFirstActivation() + flow { + emit(Effect.NewStatisticsAvailablesEffect) + if (isFirstActivation) emit(Effect.FirstIPTrackerActivationEffect) + } } Action.InitAction -> merge( - getPrivacyStateUseCase.quickPrivacyEnabledFlow.map { + getPrivacyStateUseCase.quickPrivacyState.map { Effect.UpdateStateEffect(it) }, - ipScramblingStateUseCase.internetPrivacyMode.map { + getPrivacyStateUseCase.isIpHidden.map { Effect.IpScramblingModeUpdatedEffect(it) }, trackersStatisticsUseCase.listenUpdates().map { Effect.NewStatisticsAvailablesEffect }, - trackersStateUseCase.areAllTrackersBlocked.map { + getPrivacyStateUseCase.isTrackersDenied.map { Effect.TrackersBlockedUpdatedEffect(it) }, - fakeLocationStateUseCase.locationMode.map { + getPrivacyStateUseCase.isLocationHidden.map { + Effect.LocationHiddenUpdatedEffect(it) + }, + getPrivacyStateUseCase.locationMode.map { Effect.UpdateLocationModeEffect(it) } ) @@ -188,6 +197,10 @@ class DashboardFeature( SingleEvent.NavigateToTrackersSingleEvent is Effect.NewStatisticsAvailablesEffect -> SingleEvent.NewStatisticsAvailableSingleEvent + is Effect.FirstIPTrackerActivationEffect -> + SingleEvent.ToastMessageSingleEvent( + message = R.string.dashboard_first_ipscrambling_activation + ) else -> null } } 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 96ace56..588eedd 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 @@ -22,6 +22,7 @@ import android.os.Bundle import android.text.Html import android.text.Html.FROM_HTML_MODE_LEGACY import android.view.View +import android.widget.Toast import androidx.core.content.ContextCompat.getColor import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels @@ -35,8 +36,8 @@ import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.common.GraphHolder import foundation.e.privacycentralapp.common.NavToolbarFragment import foundation.e.privacycentralapp.databinding.FragmentDashboardBinding -import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode import foundation.e.privacycentralapp.domain.entities.LocationMode +import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf import foundation.e.privacycentralapp.features.dashboard.DashboardFeature.State import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyFragment @@ -104,6 +105,9 @@ class DashboardFragment : DashboardFeature.SingleEvent.NewStatisticsAvailableSingleEvent -> { viewModel.submitAction(DashboardFeature.Action.FetchStatistics) } + is DashboardFeature.SingleEvent.ToastMessageSingleEvent -> + Toast.makeText(requireContext(), event.message, Toast.LENGTH_LONG) + .show() } } } @@ -159,50 +163,48 @@ class DashboardFragment : override fun render(state: State) { binding.stateLabel.text = getString( - if (state.isQuickPrivacyEnabled) R.string.dashboard_state_title_on - else R.string.dashboard_state_title_off + when (state.quickPrivacyState) { + QuickPrivacyState.DISABLED -> R.string.dashboard_state_title_off + QuickPrivacyState.FULL_ENABLED -> R.string.dashboard_state_title_on + QuickPrivacyState.ENABLED -> R.string.dashboard_state_title_custom + } ) binding.stateIcon.setImageResource( - if (state.isQuickPrivacyEnabled) R.drawable.ic_shield_on + if (state.quickPrivacyState.isEnabled()) R.drawable.ic_shield_on else R.drawable.ic_shield_off ) - binding.togglePrivacyCentral.isChecked = state.isQuickPrivacyEnabled + binding.togglePrivacyCentral.isChecked = state.quickPrivacyState.isEnabled() - val trackersEnabled = state.isQuickPrivacyEnabled && state.isAllTrackersBlocked binding.stateTrackers.text = getString( - if (trackersEnabled) R.string.dashboard_state_trackers_on + if (state.isTrackersDenied) R.string.dashboard_state_trackers_on else R.string.dashboard_state_trackers_off ) binding.stateTrackers.setTextColor( getColor( requireContext(), - if (trackersEnabled) R.color.green_valid + if (state.isTrackersDenied) R.color.green_valid else R.color.red_off ) ) - val geolocEnabled = state.isQuickPrivacyEnabled && state.locationMode != LocationMode.REAL_LOCATION binding.stateGeolocation.text = getString( - if (geolocEnabled) R.string.dashboard_state_geolocation_on + if (state.isLocationHidden) R.string.dashboard_state_geolocation_on else R.string.dashboard_state_geolocation_off ) binding.stateGeolocation.setTextColor( getColor( requireContext(), - if (geolocEnabled) R.color.green_valid + if (state.isLocationHidden) R.color.green_valid else R.color.red_off ) ) - val ipAddressEnabled = state.isQuickPrivacyEnabled && state.internetPrivacyMode != InternetPrivacyMode.REAL_IP - val isLoading = state.isQuickPrivacyEnabled && state.internetPrivacyMode in listOf( - InternetPrivacyMode.HIDE_IP_LOADING, - InternetPrivacyMode.REAL_IP_LOADING - ) + val isLoading = state.isIpHidden == null + binding.stateIpAddress.text = getString( - if (ipAddressEnabled) R.string.dashboard_state_ipaddress_on + if (state.isIpHidden == true) R.string.dashboard_state_ipaddress_on else R.string.dashboard_state_ipaddress_off ) @@ -212,7 +214,7 @@ class DashboardFragment : binding.stateIpAddress.setTextColor( getColor( requireContext(), - if (ipAddressEnabled) R.color.green_valid + if (state.isIpHidden == true) R.color.green_valid else R.color.red_off ) ) @@ -252,10 +254,7 @@ class DashboardFragment : ) binding.internetActivityPrivacy.subTitle = getString( - if (state.isQuickPrivacyEnabled && - state.internetPrivacyMode != InternetPrivacyMode.REAL_IP - ) - R.string.dashboard_internet_activity_privacy_subtitle_on + if (state.isIpHidden == true) 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 0dbcdda..ffd7951 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 @@ -20,10 +20,7 @@ package foundation.e.privacycentralapp.features.dashboard import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.common.Factory -import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase -import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase -import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -31,10 +28,7 @@ import kotlinx.coroutines.launch class DashboardViewModel( private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - private val ipScramblingStateUseCase: IpScramblingStateUseCase, private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val trackersStateUseCase: TrackersStateUseCase, - private val fakeLocationStateUseCase: FakeLocationStateUseCase ) : ViewModel() { private val _actions = MutableSharedFlow<DashboardFeature.Action>() @@ -44,10 +38,7 @@ class DashboardViewModel( DashboardFeature.create( coroutineScope = viewModelScope, getPrivacyStateUseCase = getPrivacyStateUseCase, - ipScramblingStateUseCase = ipScramblingStateUseCase, trackersStatisticsUseCase = trackersStatisticsUseCase, - trackersStateUseCase = trackersStateUseCase, - fakeLocationStateUseCase = fakeLocationStateUseCase ) } @@ -60,12 +51,9 @@ class DashboardViewModel( class DashBoardViewModelFactory( private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - private val ipScramblingStateUseCase: IpScramblingStateUseCase, private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val trackersStateUseCase: TrackersStateUseCase, - private val fakeLocationStateUseCase: FakeLocationStateUseCase ) : Factory<DashboardViewModel> { override fun create(): DashboardViewModel { - return DashboardViewModel(getPrivacyStateUseCase, ipScramblingStateUseCase, trackersStatisticsUseCase, trackersStateUseCase, fakeLocationStateUseCase) + return DashboardViewModel(getPrivacyStateUseCase, trackersStatisticsUseCase) } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt index 1969fe5..7efb6b0 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt @@ -20,10 +20,7 @@ package foundation.e.privacycentralapp import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProvider import android.content.Context -import foundation.e.privacycentralapp.domain.usecases.FakeLocationStateUseCase import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase -import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase -import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import foundation.e.privacycentralapp.widget.State import foundation.e.privacycentralapp.widget.render @@ -72,25 +69,22 @@ class Widget : AppWidgetProvider() { private fun initState( getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - ipScramblingStateUseCase: IpScramblingStateUseCase, trackersStatisticsUseCase: TrackersStatisticsUseCase, - trackersStateUseCase: TrackersStateUseCase, - fakeLocationStateUseCase: FakeLocationStateUseCase, coroutineScope: CoroutineScope ): StateFlow<State> { return combine( - getPrivacyStateUseCase.quickPrivacyEnabledFlow, - trackersStateUseCase.areAllTrackersBlocked, - fakeLocationStateUseCase.locationMode, - ipScramblingStateUseCase.internetPrivacyMode - ) { isQuickPrivacyEnabled, isAllTrackersBlocked, locationMode, internetPrivacyMode -> + getPrivacyStateUseCase.quickPrivacyState, + getPrivacyStateUseCase.isTrackersDenied, + getPrivacyStateUseCase.isLocationHidden, + getPrivacyStateUseCase.isIpHidden, + ) { quickPrivacyState, isTrackersDenied, isLocationHidden, isIpHidden -> State( - isQuickPrivacyEnabled = isQuickPrivacyEnabled, - isAllTrackersBlocked = isAllTrackersBlocked, - locationMode = locationMode, - internetPrivacyMode = internetPrivacyMode + quickPrivacyState = quickPrivacyState, + isTrackersDenied = isTrackersDenied, + isLocationHidden = isLocationHidden, + isIpHidden = isIpHidden ) }.sample(50) .combine( @@ -112,17 +106,11 @@ class Widget : AppWidgetProvider() { fun startListening( appContext: Context, getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - ipScramblingStateUseCase: IpScramblingStateUseCase, trackersStatisticsUseCase: TrackersStatisticsUseCase, - trackersStateUseCase: TrackersStateUseCase, - fakeLocationStateUseCase: FakeLocationStateUseCase ) { state = initState( getPrivacyStateUseCase, - ipScramblingStateUseCase, trackersStatisticsUseCase, - trackersStateUseCase, - fakeLocationStateUseCase, GlobalScope ) diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt index 87e88df..a4e3079 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt @@ -27,7 +27,7 @@ class WidgetCommandReceiver : BroadcastReceiver() { when (intent?.action) { ACTION_TOGGLE_PRIVACY -> { (context?.applicationContext as? PrivacyCentralApplication) - ?.dependencyContainer?.getQuickPrivacyStateUseCase?.toggle() + ?.dependencyContainer?.getQuickPrivacyStateUseCase?.toggleReturnIsFirstActivation() } else -> {} } diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt index 2529f6c..57ddd0c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt @@ -27,24 +27,20 @@ import android.view.View import android.widget.RemoteViews import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.Widget -import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode -import foundation.e.privacycentralapp.domain.entities.LocationMode +import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState import foundation.e.privacycentralapp.extensions.dpToPxF import foundation.e.privacycentralapp.main.MainActivity import foundation.e.privacycentralapp.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_PRIVACY import kotlinx.coroutines.FlowPreview data class State( - val isQuickPrivacyEnabled: Boolean = false, - val isAllTrackersBlocked: Boolean = false, - val locationMode: LocationMode = LocationMode.REAL_LOCATION, - val internetPrivacyMode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP, + val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED, + val isTrackersDenied: Boolean = false, + val isLocationHidden: Boolean = false, + val isIpHidden: Boolean? = false, val dayStatistics: List<Pair<Int, Int>> = emptyList(), val activeTrackersCount: Int = 0, -) { - val isTrackersDenied get() = isQuickPrivacyEnabled && isAllTrackersBlocked - val isLocationHidden get() = isQuickPrivacyEnabled && locationMode != LocationMode.REAL_LOCATION -} +) @FlowPreview fun render( @@ -62,18 +58,22 @@ fun render( setImageViewResource( R.id.state_icon, - if (state.isQuickPrivacyEnabled) R.drawable.ic_shield_on else R.drawable.ic_shield_off + if (state.quickPrivacyState.isEnabled()) R.drawable.ic_shield_on_white + else R.drawable.ic_shield_off_white ) setTextViewText( R.id.state_label, context.getString( - if (state.isQuickPrivacyEnabled) R.string.widget_state_title_on - else R.string.widget_state_title_off + when (state.quickPrivacyState) { + QuickPrivacyState.DISABLED -> R.string.widget_state_title_off + QuickPrivacyState.FULL_ENABLED -> R.string.widget_state_title_on + QuickPrivacyState.ENABLED -> R.string.widget_state_title_custom + } ) ) setImageViewResource( R.id.toggle_privacy_central, - if (state.isQuickPrivacyEnabled) R.drawable.ic_switch_enabled + if (state.quickPrivacyState.isEnabled()) R.drawable.ic_switch_enabled else R.drawable.ic_switch_disabled ) @@ -108,16 +108,12 @@ fun render( setTextViewText( R.id.state_ip_address, context.getString( - if (state.internetPrivacyMode != InternetPrivacyMode.HIDE_IP) - R.string.widget_state_ipaddress_off - else R.string.widget_state_ipaddress_on + if (state.isIpHidden == true) R.string.widget_state_ipaddress_on + else R.string.widget_state_ipaddress_off ) ) - val loading = state.internetPrivacyMode in listOf( - InternetPrivacyMode.HIDE_IP_LOADING, - InternetPrivacyMode.REAL_IP_LOADING - ) + val loading = state.isIpHidden == null setViewVisibility(R.id.state_ip_address, if (loading) View.GONE else View.VISIBLE) diff --git a/app/src/main/res/drawable-night/ic_shield_off.xml b/app/src/main/res/drawable-night/ic_shield_off.xml new file mode 100644 index 0000000..f45fd98 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_shield_off.xml @@ -0,0 +1,19 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="25dp" + android:height="24dp" + android:viewportWidth="25" + android:viewportHeight="24"> + <group> + <clip-path + android:pathData="M12.0183,1L3.0183,5V11C3.0183,16.55 6.8583,21.74 12.0183,23C17.1783,21.74 21.0183,16.55 21.0183,11V5L12.0183,1Z"/> + <path + android:pathData="M12.0183,1L12.8306,-0.8276L12.0183,-1.1886L11.206,-0.8276L12.0183,1ZM3.0183,5L2.206,3.1724L1.0183,3.7002V5H3.0183ZM12.0183,23L11.5439,24.9429L12.0183,25.0588L12.4927,24.9429L12.0183,23ZM21.0183,5H23.0183V3.7002L21.8306,3.1724L21.0183,5ZM11.206,-0.8276L2.206,3.1724L3.8306,6.8276L12.8306,2.8276L11.206,-0.8276ZM1.0183,5V11H5.0183V5H1.0183ZM1.0183,11C1.0183,17.3888 5.4102,23.4452 11.5439,24.9429L12.4927,21.0571C8.3064,20.0348 5.0183,15.7112 5.0183,11H1.0183ZM12.4927,24.9429C18.6264,23.4452 23.0183,17.3888 23.0183,11H19.0183C19.0183,15.7112 15.7302,20.0348 11.5439,21.0571L12.4927,24.9429ZM23.0183,11V5H19.0183V11H23.0183ZM21.8306,3.1724L12.8306,-0.8276L11.206,2.8276L20.206,6.8276L21.8306,3.1724Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87"/> + </group> + <path + android:pathData="M15.1396,10.1213L15.8467,9.4142L14.4325,8L13.7254,8.7071L11.9325,10.5L10.1396,8.7071L9.4325,8L8.0183,9.4142L8.7254,10.1213L10.5183,11.9142L8.7254,13.7071L8.0183,14.4142L9.4325,15.8284L10.1396,15.1213L11.9325,13.3284L13.7254,15.1213L14.4325,15.8284L15.8467,14.4142L15.1396,13.7071L13.3467,11.9142L15.1396,10.1213Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87" + android:fillType="evenOdd"/> +</vector> diff --git a/app/src/main/res/drawable-night/ic_shield_on.xml b/app/src/main/res/drawable-night/ic_shield_on.xml new file mode 100644 index 0000000..ecc27b4 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_shield_on.xml @@ -0,0 +1,18 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="25dp" + android:viewportWidth="24" + android:viewportHeight="25"> + <group> + <clip-path + android:pathData="M12,1.5796L3,5.5796V11.5796C3,17.1296 6.84,22.3196 12,23.5796C17.16,22.3196 21,17.1296 21,11.5796V5.5796L12,1.5796Z"/> + <path + android:pathData="M12,1.5796L12.8123,-0.248L12,-0.609L11.1877,-0.248L12,1.5796ZM3,5.5796L2.1877,3.752L1,4.2798V5.5796H3ZM12,23.5796L11.5256,25.5225L12,25.6384L12.4744,25.5225L12,23.5796ZM21,5.5796H23V4.2798L21.8123,3.752L21,5.5796ZM11.1877,-0.248L2.1877,3.752L3.8123,7.4072L12.8123,3.4072L11.1877,-0.248ZM1,5.5796V11.5796H5V5.5796H1ZM1,11.5796C1,17.9684 5.3919,24.0247 11.5256,25.5225L12.4744,21.6367C8.2881,20.6144 5,16.2907 5,11.5796H1ZM12.4744,25.5225C18.6081,24.0247 23,17.9684 23,11.5796H19C19,16.2907 15.7119,20.6144 11.5256,21.6367L12.4744,25.5225ZM23,11.5796V5.5796H19V11.5796H23ZM21.8123,3.752L12.8123,-0.248L11.1877,3.4072L20.1877,7.4072L21.8123,3.752Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87"/> + </group> + <path + android:pathData="M10.6951,14.5796L10.0141,15.3118L10.6948,15.9449L11.3758,15.3122L10.6951,14.5796ZM9.681,12.2708L8.9488,11.5897L7.5867,13.0542L8.319,13.7353L9.681,12.2708ZM15.6807,11.3122L16.4133,10.6315L15.0519,9.1663L14.3193,9.847L15.6807,11.3122ZM11.3762,13.8473L9.681,12.2708L8.319,13.7353L10.0141,15.3118L11.3762,13.8473ZM14.3193,9.847L10.0144,13.847L11.3758,15.3122L15.6807,11.3122L14.3193,9.847Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87"/> +</vector> diff --git a/app/src/main/res/drawable/ic_shield_off.xml b/app/src/main/res/drawable/ic_shield_off.xml index 8f811a2..cd60ba4 100644 --- a/app/src/main/res/drawable/ic_shield_off.xml +++ b/app/src/main/res/drawable/ic_shield_off.xml @@ -1,16 +1,19 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" + android:width="25dp" android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="@color/red_off"> + android:viewportWidth="25" + android:viewportHeight="24"> + <group> + <clip-path + android:pathData="M12.5,1L3.5,5V11C3.5,16.55 7.34,21.74 12.5,23C17.66,21.74 21.5,16.55 21.5,11V5L12.5,1Z"/> <path - android:pathData="M4,11V5.6499L12,2.0943L20,5.6499V11C20,16.0434 16.556,20.7257 12,21.9673C7.444,20.7257 4,16.0434 4,11Z" - android:strokeWidth="2" - android:fillColor="#00000000" - android:strokeColor="#000000"/> - <path - android:pathData="M15.1213,10.1213L15.8284,9.4142L14.4142,8L13.7071,8.7071L11.9142,10.5L10.1213,8.7071L9.4142,8L8,9.4142L8.7071,10.1213L10.5,11.9142L8.7071,13.7071L8,14.4142L9.4142,15.8284L10.1213,15.1213L11.9142,13.3284L13.7071,15.1213L14.4142,15.8284L15.8284,14.4142L15.1213,13.7071L13.3284,11.9142L15.1213,10.1213Z" + android:pathData="M12.5,1L13.3123,-0.8276L12.5,-1.1886L11.6877,-0.8276L12.5,1ZM3.5,5L2.6877,3.1724L1.5,3.7002V5H3.5ZM12.5,23L12.0256,24.9429L12.5,25.0588L12.9744,24.9429L12.5,23ZM21.5,5H23.5V3.7002L22.3123,3.1724L21.5,5ZM11.6877,-0.8276L2.6877,3.1724L4.3123,6.8276L13.3123,2.8276L11.6877,-0.8276ZM1.5,5V11H5.5V5H1.5ZM1.5,11C1.5,17.3888 5.8919,23.4452 12.0256,24.9429L12.9744,21.0571C8.7881,20.0348 5.5,15.7112 5.5,11H1.5ZM12.9744,24.9429C19.1081,23.4452 23.5,17.3888 23.5,11H19.5C19.5,15.7112 16.2119,20.0348 12.0256,21.0571L12.9744,24.9429ZM23.5,11V5H19.5V11H23.5ZM22.3123,3.1724L13.3123,-0.8276L11.6877,2.8276L20.6877,6.8276L22.3123,3.1724Z" android:fillColor="#000000" - android:fillType="evenOdd"/> + android:fillAlpha="0.87"/> + </group> + <path + android:pathData="M15.6213,10.1213L16.3284,9.4142L14.9142,8L14.2071,8.7071L12.4142,10.5L10.6213,8.7071L9.9142,8L8.5,9.4142L9.2071,10.1213L11,11.9142L9.2071,13.7071L8.5,14.4142L9.9142,15.8284L10.6213,15.1213L12.4142,13.3284L14.2071,15.1213L14.9142,15.8284L16.3284,14.4142L15.6213,13.7071L13.8284,11.9142L15.6213,10.1213Z" + android:fillColor="#000000" + android:fillAlpha="0.87" + android:fillType="evenOdd"/> </vector> diff --git a/app/src/main/res/drawable/ic_shield_off_white.xml b/app/src/main/res/drawable/ic_shield_off_white.xml new file mode 100644 index 0000000..f45fd98 --- /dev/null +++ b/app/src/main/res/drawable/ic_shield_off_white.xml @@ -0,0 +1,19 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="25dp" + android:height="24dp" + android:viewportWidth="25" + android:viewportHeight="24"> + <group> + <clip-path + android:pathData="M12.0183,1L3.0183,5V11C3.0183,16.55 6.8583,21.74 12.0183,23C17.1783,21.74 21.0183,16.55 21.0183,11V5L12.0183,1Z"/> + <path + android:pathData="M12.0183,1L12.8306,-0.8276L12.0183,-1.1886L11.206,-0.8276L12.0183,1ZM3.0183,5L2.206,3.1724L1.0183,3.7002V5H3.0183ZM12.0183,23L11.5439,24.9429L12.0183,25.0588L12.4927,24.9429L12.0183,23ZM21.0183,5H23.0183V3.7002L21.8306,3.1724L21.0183,5ZM11.206,-0.8276L2.206,3.1724L3.8306,6.8276L12.8306,2.8276L11.206,-0.8276ZM1.0183,5V11H5.0183V5H1.0183ZM1.0183,11C1.0183,17.3888 5.4102,23.4452 11.5439,24.9429L12.4927,21.0571C8.3064,20.0348 5.0183,15.7112 5.0183,11H1.0183ZM12.4927,24.9429C18.6264,23.4452 23.0183,17.3888 23.0183,11H19.0183C19.0183,15.7112 15.7302,20.0348 11.5439,21.0571L12.4927,24.9429ZM23.0183,11V5H19.0183V11H23.0183ZM21.8306,3.1724L12.8306,-0.8276L11.206,2.8276L20.206,6.8276L21.8306,3.1724Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87"/> + </group> + <path + android:pathData="M15.1396,10.1213L15.8467,9.4142L14.4325,8L13.7254,8.7071L11.9325,10.5L10.1396,8.7071L9.4325,8L8.0183,9.4142L8.7254,10.1213L10.5183,11.9142L8.7254,13.7071L8.0183,14.4142L9.4325,15.8284L10.1396,15.1213L11.9325,13.3284L13.7254,15.1213L14.4325,15.8284L15.8467,14.4142L15.1396,13.7071L13.3467,11.9142L15.1396,10.1213Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87" + android:fillType="evenOdd"/> +</vector> diff --git a/app/src/main/res/drawable/ic_shield_on.xml b/app/src/main/res/drawable/ic_shield_on.xml index b70dc6e..e29f766 100644 --- a/app/src/main/res/drawable/ic_shield_on.xml +++ b/app/src/main/res/drawable/ic_shield_on.xml @@ -1,17 +1,18 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" + android:width="25dp" android:height="25dp" - android:viewportWidth="24" + android:viewportWidth="25" android:viewportHeight="25"> + <group> + <clip-path + android:pathData="M12.5,1.5L3.5,5.5V11.5C3.5,17.05 7.34,22.24 12.5,23.5C17.66,22.24 21.5,17.05 21.5,11.5V5.5L12.5,1.5Z"/> <path - android:pathData="M4,11.5V6.1499L12,2.5943L20,6.1499V11.5C20,16.5434 16.556,21.2257 12,22.4673C7.444,21.2257 4,16.5434 4,11.5Z" - android:strokeWidth="2" - android:fillColor="#00000000" - android:strokeColor="#2CC766"/> - <path - android:pathData="M9,12.9234L10.6951,14.5L15,10.5" - android:strokeWidth="2" - android:fillColor="#00000000" - android:strokeColor="#2CC766" - android:strokeLineCap="square"/> + android:pathData="M12.5,1.5L13.3123,-0.3276L12.5,-0.6886L11.6877,-0.3276L12.5,1.5ZM3.5,5.5L2.6877,3.6724L1.5,4.2003V5.5H3.5ZM12.5,23.5L12.0256,25.4429L12.5,25.5588L12.9744,25.4429L12.5,23.5ZM21.5,5.5H23.5V4.2003L22.3123,3.6724L21.5,5.5ZM11.6877,-0.3276L2.6877,3.6724L4.3123,7.3276L13.3123,3.3276L11.6877,-0.3276ZM1.5,5.5V11.5H5.5V5.5H1.5ZM1.5,11.5C1.5,17.8888 5.8919,23.9452 12.0256,25.4429L12.9744,21.5571C8.7881,20.5348 5.5,16.2112 5.5,11.5H1.5ZM12.9744,25.4429C19.1081,23.9452 23.5,17.8888 23.5,11.5H19.5C19.5,16.2112 16.2119,20.5348 12.0256,21.5571L12.9744,25.4429ZM23.5,11.5V5.5H19.5V11.5H23.5ZM22.3123,3.6724L13.3123,-0.3276L11.6877,3.3276L20.6877,7.3276L22.3123,3.6724Z" + android:fillColor="#000000" + android:fillAlpha="0.87"/> + </group> + <path + android:pathData="M11.1951,14.5L10.5141,15.2323L11.1948,15.8654L11.8758,15.2326L11.1951,14.5ZM10.181,12.1912L9.4488,11.5102L8.0867,12.9747L8.819,13.6557L10.181,12.1912ZM16.1807,11.2326L16.9133,10.5519L15.5519,9.0867L14.8193,9.7674L16.1807,11.2326ZM11.8762,13.7677L10.181,12.1912L8.819,13.6557L10.5141,15.2323L11.8762,13.7677ZM14.8193,9.7674L10.5144,13.7674L11.8758,15.2326L16.1807,11.2326L14.8193,9.7674Z" + android:fillColor="#000000" + android:fillAlpha="0.87"/> </vector> diff --git a/app/src/main/res/drawable/ic_shield_on_white.xml b/app/src/main/res/drawable/ic_shield_on_white.xml new file mode 100644 index 0000000..ecc27b4 --- /dev/null +++ b/app/src/main/res/drawable/ic_shield_on_white.xml @@ -0,0 +1,18 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="25dp" + android:viewportWidth="24" + android:viewportHeight="25"> + <group> + <clip-path + android:pathData="M12,1.5796L3,5.5796V11.5796C3,17.1296 6.84,22.3196 12,23.5796C17.16,22.3196 21,17.1296 21,11.5796V5.5796L12,1.5796Z"/> + <path + android:pathData="M12,1.5796L12.8123,-0.248L12,-0.609L11.1877,-0.248L12,1.5796ZM3,5.5796L2.1877,3.752L1,4.2798V5.5796H3ZM12,23.5796L11.5256,25.5225L12,25.6384L12.4744,25.5225L12,23.5796ZM21,5.5796H23V4.2798L21.8123,3.752L21,5.5796ZM11.1877,-0.248L2.1877,3.752L3.8123,7.4072L12.8123,3.4072L11.1877,-0.248ZM1,5.5796V11.5796H5V5.5796H1ZM1,11.5796C1,17.9684 5.3919,24.0247 11.5256,25.5225L12.4744,21.6367C8.2881,20.6144 5,16.2907 5,11.5796H1ZM12.4744,25.5225C18.6081,24.0247 23,17.9684 23,11.5796H19C19,16.2907 15.7119,20.6144 11.5256,21.6367L12.4744,25.5225ZM23,11.5796V5.5796H19V11.5796H23ZM21.8123,3.752L12.8123,-0.248L11.1877,3.4072L20.1877,7.4072L21.8123,3.752Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87"/> + </group> + <path + android:pathData="M10.6951,14.5796L10.0141,15.3118L10.6948,15.9449L11.3758,15.3122L10.6951,14.5796ZM9.681,12.2708L8.9488,11.5897L7.5867,13.0542L8.319,13.7353L9.681,12.2708ZM15.6807,11.3122L16.4133,10.6315L15.0519,9.1663L14.3193,9.847L15.6807,11.3122ZM11.3762,13.8473L9.681,12.2708L8.319,13.7353L10.0141,15.3118L11.3762,13.8473ZM14.3193,9.847L10.0144,13.847L11.3758,15.3122L15.6807,11.3122L14.3193,9.847Z" + android:fillColor="#ffffff" + android:fillAlpha="0.87"/> +</vector> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cf293e1..1fc7841 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ <string name="dashboard_title">@string/app_name</string> <string name="dashboard_state_title_on">Your online privacy is protected</string> <string name="dashboard_state_title_off">Your online privacy is unprotected</string> + <string name="dashboard_state_title_custom">Custom privacy settings applied</string> <string name="dashboard_state_trackers_label">Trackers:</string> <string name="dashboard_state_trackers_off">Vulnerable</string> <string name="dashboard_state_trackers_on">Denied</string> @@ -25,6 +26,7 @@ <string name="dashboard_graph_label">Personal data leakage:</string> <string name="dashboard_graph_period">Today</string> <string name="dashboard_graph_trackers_legend">%s trackers have profiled you in the last 24 hours</string> + <string name="dashboard_first_ipscrambling_activation">While your IP is faked, your internet speed is likely to be reduced.</string> <string name="dashboard_am_i_tracked_title">Manage apps\' trackers</string> <string name="dashboard_am_i_tracked_subtitle">%1$d app trackers, %2$d active trackers</string> @@ -126,6 +128,7 @@ <string name="widget_title">@string/app_name</string> <string name="widget_state_title_on">Your online privacy is protected</string> <string name="widget_state_title_off">Your online privacy is unprotected</string> + <string name="widget_state_title_custom">Custom privacy settings applied</string> <string name="widget_state_trackers_label">@string/dashboard_state_trackers_label</string> <string name="widget_state_trackers_off">@string/dashboard_state_trackers_off</string> <string name="widget_state_trackers_on">@string/dashboard_state_trackers_on</string> |