From 9035bac3ff801bb982bf54b02c0e9850d6afbc22 Mon Sep 17 00:00:00 2001 From: jacquarg Date: Thu, 28 Oct 2021 22:35:19 +0200 Subject: Update dashboard UI and feature --- .../e/privacycentralapp/DependencyContainer.kt | 16 + .../data/repositories/LocalStateRepository.kt | 7 +- .../domain/entities/InternetPrivacyMode.kt | 22 + .../domain/entities/LocationMode.kt | 22 + .../domain/usecases/GetQuickPrivacyStateUseCase.kt | 8 +- .../e/privacycentralapp/dummy/CityDataSource.kt | 1 + .../e/privacycentralapp/dummy/DummyDataSource.kt | 10 +- .../e/privacycentralapp/dummy/Extensions.kt | 2 + .../features/dashboard/DashboardFeature.kt | 186 ++++---- .../features/dashboard/DashboardFragment.kt | 230 ++++++---- .../features/dashboard/DashboardViewModel.kt | 18 +- .../features/dashboard/QuickProtectionFragment.kt | 2 +- .../internetprivacy/InternetPrivacyFeature.kt | 13 - .../internetprivacy/InternetPrivacyFragment.kt | 4 +- .../features/location/FakeLocationFeature.kt | 2 +- .../features/location/FakeLocationFragment.kt | 4 +- app/src/main/res/drawable/ic_apps_permissions.xml | 36 +- app/src/main/res/drawable/ic_internet_activity.xml | 48 ++- app/src/main/res/drawable/ic_my_location.xml | 36 +- app/src/main/res/drawable/ic_privacy_toggle.xml | 22 - app/src/main/res/drawable/ic_quick_privacy_off.png | Bin 0 -> 99093 bytes app/src/main/res/drawable/ic_quick_privacy_on.png | Bin 0 -> 90230 bytes app/src/main/res/drawable/ic_tracked.xml | 63 ++- .../res/layout/dashboard_item_submenu_button.xml | 83 ++++ app/src/main/res/layout/fragment_dashboard.xml | 472 ++++++++------------- .../layout/fragment_internet_activity_policy.xml | 2 +- app/src/main/res/values/colors.xml | 12 + app/src/main/res/values/strings.xml | 42 +- app/src/main/res/values/themes.xml | 4 + 29 files changed, 759 insertions(+), 608 deletions(-) create mode 100644 app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt create mode 100644 app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt delete mode 100644 app/src/main/res/drawable/ic_privacy_toggle.xml create mode 100644 app/src/main/res/drawable/ic_quick_privacy_off.png create mode 100644 app/src/main/res/drawable/ic_quick_privacy_on.png create mode 100644 app/src/main/res/layout/dashboard_item_submenu_button.xml (limited to 'app/src') diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 1ab848c..17967db 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -20,6 +20,9 @@ package foundation.e.privacycentralapp import android.app.Application import android.content.Context import android.os.Process +import foundation.e.privacycentralapp.data.repositories.LocalStateRepository +import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase +import foundation.e.privacycentralapp.features.dashboard.DashBoardViewModelFactory import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyViewModelFactory import foundation.e.privacycentralapp.features.location.FakeLocationViewModelFactory import foundation.e.privacycentralapp.features.location.LocationApiDelegate @@ -40,6 +43,7 @@ class DependencyContainer constructor(val app: Application) { val context: Context by lazy { app.applicationContext } + // Drivers private val fakeLocationModule: IFakeLocation by lazy { FakeLocation(app.applicationContext) } private val permissionsModule by lazy { PermissionsPrivacyModule(app.applicationContext) } private val ipScramblerModule: IIpScramblerModule by lazy { IpScramblerModule(app.applicationContext) } @@ -57,6 +61,18 @@ class DependencyContainer constructor(val app: Application) { LocationApiDelegate(fakeLocationModule, permissionsModule, appDesc) } + // Repositories + private val localStateRepository by lazy { LocalStateRepository(context) } + + // Usecases + private val getQuickPrivacyStateUseCase by lazy { + GetQuickPrivacyStateUseCase(localStateRepository) + } + + val dashBoardViewModelFactory by lazy { + DashBoardViewModelFactory(getQuickPrivacyStateUseCase) + } + val fakeLocationViewModelFactory by lazy { FakeLocationViewModelFactory(locationApi) } 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 390044b..3cabae7 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 @@ -31,10 +31,7 @@ class LocalStateRepository(context: Context) { get() = sharedPref.getBoolean(KEY_QUICK_PRIVACY, false) set(value) = set(KEY_QUICK_PRIVACY, value) - private fun set(key: String, value: Boolean) { - sharedPref.edit().putBoolean(key, value).apply() + sharedPref.edit().putBoolean(key, value).commit() } - - -} \ No newline at end of file +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt new file mode 100644 index 0000000..879c435 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/InternetPrivacyMode.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacycentralapp.domain.entities + +enum class InternetPrivacyMode { + REAL_IP, HIDE_IP +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt new file mode 100644 index 0000000..dbb9b0a --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/LocationMode.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 E FOUNDATION + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacycentralapp.domain.entities + +enum class LocationMode { + REAL_LOCATION, RANDOM_LOCATION, CUSTOM_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 746ead4..20ac0d9 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 @@ -19,6 +19,12 @@ package foundation.e.privacycentralapp.domain.usecases import foundation.e.privacycentralapp.data.repositories.LocalStateRepository -class GetQuickPrivacyStateUseCase(localStateRepository: LocalStateRepository) { +class GetQuickPrivacyStateUseCase(private val localStateRepository: LocalStateRepository) { val isQuickPrivacyEnabled = localStateRepository.isQuickPrivacyEnabled + + fun toggle(): Boolean { + val newState = !localStateRepository.isQuickPrivacyEnabled + localStateRepository.isQuickPrivacyEnabled = newState + return newState + } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt index 3bb2f12..988c7f4 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/CityDataSource.kt @@ -17,6 +17,7 @@ package foundation.e.privacycentralapp.dummy +import foundation.e.privacycentralapp.domain.entities.LocationMode import kotlin.random.Random data class City(val name: String, val latitude: Double, val longitude: Double) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt index fe61354..246854b 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt @@ -18,6 +18,8 @@ package foundation.e.privacycentralapp.dummy import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode +import foundation.e.privacycentralapp.domain.entities.LocationMode import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlin.random.Random @@ -52,14 +54,6 @@ data class Permission( val packagesAllowed: Set = emptySet() ) -enum class LocationMode { - REAL_LOCATION, RANDOM_LOCATION, CUSTOM_LOCATION -} - -enum class InternetPrivacyMode { - REAL_IP, HIDE_IP -} - data class Location(val mode: LocationMode, val latitude: Double, val longitude: Double) object DummyDataSource { diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt index 133ad84..91a52ca 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/Extensions.kt @@ -19,6 +19,8 @@ package foundation.e.privacycentralapp.dummy import android.content.Context import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode +import foundation.e.privacycentralapp.domain.entities.LocationMode fun LocationMode.mapToString(context: Context): String = when (this) { LocationMode.REAL_LOCATION -> context.getString(R.string.real_location_mode) 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 c26fce1..a461b65 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,15 +22,11 @@ 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.dummy.DummyDataSource -import foundation.e.privacycentralapp.dummy.InternetPrivacyMode -import foundation.e.privacycentralapp.dummy.LocationMode -import foundation.e.privacycentralapp.dummy.TrackersDataSource +import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode +import foundation.e.privacycentralapp.domain.entities.LocationMode +import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge // Define a state machine for Dashboard Feature class DashboardFeature( @@ -46,20 +42,23 @@ class DashboardFeature( initialState, actor, reducer, coroutineScope, { message -> Log.d("DashboardFeature", message) }, singleEventProducer ) { - sealed class State { - object InitialState : State() - object LoadingDashboardState : State() - data class DashboardState( - val trackersCount: Int, - val activeTrackersCount: Int, - val totalApps: Int, - val permissionCount: Int, - val appsUsingLocationPerm: Int, - val locationMode: LocationMode, - val internetPrivacyMode: InternetPrivacyMode + sealed class State() { + object LoadingState : State() + data class DisabledState( + val totalGraph: Int = 230, + // val graphData + val trackersCount: Int = 77, + val activeTrackersCount: Int = 22 + ) : State() + data class EnabledState( + val isAllTrackersBlocked: Boolean = false, + val locationMode: LocationMode = LocationMode.CUSTOM_LOCATION, + val internetPrivacyMode: InternetPrivacyMode, + val totalGraph: Int = 150, + // val graphData + val trackersCount: Int = 80, + val activeTrackersCount: Int = 10 ) : State() - - object QuickProtectionState : State() } sealed class SingleEvent { @@ -71,9 +70,10 @@ class DashboardFeature( } sealed class Action { - object ShowQuickPrivacyProtectionInfoAction : Action() - object ObserveDashboardAction : Action() - object ShowDashboardAction : Action() + object TogglePrivacyAction : Action() + // object ShowQuickPrivacyProtectionInfoAction : Action() + // object ObserveDashboardAction : Action() + // object ShowDashboardAction : Action() object ShowFakeMyLocationAction : Action() object ShowInternetActivityPrivacyAction : Action() object ShowAppsPermissions : Action() @@ -81,6 +81,8 @@ class DashboardFeature( } sealed class Effect { + data class UpdateStateEffect(val isEnabled: Boolean) : Effect() + object OpenQuickPrivacyProtectionEffect : Effect() data class OpenDashboardEffect( val trackersCount: Int, @@ -98,6 +100,7 @@ class DashboardFeature( data class UpdateLocationModeEffect(val mode: LocationMode) : Effect() data class UpdateInternetActivityModeEffect(val mode: InternetPrivacyMode) : Effect() data class UpdateAppsUsingLocationPermEffect(val apps: Int) : Effect() + object OpenFakeMyLocationEffect : Effect() object OpenInternetActivityPrivacyEffect : Effect() object OpenAppsPermissionsEffect : Effect() @@ -105,14 +108,38 @@ class DashboardFeature( } companion object { - fun create(initialState: State, coroutineScope: CoroutineScope): DashboardFeature = + fun create( + coroutineScope: CoroutineScope, + getPrivacyStateUseCase: GetQuickPrivacyStateUseCase + ): DashboardFeature = DashboardFeature( - initialState, + initialState = State.DisabledState(), coroutineScope, reducer = { state, effect -> - when (effect) { - Effect.OpenQuickPrivacyProtectionEffect -> State.QuickProtectionState - is Effect.OpenDashboardEffect -> State.DashboardState( + if (state is State.LoadingState) state + else when (effect) { + is Effect.UpdateStateEffect -> when { + effect.isEnabled && state is State.EnabledState + || !effect.isEnabled && state is State.DisabledState -> state + effect.isEnabled && state is State.DisabledState -> State.EnabledState( + isAllTrackersBlocked = false, + locationMode = LocationMode.REAL_LOCATION, + internetPrivacyMode = InternetPrivacyMode.REAL_IP, + totalGraph = state.totalGraph, + // val graphData + trackersCount = state.trackersCount, + activeTrackersCount = state.activeTrackersCount + ) + !effect.isEnabled && state is State.EnabledState -> State.DisabledState( + totalGraph = state.totalGraph, + // val graphData + trackersCount = state.trackersCount, + activeTrackersCount = state.activeTrackersCount + ) + else -> state + } + + /*is Effect.OpenDashboardEffect -> State.DashboardState( effect.trackersCount, effect.activeTrackersCount, effect.totalApps, @@ -120,46 +147,57 @@ class DashboardFeature( effect.appsUsingLocationPerm, effect.locationMode, effect.internetPrivacyMode - ) - Effect.LoadingDashboardEffect -> { - if (state is State.InitialState) { - State.LoadingDashboardState - } else state - } - is Effect.UpdateActiveTrackersCountEffect -> { - if (state is State.DashboardState) { - state.copy(activeTrackersCount = effect.count) - } else state - } - is Effect.UpdateTotalTrackersCountEffect -> { - if (state is State.DashboardState) { - state.copy(trackersCount = effect.count) - } else state - } - is Effect.UpdateInternetActivityModeEffect -> { - if (state is State.DashboardState) { - state.copy(internetPrivacyMode = effect.mode) - } else state - } - is Effect.UpdateLocationModeEffect -> { - if (state is State.DashboardState) { - state.copy(locationMode = effect.mode) + ) + Effect.LoadingDashboardEffect -> { + if (state is State.InitialState) { + State.LoadingDashboardState + } else state + } + is Effect.UpdateActiveTrackersCountEffect -> { + if (state is State.DashboardState) { + state.copy(activeTrackersCount = effect.count) + } else state + } + is Effect.UpdateTotalTrackersCountEffect -> { + if (state is State.DashboardState) { + state.copy(trackersCount = effect.count) + } else state + } + is Effect.UpdateInternetActivityModeEffect -> { + if (state is State.DashboardState) { + state.copy(internetPrivacyMode = effect.mode) + } else state + } + is Effect.UpdateLocationModeEffect -> { + if (state is State.DashboardState) { + state.copy(locationMode = effect.mode) + } else state + } + is Effect.UpdateAppsUsingLocationPermEffect -> if (state is State.DashboardState) { + state.copy(appsUsingLocationPerm = effect.apps) } else state - } - is Effect.UpdateAppsUsingLocationPermEffect -> if (state is State.DashboardState) { - state.copy(appsUsingLocationPerm = effect.apps) - } else state - Effect.OpenFakeMyLocationEffect -> state - Effect.OpenAppsPermissionsEffect -> state - Effect.OpenInternetActivityPrivacyEffect -> state - Effect.OpenTrackersEffect -> state + Effect.OpenFakeMyLocationEffect -> state + Effect.OpenAppsPermissionsEffect -> state + Effect.OpenInternetActivityPrivacyEffect -> state + Effect.OpenTrackersEffect -> state + */ + + else -> state } }, - actor = { _: State, action: Action -> + actor = { state: State, action: Action -> Log.d("Feature", "action: $action") when (action) { - Action.ObserveDashboardAction -> merge( + Action.TogglePrivacyAction -> { + if (state != State.LoadingState) { + flowOf(Effect.UpdateStateEffect(getPrivacyStateUseCase.toggle())) + } else { + flowOf(Effect.UpdateStateEffect(getPrivacyStateUseCase.isQuickPrivacyEnabled)) + } + } + + /*Action.ObserveDashboardAction -> merge( TrackersDataSource.trackers.map { var activeTrackersCount: Int = 0 outer@ for (tracker in it) { @@ -201,7 +239,7 @@ class DashboardFeature( DummyDataSource.internetActivityMode.value ) ) - } + }*/ Action.ShowFakeMyLocationAction -> flowOf(Effect.OpenFakeMyLocationEffect) Action.ShowAppsPermissions -> flowOf(Effect.OpenAppsPermissionsEffect) Action.ShowInternetActivityPrivacyAction -> flowOf( @@ -212,17 +250,17 @@ class DashboardFeature( }, singleEventProducer = { state, _, effect -> Log.d("DashboardFeature", "$state, $effect") - if (state is State.DashboardState && effect is Effect.OpenFakeMyLocationEffect) - SingleEvent.NavigateToLocationSingleEvent - else if (state is State.QuickProtectionState && effect is Effect.OpenQuickPrivacyProtectionEffect) - SingleEvent.NavigateToQuickProtectionSingleEvent - else if (state is State.DashboardState && effect is Effect.OpenInternetActivityPrivacyEffect) - SingleEvent.NavigateToInternetActivityPrivacySingleEvent - else if (state is State.DashboardState && effect is Effect.OpenAppsPermissionsEffect) - SingleEvent.NavigateToPermissionsSingleEvent - else if (state is State.DashboardState && effect is Effect.OpenTrackersEffect) - SingleEvent.NavigateToTrackersSingleEvent - else null + when (effect) { + is Effect.OpenFakeMyLocationEffect -> + SingleEvent.NavigateToLocationSingleEvent + is Effect.OpenInternetActivityPrivacyEffect -> + SingleEvent.NavigateToInternetActivityPrivacySingleEvent + is Effect.OpenAppsPermissionsEffect -> + SingleEvent.NavigateToPermissionsSingleEvent + is Effect.OpenTrackersEffect -> + SingleEvent.NavigateToTrackersSingleEvent + 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 e7ce353..3e47a18 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 @@ -17,36 +17,50 @@ package foundation.e.privacycentralapp.features.dashboard +import android.content.Intent import android.graphics.Color import android.os.Bundle import android.text.Spannable import android.text.SpannableString import android.text.style.ForegroundColorSpan import android.view.View -import android.widget.ProgressBar -import android.widget.RelativeLayout import android.widget.TextView -import androidx.core.widget.NestedScrollView +import androidx.core.content.ContextCompat.getColor import androidx.fragment.app.activityViewModels import androidx.fragment.app.add import androidx.fragment.app.commit import androidx.lifecycle.lifecycleScope 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.ToolbarFragment -import foundation.e.privacycentralapp.dummy.mapToString +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.extensions.viewModelProviderFactoryOf +import foundation.e.privacycentralapp.features.dashboard.DashboardFeature.State.DisabledState +import foundation.e.privacycentralapp.features.dashboard.DashboardFeature.State.EnabledState +import foundation.e.privacycentralapp.features.dashboard.DashboardFeature.State.LoadingState import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyFragment import foundation.e.privacycentralapp.features.location.FakeLocationFragment -import foundation.e.privacycentralapp.features.permissions.PermissionsFragment import foundation.e.privacycentralapp.features.trackers.TrackersFragment import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect class DashboardFragment : - ToolbarFragment(R.layout.fragment_dashboard), + NavToolbarFragment(R.layout.fragment_dashboard), MVIView { - private val viewModel: DashboardViewModel by activityViewModels() + private val dependencyContainer: DependencyContainer by lazy { + (this.requireActivity().application as PrivacyCentralApplication).dependencyContainer + } + + private val viewModel: DashboardViewModel by activityViewModels { + viewModelProviderFactoryOf { dependencyContainer.dashBoardViewModelFactory.create() } + } + + private lateinit var binding: FragmentDashboardBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -78,11 +92,8 @@ class DashboardFragment : } } is DashboardFeature.SingleEvent.NavigateToPermissionsSingleEvent -> { - requireActivity().supportFragmentManager.commit { - add(R.id.container) - setReorderingAllowed(true) - addToBackStack("dashboard") - } + val intent = Intent("android.intent.action.MANAGE_PERMISSIONS") + requireActivity().startActivity(intent) } DashboardFeature.SingleEvent.NavigateToTrackersSingleEvent -> { requireActivity().supportFragmentManager.commit { @@ -94,35 +105,37 @@ class DashboardFragment : } } } - lifecycleScope.launchWhenStarted { - viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction) - viewModel.submitAction(DashboardFeature.Action.ObserveDashboardAction) - } + // lifecycleScope.launchWhenStarted { + // viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction) + // viewModel.submitAction(DashboardFeature.Action.ObserveDashboardAction) + // } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - addClickToMore(view.findViewById(R.id.personal_leakag_info)) - view.let { - it.findViewById(R.id.tap_to_enable_quick_protection).setOnClickListener { - viewModel.submitAction(DashboardFeature.Action.ShowQuickPrivacyProtectionInfoAction) - } - it.findViewById(R.id.my_location).setOnClickListener { - viewModel.submitAction(DashboardFeature.Action.ShowFakeMyLocationAction) - } - it.findViewById(R.id.internet_activity_privacy).setOnClickListener { - viewModel.submitAction(DashboardFeature.Action.ShowInternetActivityPrivacyAction) - } - it.findViewById(R.id.apps_permissions).setOnClickListener { - viewModel.submitAction(DashboardFeature.Action.ShowAppsPermissions) - } - it.findViewById(R.id.am_i_tracked).setOnClickListener { - viewModel.submitAction(DashboardFeature.Action.ShowTrackers) - } + binding = FragmentDashboardBinding.bind(view) + + binding.togglePrivacyCentral.setOnClickListener { + viewModel.submitAction(DashboardFeature.Action.TogglePrivacyAction) + } + binding.myLocation.container.setOnClickListener { + viewModel.submitAction(DashboardFeature.Action.ShowFakeMyLocationAction) + } + binding.internetActivityPrivacy.container.setOnClickListener { + viewModel.submitAction(DashboardFeature.Action.ShowInternetActivityPrivacyAction) + } + binding.appsPermissions.container.setOnClickListener { + viewModel.submitAction(DashboardFeature.Action.ShowAppsPermissions) + } + + binding.amITracked.container.setOnClickListener { + viewModel.submitAction(DashboardFeature.Action.ShowTrackers) } } - override fun getTitle(): String = getString(R.string.privacy_dashboard) + override fun getTitle(): String { + return getString(R.string.dashboard_title) + } private fun addClickToMore(textView: TextView) { val clickToMore = SpannableString(getString(R.string.click_to_learn_more)) @@ -136,65 +149,96 @@ class DashboardFragment : } override fun render(state: DashboardFeature.State) { - when (state) { - is DashboardFeature.State.InitialState, is DashboardFeature.State.LoadingDashboardState -> { - view?.let { - it.findViewById(R.id.loadingSpinner).visibility = View.VISIBLE - it.findViewById(R.id.scrollContainer).visibility = View.GONE - } - } - is DashboardFeature.State.DashboardState -> { - view?.let { view -> - view.findViewById(R.id.loadingSpinner).visibility = View.GONE - view.findViewById(R.id.scrollContainer).visibility = - View.VISIBLE - view.findViewById(R.id.am_i_tracked_subtitle).text = getString( - R.string.am_i_tracked_subtitle, - state.trackersCount, - state.activeTrackersCount - ) - view.findViewById(R.id.apps_permissions_subtitle).text = getString( - R.string.apps_permissions_subtitle, - state.totalApps, - state.permissionCount - ) - view.findViewById(R.id.my_location_subtitle).let { textView -> - textView.text = getString( - R.string.my_location_subtitle, - state.appsUsingLocationPerm, - ) - textView.append( - SpannableString(state.locationMode.mapToString(requireContext())) - .also { - it.setSpan( - ForegroundColorSpan(Color.parseColor("#007fff")), - 0, - it.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } - ) - } - view.findViewById(R.id.internet_activity_privacy_subtitle) - .let { textView -> - textView.text = getString(R.string.internet_activity_privacy_subtitle) - textView.append( - SpannableString(state.internetPrivacyMode.mapToString(requireContext())) - .also { - it.setSpan( - ForegroundColorSpan(Color.parseColor("#007fff")), - 0, - it.length, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE - ) - } - ) - } - } - } - DashboardFeature.State.QuickProtectionState -> { - } + val enabled = state is EnabledState + binding.stateLabel.text = getString( + if (enabled) R.string.dashboard_state_label_on + else R.string.dashboard_state_label_off + ) + + binding.togglePrivacyCentral.setImageResource( + if (enabled) R.drawable.ic_quick_privacy_on + else R.drawable.ic_quick_privacy_off + ) + binding.stateLabel.setTextColor( + getColor( + requireContext(), + if (enabled) R.color.green_on + else R.color.orange_off + ) + ) + + val trackersEnabled = state is EnabledState && + state.isAllTrackersBlocked + binding.stateTrackers.text = getString( + if (trackersEnabled) R.string.dashboard_state_trackers_on + else R.string.dashboard_state_trackers_off + ) + binding.stateTrackers.setTextColor( + getColor( + requireContext(), + if (trackersEnabled) R.color.green_on + else R.color.black_text + ) + ) + + val geolocEnabled = state is EnabledState && state.locationMode != LocationMode.REAL_LOCATION + binding.stateGeolocation.text = getString( + if (geolocEnabled) R.string.dashboard_state_geolocation_on + else R.string.dashboard_state_geolocation_off + ) + binding.stateGeolocation.setTextColor( + getColor( + requireContext(), + if (geolocEnabled) R.color.green_on + else R.color.black_text + ) + ) + + val ipAddressEnabled = state is EnabledState && state.internetPrivacyMode != InternetPrivacyMode.REAL_IP + binding.stateIpAddress.text = getString( + if (ipAddressEnabled) R.string.dashboard_state_ipaddress_on + else R.string.dashboard_state_ipaddress_off + ) + binding.stateIpAddress.setTextColor( + getColor( + requireContext(), + if (ipAddressEnabled) R.color.green_on + else R.color.black_text + ) + ) + + // binding.graphTotal.text = if (state == DashboardFeature.State.LoadingState) { + // "" + // } else { + // val value = if (state is DashboardFeature.State.EnabledState) state.totalGraph + // else if (state is DashboardFeature.State.DisabledState) state.totalGraph + // else 0 // dummy + // getString(R.string.dashboard_graph_total, value) + // } + + binding.amITracked.subtitle.text = if (state == LoadingState) "" + else { + val value = if (state is EnabledState) state.activeTrackersCount + else if (state is DisabledState) state.activeTrackersCount + else 0 // dummy + getString(R.string.dashboard_am_i_tracked_subtitle, 77, value) } + + binding.myLocation.subtitle.text = getString( + if (state is EnabledState && + state.locationMode != LocationMode.REAL_LOCATION + ) + R.string.dashboard_location_subtitle_on + else R.string.dashboard_location_subtitle_off + ) + + binding.internetActivityPrivacy.subtitle.text = getString( + if (state is DashboardFeature.State.EnabledState && + state.internetPrivacyMode != InternetPrivacyMode.REAL_IP + ) + R.string.dashboard_internet_activity_privacy_subtitle_on + else R.string.dashboard_internet_activity_privacy_subtitle_on + ) } override fun actions(): Flow = viewModel.actions 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 3aa104e..c25f215 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 @@ -19,19 +19,23 @@ 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.GetQuickPrivacyStateUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch -class DashboardViewModel : ViewModel() { +class DashboardViewModel( + private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase +) : ViewModel() { private val _actions = MutableSharedFlow() val actions = _actions.asSharedFlow() val dashboardFeature: DashboardFeature by lazy { DashboardFeature.create( - DashboardFeature.State.InitialState, - coroutineScope = viewModelScope + coroutineScope = viewModelScope, + getPrivacyStateUseCase = getPrivacyStateUseCase ) } @@ -41,3 +45,11 @@ class DashboardViewModel : ViewModel() { } } } + +class DashBoardViewModelFactory( + private val getPrivacyStateUseCase: GetQuickPrivacyStateUseCase +) : Factory { + override fun create(): DashboardViewModel { + return DashboardViewModel(getPrivacyStateUseCase) + } +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt index 727afa9..981c8da 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/QuickProtectionFragment.kt @@ -32,7 +32,7 @@ class QuickProtectionFragment : NavToolbarFragment(R.layout.fragment_quick_prote override fun onAttach(context: Context) { super.onAttach(context) requireActivity().onBackPressedDispatcher.addCallback(this, true) { - viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction) + // viewModel.submitAction(DashboardFeature.Action.ShowDashboardAction) this.isEnabled = false requireActivity().onBackPressed() } 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 41ce9ad..cbe0a04 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 @@ -239,19 +239,6 @@ class InternetPrivacyFeature( action is Action.UseHiddenIPAction && effect is Effect.ShowAndroidVpnDisclaimerEffect -> SingleEvent.StartAndroidVpnActivityEvent(effect.intent) - - // Action.UseRealIPAction, Action.UseHiddenIPAction -> when (effect) { - // is Effect.ModeUpdatedEffect -> { - // if (effect.mode == InternetPrivacyMode.REAL_IP) { - // SingleEvent.RealIPSelectedEvent - // } else { - // SingleEvent.HiddenIPSelectedEvent - // } - // } - // is Effect.ErrorEffect -> { - // SingleEvent.ErrorEvent(effect.message) - // } - // } else -> null } } 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 22e63e3..c2be7b1 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 @@ -35,8 +35,8 @@ import foundation.e.flowmvi.MVIView import foundation.e.privacycentralapp.DependencyContainer import foundation.e.privacycentralapp.PrivacyCentralApplication import foundation.e.privacycentralapp.R -import foundation.e.privacycentralapp.common.NavToolbarFragment import foundation.e.privacycentralapp.common.ToggleAppsAdapter +import foundation.e.privacycentralapp.common.ToolbarFragment import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule import kotlinx.coroutines.flow.Flow @@ -44,7 +44,7 @@ import kotlinx.coroutines.flow.collect import java.util.Locale class InternetPrivacyFragment : - NavToolbarFragment(R.layout.fragment_internet_activity_policy), + ToolbarFragment(R.layout.fragment_internet_activity_policy), MVIView { private val dependencyContainer: DependencyContainer by lazy { 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 9124f85..e9fb078 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 @@ -23,10 +23,10 @@ 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.LocationMode import foundation.e.privacycentralapp.dummy.CityDataSource import foundation.e.privacycentralapp.dummy.DummyDataSource import foundation.e.privacycentralapp.dummy.Location -import foundation.e.privacycentralapp.dummy.LocationMode import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.flowOf diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt index 7281afc..bb9bd26 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt @@ -56,7 +56,7 @@ 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.dummy.LocationMode +import foundation.e.privacycentralapp.domain.entities.LocationMode import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf import kotlinx.coroutines.Job import kotlinx.coroutines.delay @@ -178,7 +178,7 @@ class FakeLocationFragment : Mapbox.getInstance(requireContext(), getString(R.string.mapbox_key)) } - override fun getTitle(): String = getString(R.string.my_location_title) + override fun getTitle(): String = getString(R.string.dashboard_location_title) private fun displayToast(message: String) { Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT) diff --git a/app/src/main/res/drawable/ic_apps_permissions.xml b/app/src/main/res/drawable/ic_apps_permissions.xml index b7eb1ab..5e7a570 100644 --- a/app/src/main/res/drawable/ic_apps_permissions.xml +++ b/app/src/main/res/drawable/ic_apps_permissions.xml @@ -1,22 +1,16 @@ - - - - - + + + + diff --git a/app/src/main/res/drawable/ic_internet_activity.xml b/app/src/main/res/drawable/ic_internet_activity.xml index ef34960..83695ad 100644 --- a/app/src/main/res/drawable/ic_internet_activity.xml +++ b/app/src/main/res/drawable/ic_internet_activity.xml @@ -1,22 +1,28 @@ - - - - - + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_my_location.xml b/app/src/main/res/drawable/ic_my_location.xml index 3b04dc4..5d70d16 100644 --- a/app/src/main/res/drawable/ic_my_location.xml +++ b/app/src/main/res/drawable/ic_my_location.xml @@ -1,22 +1,16 @@ - - - - - + + + + diff --git a/app/src/main/res/drawable/ic_privacy_toggle.xml b/app/src/main/res/drawable/ic_privacy_toggle.xml deleted file mode 100644 index 6a0f647..0000000 --- a/app/src/main/res/drawable/ic_privacy_toggle.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_quick_privacy_off.png b/app/src/main/res/drawable/ic_quick_privacy_off.png new file mode 100644 index 0000000..90f1b04 Binary files /dev/null and b/app/src/main/res/drawable/ic_quick_privacy_off.png differ diff --git a/app/src/main/res/drawable/ic_quick_privacy_on.png b/app/src/main/res/drawable/ic_quick_privacy_on.png new file mode 100644 index 0000000..99f6719 Binary files /dev/null and b/app/src/main/res/drawable/ic_quick_privacy_on.png differ diff --git a/app/src/main/res/drawable/ic_tracked.xml b/app/src/main/res/drawable/ic_tracked.xml index 9aa4736..6cef537 100644 --- a/app/src/main/res/drawable/ic_tracked.xml +++ b/app/src/main/res/drawable/ic_tracked.xml @@ -1,22 +1,43 @@ - - - - - + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dashboard_item_submenu_button.xml b/app/src/main/res/layout/dashboard_item_submenu_button.xml new file mode 100644 index 0000000..8802bca --- /dev/null +++ b/app/src/main/res/layout/dashboard_item_submenu_button.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml index effd992..077eaf1 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -1,26 +1,16 @@ + - - - - @@ -30,314 +20,222 @@ android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" - tools:context=".main.MainActivity" > - - - - - - - + - - + - - - - - - - - - - - - + - - + - - - - - - - - - - - - + - - + - - - - + android:text="@string/dashboard_state_ipaddress_off" + android:textSize="12sp" + android:textColor="@color/black_text" + android:textAllCaps="true" + /> + + - - - - + + - + + + + + + + + + + - - - - - - - - - + android:src="@drawable/dummy_leakage_analytics" + app:layout_constraintTop_toBottomOf="@+id/graph_period" + /> + + + + - - + + - + + + + - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_internet_activity_policy.xml b/app/src/main/res/layout/fragment_internet_activity_policy.xml index 12094ab..2c7e102 100644 --- a/app/src/main/res/layout/fragment_internet_activity_policy.xml +++ b/app/src/main/res/layout/fragment_internet_activity_policy.xml @@ -57,7 +57,7 @@ android:layout_width="wrap_content" android:paddingBottom="8dp" android:paddingTop="16dp" - android:text="@string/internet_activity_privacy_title" + android:text="@string/dashboard_internet_activity_privacy_title" android:textColor="@color/black" android:textSize="14sp" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 4f45122..2a16240 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -9,4 +9,16 @@ #FFFFFFFF @lineageos.platform:color/color_default_accent + + #DE000000 + + #99000000 + #61000000 + #14212121 + + #FC7222 + #169659 + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d18ccf5..f6bcf72 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,17 +1,37 @@ PrivacyCentralApp - Privacy dashboard helps you control and better protect your privacy - Tap to enable quick privacy protection - Personal data leakage over past 24 hours. - Am I tracked? - Currently there are %1$d trackers in your apps, %2$d trackers are active - Apps Permissions - %1$d apps are requesting %2$d permissions - My Location - "%1$d apps are using location permission\nCurrent location mode: " + + + Quick Privacy + Your online privacy is not protected + Your online privacy is now protected! + Tap to enable your privacy preferences + Trackers: + Vulnerable + Denied + Geolocation: + Exposed + Fake + IP address: + Exposed + Hidden + Personal data leakage + Last 24 hours + %d hits + + Am I tracked? + %1$d app trackers, %2$d active trackers + Apps Permissions + Manage your permissions + Geolocation mode + Real geolocation + Fake geolocation + My internet activity privacy + Real IP address exposed + Real IP address hidden + + Internet Activity Privacy - My Internet Activity Privacy - "Current internet activity mode: " Quick protection enables these settings when turned on - All trackers are turned off.\n- Your geolocation will be faked.\n- Your real IP address will be hidden. Learn more diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 3a7bad8..1e20ab1 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -8,5 +8,9 @@ #007fff + @color/black_text + 16sp + + \ No newline at end of file -- cgit v1.2.1