diff options
author | Romain Hunault <romain.hunault@e.email> | 2021-08-13 10:14:03 +0000 |
---|---|---|
committer | Romain Hunault <romain.hunault@e.email> | 2021-08-13 10:14:03 +0000 |
commit | daea2f9510ac1af22a4e2e2f3db7c2d6d314008b (patch) | |
tree | 2dfcd606e8693a79520f9931e566c237547e7cd1 /app | |
parent | 97b51f18dcc2f87a9cdd7f482033e30a6282d853 (diff) | |
parent | f522c4615503120b5e62e45405e8c6c3b18e5e4a (diff) |
Merge branch 'features/blocker' into 'master'
Add blocker feature
See merge request e/privacy-central/privacycentralapp!6
Diffstat (limited to 'app')
33 files changed, 1042 insertions, 220 deletions
diff --git a/app/build.gradle b/app/build.gradle index 24ec426..8d94e2b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -80,10 +80,15 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + + aaptOptions { + additionalParameters '-I', 'app/libs/e-ui-sdk-1.0.1-q.jar' + } } dependencies { compileOnly files('libs/e-ui-sdk-1.0.1-q.jar') + implementation files('libs/lineage-sdk.jar') implementation project(":privacymodulesapi") // include the google specific version of the modules, just for the google flavor diff --git a/app/libs/lineage-sdk.jar b/app/libs/lineage-sdk.jar Binary files differnew file mode 100644 index 0000000..c2f04a7 --- /dev/null +++ b/app/libs/lineage-sdk.jar diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7145af9..4babd46 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" tools:ignore="ProtectedPermissions" /> + <uses-permission android:name="lineageos.permission.ACCESS_BLOCKER" /> <application android:name=".PrivacyCentralApplication" diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 364ae4a..fcc2eaa 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -26,6 +26,7 @@ import foundation.e.privacymodules.location.FakeLocation import foundation.e.privacymodules.location.IFakeLocation import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription +import lineageos.blockers.BlockerInterface /** * Simple container to hold application wide dependencies. @@ -55,4 +56,6 @@ class DependencyContainer constructor(val app: Application) { val fakeLocationViewModelFactory by lazy { FakeLocationViewModelFactory(locationApi) } + + val blockerService = BlockerInterface.getInstance(context) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt index 9372a66..153e0c1 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt @@ -18,9 +18,17 @@ package foundation.e.privacycentralapp import android.app.Application +import foundation.e.privacycentralapp.dummy.TrackersDataSource class PrivacyCentralApplication : Application() { // Initialize the dependency container. val dependencyContainer: DependencyContainer by lazy { DependencyContainer(this) } + + override fun onCreate() { + super.onCreate() + + // Inject blocker service in trackers source. + TrackersDataSource.injectBlockerService(dependencyContainer.blockerService) + } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/ToolbarFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/common/ToolbarFragment.kt index f156e09..5c18548 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/ToolbarFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/ToolbarFragment.kt @@ -40,4 +40,6 @@ abstract class ToolbarFragment(@LayoutRes contentLayoutId: Int) : Fragment(conte open fun setupToolbar(toolbar: MaterialToolbar) { toolbar.title = getTitle() } + + fun getToolbar(): MaterialToolbar? = view?.findViewById(R.id.toolbar) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackersDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackersDataSource.kt new file mode 100644 index 0000000..b6f319c --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackersDataSource.kt @@ -0,0 +1,105 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.dummy + +import foundation.e.privacycentralapp.R +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import lineageos.blockers.BlockerInterface + +data class TrackedApp(val appName: String, val isEnabled: Boolean, val iconId: Int) + +data class Tracker( + val name: String, + val domain: String? = null, + val ipAddress: String? = null, + val trackedApps: List<TrackedApp> +) + +object TrackersDataSource { + + private lateinit var blockerService: BlockerInterface + + val facebook = TrackedApp("Facebook", true, R.drawable.ic_facebook) + val firefox = TrackedApp("Firefox", true, R.drawable.ic_facebook) + val google = TrackedApp("Google", true, R.drawable.ic_facebook) + val whatsapp = TrackedApp("Whatsapp", true, R.drawable.ic_facebook) + val blisslauncher = TrackedApp("BlissLauncher", true, R.drawable.ic_facebook) + val youtube = TrackedApp("Youtube", true, R.drawable.ic_facebook) + + val crashlytics = Tracker( + "Google Crashlytics", + domain = "|0b|crashlytics|03|com", + trackedApps = listOf(facebook, firefox) + ) + + val facebookAds = Tracker( + "Facebook Analytics", + domain = "|08|facebook|03|com", + trackedApps = listOf(facebook, whatsapp) + ) + val rubiconTracker = Tracker( + "Rubicon Projects", + domain = "|03|ads|0e|rubiconproject|03|com", + trackedApps = listOf(google, blisslauncher, youtube) + ) + val googleAnalytics = Tracker( + "Google Analytics", + domain = "|10|google-analytics|03|com", + trackedApps = listOf(facebook, firefox) + ) + + val _trackers = + MutableStateFlow(listOf(crashlytics, facebookAds, rubiconTracker, googleAnalytics)) + val trackers = _trackers.asStateFlow() + + fun injectBlockerService(blockerInterface: BlockerInterface) { + this.blockerService = blockerInterface + } + + fun getTracker(name: String): Tracker? { + try { + return _trackers.value.first { + it.name == name + } + } catch (e: NoSuchElementException) { + return null + } + } + + fun toggleTracker(tracker: Tracker, enable: Boolean): Boolean { + val result = if (!enable) { + blockerService.blockDomain(tracker.domain) + } else { + blockerService.unblockDomain(tracker.domain) + } + + if (result) { + _trackers.value = _trackers.value.map { + if (it.name == tracker.name) { + it.copy( + trackedApps = it.trackedApps.map { app -> + app.copy(isEnabled = enable) + } + ) + } else it + } + } + return result + } +} 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 5a20489..c26fce1 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt @@ -25,6 +25,7 @@ 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 kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOf @@ -76,6 +77,7 @@ class DashboardFeature( object ShowFakeMyLocationAction : Action() object ShowInternetActivityPrivacyAction : Action() object ShowAppsPermissions : Action() + object ShowTrackers : Action() } sealed class Effect { @@ -92,12 +94,14 @@ class DashboardFeature( object LoadingDashboardEffect : Effect() data class UpdateActiveTrackersCountEffect(val count: Int) : Effect() + data class UpdateTotalTrackersCountEffect(val count: Int) : Effect() 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() + object OpenTrackersEffect : Effect() } companion object { @@ -127,6 +131,11 @@ class DashboardFeature( 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) @@ -144,14 +153,27 @@ class DashboardFeature( Effect.OpenFakeMyLocationEffect -> state Effect.OpenAppsPermissionsEffect -> state Effect.OpenInternetActivityPrivacyEffect -> state + Effect.OpenTrackersEffect -> state } }, actor = { _: State, action: Action -> Log.d("Feature", "action: $action") when (action) { Action.ObserveDashboardAction -> merge( - DummyDataSource.activeTrackersCount.map { - Effect.UpdateActiveTrackersCountEffect(it) + TrackersDataSource.trackers.map { + var activeTrackersCount: Int = 0 + outer@ for (tracker in it) { + for (app in tracker.trackedApps) { + if (!app.isEnabled) { + continue@outer + } + } + activeTrackersCount++ + } + Effect.UpdateActiveTrackersCountEffect(activeTrackersCount) + }, + TrackersDataSource.trackers.map { + Effect.UpdateTotalTrackersCountEffect(it.size) }, DummyDataSource.appsUsingLocationPerm.map { Effect.UpdateAppsUsingLocationPermEffect(it.size) @@ -185,6 +207,7 @@ class DashboardFeature( Action.ShowInternetActivityPrivacyAction -> flowOf( Effect.OpenInternetActivityPrivacyEffect ) + Action.ShowTrackers -> flowOf(Effect.OpenTrackersEffect) } }, singleEventProducer = { state, _, effect -> @@ -197,6 +220,8 @@ class DashboardFeature( 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 } ) 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 c57e6cc..e7ce353 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 @@ -38,6 +38,7 @@ import foundation.e.privacycentralapp.dummy.mapToString 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 @@ -84,6 +85,11 @@ class DashboardFragment : } } DashboardFeature.SingleEvent.NavigateToTrackersSingleEvent -> { + requireActivity().supportFragmentManager.commit { + add<TrackersFragment>(R.id.container) + setReorderingAllowed(true) + addToBackStack("dashboard") + } } } } @@ -110,6 +116,9 @@ class DashboardFragment : it.findViewById<RelativeLayout>(R.id.apps_permissions).setOnClickListener { viewModel.submitAction(DashboardFeature.Action.ShowAppsPermissions) } + it.findViewById<RelativeLayout>(R.id.am_i_tracked).setOnClickListener { + viewModel.submitAction(DashboardFeature.Action.ShowTrackers) + } } } 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 7e45049..9124f85 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 @@ -36,8 +36,7 @@ class FakeLocationFeature( coroutineScope: CoroutineScope, reducer: Reducer<State, Effect>, actor: Actor<State, Action, Effect>, - singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent>, - private val locationApi: LocationApiDelegate + singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent> ) : BaseFeature<FakeLocationFeature.State, FakeLocationFeature.Action, FakeLocationFeature.Effect, FakeLocationFeature.SingleEvent>( initialState, actor, @@ -206,8 +205,7 @@ class FakeLocationFeature( is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message) else -> null } - }, - locationApi = locationApi + } ) } } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt index 4f9b602..4905dca 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt @@ -36,17 +36,17 @@ class PermissionAppsAdapter( val appName: TextView = view.findViewById(R.id.app_title) @SuppressLint("UseSwitchCompatOrMaterialCode") - val togglePermission: Switch = view.findViewById(R.id.togglePermission) + val togglePermission: Switch = view.findViewById(R.id.toggle) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PermissionViewHolder { val view = LayoutInflater.from(parent.context) - .inflate(R.layout.item_permission_apps, parent, false) + .inflate(R.layout.item_app_toggle, parent, false) val holder = PermissionViewHolder(view) holder.togglePermission.setOnCheckedChangeListener { _, isChecked -> listener(dataSet[holder.adapterPosition].first, isChecked) } - view.findViewById<Switch>(R.id.togglePermission) + view.findViewById<Switch>(R.id.toggle) return holder } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt new file mode 100644 index 0000000..ae236b9 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt @@ -0,0 +1,61 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.features.trackers + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Switch +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.dummy.Tracker + +class TrackerAppsAdapter( + private var tracker: Tracker, + private val listener: (Tracker, Boolean) -> Unit +) : + RecyclerView.Adapter<TrackerAppsAdapter.TrackerViewHolder>() { + + class TrackerViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val titleView: TextView = view.findViewById(R.id.app_title) + @SuppressLint("UseSwitchCompatOrMaterialCode") + val toggleBlocker: Switch = view.findViewById(R.id.toggle) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackerViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_app_toggle, parent, false) + val holder = TrackerViewHolder(view) + holder.toggleBlocker.setOnClickListener { + if (it is Switch) { + listener(tracker, it.isChecked) + } + } + return holder + } + + override fun onBindViewHolder(holder: TrackerViewHolder, position: Int) { + val app = tracker.trackedApps[position] + holder.titleView.text = app.appName + holder.toggleBlocker.isChecked = app.isEnabled + } + + override fun getItemCount(): Int = tracker.trackedApps.size +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt new file mode 100644 index 0000000..fff24dc --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt @@ -0,0 +1,101 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.features.trackers + +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.Toast +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import foundation.e.flowmvi.MVIView +import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.common.NavToolbarFragment +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect + +class TrackerAppsFragment : + NavToolbarFragment(R.layout.fragment_tracker_apps), + MVIView<TrackersFeature.State, TrackersFeature.Action> { + + private val viewModel: TrackersViewModel by viewModels() + + private val TAG = "TrackerAppsFragment" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + lifecycleScope.launchWhenStarted { + viewModel.trackersFeature.takeView(this, this@TrackerAppsFragment) + } + lifecycleScope.launchWhenStarted { + viewModel.trackersFeature.singleEvents.collect { event -> + when (event) { + is TrackersFeature.SingleEvent.ErrorEvent -> displayToast(event.error) + is TrackersFeature.SingleEvent.BlockerErrorEvent -> { + displayToast("Couldn't toggle") + // Re-render the current state to reset the switches. + render(viewModel.trackersFeature.state.value) + } + } + } + } + lifecycleScope.launchWhenStarted { + viewModel.submitAction( + TrackersFeature.Action.ObserveTracker( + requireArguments().getString( + "TRACKER" + ) + ) + ) + } + } + + private fun displayToast(message: String) { + Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT) + .show() + } + + override fun getTitle(): String = getString(R.string.tracker) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + view.findViewById<RecyclerView>(R.id.recylcer_view_tracker_apps)?.apply { + layoutManager = LinearLayoutManager(requireContext()) + setHasFixedSize(true) + } + } + + override fun render(state: TrackersFeature.State) { + Log.d(TAG, "render() called with: state = $state") + state.currentSelectedTracker?.let { tracker -> + view?.findViewById<RecyclerView>(R.id.recylcer_view_tracker_apps)?.adapter = TrackerAppsAdapter(tracker) { it, grant -> + viewModel.submitAction( + TrackersFeature.Action.ToggleTrackerAction( + it, + grant + ) + ) + } + getToolbar()?.title = tracker.name + } + } + + override fun actions(): Flow<TrackersFeature.Action> = viewModel.actions +} diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersAdapter.kt new file mode 100644 index 0000000..cef069e --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersAdapter.kt @@ -0,0 +1,56 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.features.trackers + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.dummy.Tracker + +class TrackersAdapter( + private var dataSet: List<Tracker> = emptyList(), + private val listener: (Tracker) -> Unit +) : + RecyclerView.Adapter<TrackersAdapter.TrackerViewHolder>() { + + class TrackerViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val titleView: TextView = view as TextView + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackerViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_list_tracker, parent, false) + val holder = TrackerViewHolder(view) + holder.titleView.setOnClickListener { listener(dataSet[holder.adapterPosition]) } + return holder + } + + override fun onBindViewHolder(holder: TrackerViewHolder, position: Int) { + val tracker = dataSet[position] + holder.titleView.text = tracker.name + } + + override fun getItemCount(): Int = dataSet.size + + fun setData(data: List<Tracker>) { + this.dataSet = data + } +} 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 new file mode 100644 index 0000000..9400181 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt @@ -0,0 +1,142 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.features.trackers + +import android.util.Log +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.Tracker +import foundation.e.privacycentralapp.dummy.TrackersDataSource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map + +// Define a state machine for Tracker feature. +class TrackersFeature( + initialState: State, + coroutineScope: CoroutineScope, + reducer: Reducer<State, Effect>, + actor: Actor<State, Action, Effect>, + singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent> +) : BaseFeature<TrackersFeature.State, TrackersFeature.Action, TrackersFeature.Effect, TrackersFeature.SingleEvent>( + initialState, + actor, + reducer, + coroutineScope, + { message -> Log.d("TrackersFeature", message) }, + singleEventProducer +) { + data class State( + val trackers: List<Tracker> = emptyList(), + val currentSelectedTracker: Tracker? = null + ) + + sealed class SingleEvent { + data class ErrorEvent(val error: String) : SingleEvent() + object BlockerErrorEvent : SingleEvent() + } + + sealed class Action { + object ObserveTrackers : Action() + data class SetSelectedTracker(val tracker: Tracker) : Action() + data class ToggleTrackerAction( + val tracker: Tracker, + val grant: Boolean + ) : Action() + data class ObserveTracker(val tracker: String?) : Action() + } + + sealed class Effect { + data class TrackersLoadedEffect(val trackers: List<Tracker>) : Effect() + data class TrackerSelectedEffect(val tracker: Tracker) : Effect() + data class TrackerToggleEffect(val result: Boolean) : Effect() + data class ErrorEffect(val message: String) : Effect() + data class TrackerLoadedEffect(val tracker: Tracker) : Effect() + } + + companion object { + fun create( + initialState: State = State(), + coroutineScope: CoroutineScope + ) = TrackersFeature( + initialState, coroutineScope, + reducer = { state, effect -> + when (effect) { + is Effect.TrackersLoadedEffect -> State(effect.trackers) + is Effect.TrackerSelectedEffect -> state.copy(currentSelectedTracker = effect.tracker) + is Effect.ErrorEffect -> state + is Effect.TrackerToggleEffect -> { + state + } + is Effect.TrackerLoadedEffect -> { + state.copy(currentSelectedTracker = effect.tracker) + } + } + }, + actor = { state, action -> + when (action) { + Action.ObserveTrackers -> TrackersDataSource.trackers.map { + Effect.TrackersLoadedEffect( + it + ) + } + is Action.SetSelectedTracker -> flowOf( + Effect.TrackerSelectedEffect( + action.tracker + ) + ) + + is Action.ToggleTrackerAction -> { + if (state.currentSelectedTracker != null) { + val result = TrackersDataSource.toggleTracker( + action.tracker, + action.grant + ) + flowOf(Effect.TrackerToggleEffect(result)) + } else { + flowOf(Effect.ErrorEffect("Can't toggle tracker")) + } + } + is Action.ObserveTracker -> { + if (action.tracker == null) { + flowOf(Effect.ErrorEffect("Null tracker id passed")) + } else { + val tracker = TrackersDataSource.getTracker(action.tracker) + if (tracker != null) { + flowOf(Effect.TrackerLoadedEffect(tracker)) + } else { + flowOf(Effect.ErrorEffect("Can't find tracker with name ${action.tracker}")) + } + } + } + } + }, + singleEventProducer = { _, _, effect -> + when (effect) { + is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message) + is Effect.TrackerToggleEffect -> { + if (!effect.result) SingleEvent.BlockerErrorEvent else null + } + 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 new file mode 100644 index 0000000..e3dc941 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt @@ -0,0 +1,76 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.features.trackers + +import android.os.Bundle +import android.view.View +import androidx.core.os.bundleOf +import androidx.fragment.app.add +import androidx.fragment.app.commit +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import foundation.e.flowmvi.MVIView +import foundation.e.privacycentralapp.R +import foundation.e.privacycentralapp.common.NavToolbarFragment +import kotlinx.coroutines.flow.Flow + +class TrackersFragment : + NavToolbarFragment(R.layout.fragment_trackers), + MVIView<TrackersFeature.State, TrackersFeature.Action> { + + private val viewModel: TrackersViewModel by viewModels() + private lateinit var trackersAdapter: TrackersAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + lifecycleScope.launchWhenStarted { + viewModel.trackersFeature.takeView(this, this@TrackersFragment) + } + lifecycleScope.launchWhenStarted { + viewModel.submitAction(TrackersFeature.Action.ObserveTrackers) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + trackersAdapter = TrackersAdapter { + requireActivity().supportFragmentManager.commit { + val bundle = bundleOf("TRACKER" to it.name) + add<TrackerAppsFragment>(R.id.container, args = bundle) + setReorderingAllowed(true) + addToBackStack("trackers") + } + // viewModel.submitAction(TrackersFeature.Action.SetSelectedTracker(it)) + } + view.findViewById<RecyclerView>(R.id.recylcer_view_trackers)?.apply { + layoutManager = LinearLayoutManager(requireContext()) + setHasFixedSize(true) + adapter = trackersAdapter + } + } + + override fun getTitle() = getString(R.string.trackers) + + override fun render(state: TrackersFeature.State) { + trackersAdapter.setData(state.trackers) + } + + override fun actions(): Flow<TrackersFeature.Action> = viewModel.actions +} 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 new file mode 100644 index 0000000..ee89887 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt @@ -0,0 +1,42 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +package foundation.e.privacycentralapp.features.trackers + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch + +class TrackersViewModel : ViewModel() { + + private val _actions = MutableSharedFlow<TrackersFeature.Action>() + val actions = _actions.asSharedFlow() + + val trackersFeature: TrackersFeature by lazy { + TrackersFeature.create(coroutineScope = viewModelScope) + } + + fun submitAction(action: TrackersFeature.Action) { + Log.d("TrackersViewModel", "submitting action") + viewModelScope.launch { + _actions.emit(action) + } + } +} diff --git a/app/src/main/res/drawable/dummy_trackers_usage.png b/app/src/main/res/drawable/dummy_trackers_usage.png Binary files differnew file mode 100644 index 0000000..9b7e090 --- /dev/null +++ b/app/src/main/res/drawable/dummy_trackers_usage.png diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 7013496..2627a32 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" - android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_width="match_parent" />
\ 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 dc79878..effd992 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -2,46 +2,46 @@ <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_width="match_parent" > - <include layout="@layout/topbar"/> + <include layout="@layout/topbar" /> <ProgressBar android:id="@+id/loadingSpinner" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" android:indeterminate="true" + android:layout_gravity="center" + android:layout_height="wrap_content" + android:layout_width="wrap_content" /> <androidx.core.widget.NestedScrollView android:id="@+id/scrollContainer" - android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_width="match_parent" android:visibility="gone" app:layout_behavior="@string/appbar_scrolling_view_behavior" > <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" android:background="@color/white" android:gravity="center_horizontal" + android:layout_height="match_parent" + android:layout_width="match_parent" android:orientation="vertical" tools:context=".main.MainActivity" > <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" android:gravity="center" + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" - android:paddingTop="16dp" android:paddingRight="32dp" - android:paddingBottom="16dp" + android:paddingTop="16dp" android:text="@string/privacy_dashboard_info" android:textColor="@color/black" android:textSize="14sp" @@ -49,91 +49,91 @@ <ImageView android:id="@+id/togglePrivacyCentral" - android:layout_width="96dp" android:layout_height="96dp" - android:layout_marginTop="16dp" android:layout_marginBottom="16dp" + android:layout_marginTop="16dp" + android:layout_width="96dp" android:src="@drawable/ic_privacy_toggle" /> <TextView - android:id="@+id/tap_to_enable_quick_protection" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" android:drawableEnd="@drawable/ic_chevron_right_24dp" android:fontFamily="sans-serif-medium" android:gravity="center" + android:id="@+id/tap_to_enable_quick_protection" + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" - android:paddingTop="16dp" android:paddingRight="32dp" - android:paddingBottom="16dp" + android:paddingTop="16dp" android:text="@string/tap_to_enable_quick_protection" android:textColor="@color/black" android:textSize="14sp" /> <ImageView - android:layout_width="match_parent" android:layout_height="160dp" + android:layout_width="match_parent" android:src="@drawable/dummy_leakage_analytics" /> <TextView + android:gravity="center" android:id="@+id/personal_leakag_info" - android:layout_width="match_parent" - android:layout_height="wrap_content" android:layout_gravity="center_horizontal" - android:gravity="center" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" android:paddingRight="32dp" - android:paddingBottom="16dp" android:text="@string/personal_leakage_info" android:textColor="@color/black" android:textSize="12sp" /> <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" android:background="#f9f9f9" + android:layout_height="match_parent" + android:layout_width="match_parent" android:orientation="vertical" > <RelativeLayout android:id="@+id/am_i_tracked" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" - android:paddingTop="16dp" android:paddingRight="32dp" - android:paddingBottom="16dp" + android:paddingTop="16dp" > <ImageView android:id="@+id/am_i_tracked_icon" - android:layout_width="36dp" - android:layout_height="36dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" + android:layout_height="36dp" + android:layout_width="36dp" android:src="@drawable/ic_tracked" /> <LinearLayout - android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_toStartOf="@+id/am_i_tracked_chevron" android:layout_toEndOf="@+id/am_i_tracked_icon" + android:layout_toStartOf="@+id/am_i_tracked_chevron" + android:layout_width="match_parent" android:orientation="vertical" - android:paddingStart="16dp" android:paddingEnd="32dp" + android:paddingStart="16dp" > <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/am_i_tracked_title" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="sans-serif-medium" + android:layout_width="match_parent" android:text="@string/am_i_tracked_title" android:textColor="@color/black" android:textSize="16sp" @@ -141,8 +141,8 @@ <TextView android:id="@+id/am_i_tracked_subtitle" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:text="@string/am_i_tracked_subtitle" android:textColor="@color/black" android:textSize="14sp" @@ -151,48 +151,48 @@ <ImageView android:id="@+id/am_i_tracked_chevron" - android:layout_width="24dp" - android:layout_height="24dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" + android:layout_height="24dp" + android:layout_width="24dp" android:src="@drawable/ic_chevron_right_24dp" /> </RelativeLayout> <RelativeLayout android:id="@+id/apps_permissions" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" - android:paddingTop="16dp" android:paddingRight="32dp" - android:paddingBottom="16dp" + android:paddingTop="16dp" > <ImageView android:id="@+id/apps_permissions_icon" - android:layout_width="36dp" - android:layout_height="36dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" + android:layout_height="36dp" + android:layout_width="36dp" android:src="@drawable/ic_apps_permissions" /> <LinearLayout - android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_toStartOf="@+id/apps_permissions_chevron" android:layout_toEndOf="@+id/apps_permissions_icon" + android:layout_toStartOf="@+id/apps_permissions_chevron" + android:layout_width="match_parent" android:orientation="vertical" - android:paddingStart="16dp" android:paddingEnd="32dp" + android:paddingStart="16dp" > <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/apps_permissions_title" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="sans-serif-medium" + android:layout_width="match_parent" android:text="@string/apps_permissions_title" android:textColor="@color/black" android:textSize="16sp" @@ -200,8 +200,8 @@ <TextView android:id="@+id/apps_permissions_subtitle" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:text="@string/apps_permissions_subtitle" android:textColor="@color/black" android:textSize="14sp" @@ -210,48 +210,48 @@ <ImageView android:id="@+id/apps_permissions_chevron" - android:layout_width="24dp" - android:layout_height="24dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" + android:layout_height="24dp" + android:layout_width="24dp" android:src="@drawable/ic_chevron_right_24dp" /> </RelativeLayout> <RelativeLayout android:id="@+id/my_location" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" - android:paddingTop="16dp" android:paddingRight="32dp" - android:paddingBottom="16dp" + android:paddingTop="16dp" > <ImageView android:id="@+id/my_location_icon" - android:layout_width="36dp" - android:layout_height="36dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" + android:layout_height="36dp" + android:layout_width="36dp" android:src="@drawable/ic_my_location" /> <LinearLayout - android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_toStartOf="@+id/my_location_chevron" android:layout_toEndOf="@+id/my_location_icon" + android:layout_toStartOf="@+id/my_location_chevron" + android:layout_width="match_parent" android:orientation="vertical" - android:paddingStart="16dp" android:paddingEnd="32dp" + android:paddingStart="16dp" > <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/my_location_title" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="sans-serif-medium" + android:layout_width="match_parent" android:text="@string/my_location_title" android:textColor="@color/black" android:textSize="16sp" @@ -259,8 +259,8 @@ <TextView android:id="@+id/my_location_subtitle" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:text="@string/my_location_subtitle" android:textColor="@color/black" android:textSize="14sp" @@ -269,48 +269,48 @@ <ImageView android:id="@+id/my_location_chevron" - android:layout_width="24dp" - android:layout_height="24dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" + android:layout_height="24dp" + android:layout_width="24dp" android:src="@drawable/ic_chevron_right_24dp" /> </RelativeLayout> <RelativeLayout android:id="@+id/internet_activity_privacy" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" android:paddingLeft="32dp" - android:paddingTop="16dp" android:paddingRight="32dp" - android:paddingBottom="16dp" + android:paddingTop="16dp" > <ImageView android:id="@+id/internet_activity_privacy_icon" - android:layout_width="36dp" - android:layout_height="36dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" + android:layout_height="36dp" + android:layout_width="36dp" android:src="@drawable/ic_internet_activity" /> <LinearLayout - android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_toStartOf="@+id/internet_activity_privacy_chevron" android:layout_toEndOf="@+id/internet_activity_privacy_icon" + android:layout_toStartOf="@+id/internet_activity_privacy_chevron" + android:layout_width="match_parent" android:orientation="vertical" - android:paddingStart="16dp" android:paddingEnd="32dp" + android:paddingStart="16dp" > <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/internet_activity_privacy_title" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="sans-serif-medium" + android:layout_width="match_parent" android:text="@string/internet_activity_privacy_title" android:textColor="@color/black" android:textSize="16sp" @@ -318,8 +318,8 @@ <TextView android:id="@+id/internet_activity_privacy_subtitle" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:text="@string/internet_activity_privacy_subtitle" android:textColor="@color/black" android:textSize="14sp" @@ -328,10 +328,10 @@ <ImageView android:id="@+id/internet_activity_privacy_chevron" - android:layout_width="24dp" - android:layout_height="24dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" + android:layout_height="24dp" + android:layout_width="24dp" android:src="@drawable/ic_chevron_right_24dp" /> </RelativeLayout> diff --git a/app/src/main/res/layout/fragment_fake_location.xml b/app/src/main/res/layout/fragment_fake_location.xml index 40324a1..d60513b 100644 --- a/app/src/main/res/layout/fragment_fake_location.xml +++ b/app/src/main/res/layout/fragment_fake_location.xml @@ -8,7 +8,7 @@ android:layout_width="match_parent" > - <include layout="@layout/topbar"/> + <include layout="@layout/topbar" /> <androidx.core.widget.NestedScrollView android:layout_height="match_parent" @@ -18,9 +18,9 @@ <LinearLayout android:layout_height="match_parent" + android:layout_marginBottom="32dp" android:layout_width="match_parent" android:orientation="vertical" - android:layout_marginBottom="32dp" tools:context=".main.MainActivity" > @@ -29,9 +29,9 @@ android:layout_gravity="center_horizontal" android:layout_height="wrap_content" android:layout_width="match_parent" - android:paddingTop="16dp" android:paddingLeft="32dp" android:paddingRight="32dp" + android:paddingTop="16dp" android:text="@string/fake_location_info" android:textColor="@color/black" android:textSize="14sp" 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 7a5d8b5..c3021df 100644 --- a/app/src/main/res/layout/fragment_internet_activity_policy.xml +++ b/app/src/main/res/layout/fragment_internet_activity_policy.xml @@ -2,23 +2,23 @@ <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" android:background="@color/white" + android:layout_height="match_parent" + android:layout_width="match_parent" > - <include layout="@layout/topbar"/> + <include layout="@layout/topbar" /> <androidx.core.widget.NestedScrollView - android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="32dp" + android:layout_width="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" > <LinearLayout - android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_width="match_parent" android:orientation="vertical" android:paddingLeft="32dp" android:paddingRight="32dp" @@ -27,9 +27,9 @@ <TextView android:id="@+id/internet_activity_privacy_info" - android:layout_width="match_parent" - android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" android:paddingTop="16dp" android:text="@string/internet_activity_privacy_info" android:textColor="@color/black" @@ -37,23 +37,23 @@ /> <TextView - android:id="@+id/learn_more_internet_activity_privacy_info" - android:layout_width="wrap_content" - android:layout_height="48dp" android:fontFamily="sans-serif-medium" android:gravity="center_vertical" + android:id="@+id/learn_more_internet_activity_privacy_info" + android:layout_height="48dp" + android:layout_width="wrap_content" android:text="@string/learn_more" android:textColor="#007fff" android:textSize="14sp" /> <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/my_internet_activity_header" - android:layout_width="wrap_content" android:layout_height="wrap_content" - android:fontFamily="sans-serif-medium" - android:paddingTop="16dp" + android:layout_width="wrap_content" android:paddingBottom="8dp" + android:paddingTop="16dp" android:text="@string/internet_activity_privacy_title" android:textColor="@color/black" android:textSize="14sp" @@ -61,37 +61,41 @@ <RadioGroup android:id="@+id/internet_activity_privacy_choices" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:orientation="vertical" > <foundation.e.privacycentralapp.common.RightRadioButton android:id="@+id/radio_use_real_ip" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:text="@string/use_real_ip" android:textSize="16sp" /> + <TextView - android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_width="wrap_content" android:text="@string/i_can_be_tracked" - android:textSize="14sp"/> + android:textSize="14sp" + /> <foundation.e.privacycentralapp.common.RightRadioButton android:id="@+id/radio_use_hidden_ip" - android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="8dp" + android:layout_width="match_parent" android:text="@string/hidden_ip" android:textSize="16sp" - android:layout_marginTop="8dp" /> + <TextView - android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_width="wrap_content" android:text="@string/i_am_anonymous" - android:textSize="14sp"/> + android:textSize="14sp" + /> </RadioGroup> </LinearLayout> </androidx.core.widget.NestedScrollView> diff --git a/app/src/main/res/layout/fragment_permission_apps.xml b/app/src/main/res/layout/fragment_permission_apps.xml index 65f4169..db61e93 100644 --- a/app/src/main/res/layout/fragment_permission_apps.xml +++ b/app/src/main/res/layout/fragment_permission_apps.xml @@ -2,37 +2,45 @@ <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" android:background="@color/white" + android:layout_height="match_parent" + android:layout_width="match_parent" > - <include layout="@layout/topbar"/> + <include layout="@layout/topbar" /> - <LinearLayout - android:layout_width="match_parent" + <androidx.core.widget.NestedScrollView android:layout_height="match_parent" - android:orientation="vertical" - tools:context=".main.MainActivity" + android:layout_width="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior" > - <TextView - android:id="@+id/permission_control" + <LinearLayout + android:layout_height="match_parent" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:paddingTop="16dp" - android:paddingBottom="16dp" - android:paddingLeft="32dp" - android:paddingRight="32dp" - android:textColor="@color/black" - android:textSize="14sp" - /> + android:orientation="vertical" + tools:context=".main.MainActivity" + > - <androidx.recyclerview.widget.RecyclerView - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:listitem="@layout/item_permission_apps" - android:id="@+id/recylcer_view_permission_apps"/> - </LinearLayout> + <TextView + android:id="@+id/permission_control" + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:paddingTop="16dp" + android:textColor="@color/black" + android:textSize="14sp" + /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recylcer_view_permission_apps" + android:layout_height="match_parent" + android:layout_width="match_parent" + tools:listitem="@layout/item_app_toggle" + /> + </LinearLayout> + </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_permissions.xml b/app/src/main/res/layout/fragment_permissions.xml index a452570..7ca4b43 100644 --- a/app/src/main/res/layout/fragment_permissions.xml +++ b/app/src/main/res/layout/fragment_permissions.xml @@ -2,16 +2,22 @@ <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" android:background="@color/white" + android:layout_height="match_parent" + android:layout_width="match_parent" > - <include layout="@layout/topbar"/> + <include layout="@layout/topbar" /> + + <androidx.core.widget.NestedScrollView + android:layout_height="match_parent" + android:layout_width="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + > <LinearLayout - android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_width="match_parent" android:orientation="vertical" android:paddingLeft="32dp" android:paddingRight="32dp" @@ -20,9 +26,9 @@ <TextView android:id="@+id/permission_control" - android:layout_width="match_parent" - android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" android:paddingTop="16dp" android:text="@string/permission_control_info" android:textColor="@color/black" @@ -30,20 +36,22 @@ /> <TextView - android:id="@+id/learn_more_permissions" - android:layout_width="wrap_content" - android:layout_height="48dp" android:fontFamily="sans-serif-medium" android:gravity="center_vertical" + android:id="@+id/learn_more_permissions" + android:layout_height="48dp" + android:layout_width="wrap_content" android:text="@string/learn_more" android:textColor="#007fff" android:textSize="14sp" /> <androidx.recyclerview.widget.RecyclerView - android:layout_width="match_parent" + android:id="@+id/recylcer_view_permissions" android:layout_height="match_parent" + android:layout_width="match_parent" tools:listitem="@layout/item_permission" - android:id="@+id/recylcer_view_permissions"/> + /> </LinearLayout> + </androidx.core.widget.NestedScrollView> </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_quick_protection.xml b/app/src/main/res/layout/fragment_quick_protection.xml index d57a101..569f9b1 100644 --- a/app/src/main/res/layout/fragment_quick_protection.xml +++ b/app/src/main/res/layout/fragment_quick_protection.xml @@ -1,59 +1,70 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" android:background="@color/white" + android:layout_height="match_parent" + android:layout_width="match_parent" > - <include layout="@layout/topbar"/> + <include layout="@layout/topbar" /> - <LinearLayout - android:layout_width="match_parent" + <androidx.core.widget.NestedScrollView android:layout_height="match_parent" - android:orientation="vertical" - android:layout_marginBottom="56dp" - tools:context=".main.MainActivity" + android:layout_width="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior" > - <TextView - android:id="@+id/quick_protection_info" + <LinearLayout + android:layout_height="match_parent" + android:layout_marginBottom="56dp" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:paddingLeft="32dp" - android:paddingRight="32dp" - android:paddingTop="16dp" - android:text="@string/quick_protection_info" - android:textColor="@color/black" - android:textSize="14sp" - /> + android:orientation="vertical" + tools:context=".main.MainActivity" + > + + <TextView + android:id="@+id/quick_protection_info" + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:paddingTop="16dp" + android:text="@string/quick_protection_info" + android:textColor="@color/black" + android:textSize="14sp" + /> + + <TextView + android:fontFamily="sans-serif-medium" + android:id="@+id/quick_protection_settings_list" + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:paddingTop="16dp" + android:text="@string/quick_protection_settings_list" + android:textColor="@color/black" + android:textSize="14sp" + /> + </LinearLayout> + + </androidx.core.widget.NestedScrollView> - <TextView - android:id="@+id/quick_protection_settings_list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:paddingTop="16dp" - android:paddingBottom="16dp" - android:paddingLeft="32dp" - android:paddingRight="32dp" - android:fontFamily="sans-serif-medium" - android:text="@string/quick_protection_settings_list" - android:textColor="@color/black" - android:textSize="14sp" - /> - </LinearLayout> <TextView - android:layout_width="wrap_content" + android:fontFamily="sans-serif-medium" + android:gravity="center_vertical|end" + android:id="@+id/learn_more" + android:layout_gravity="bottom|end" android:layout_height="56dp" - android:paddingRight="32dp" + android:layout_width="wrap_content" android:paddingLeft="32dp" - android:gravity="center_vertical|right" - android:id="@+id/learn_more" + android:paddingRight="32dp" android:text="@string/learn_more" android:textColor="#007fff" android:textSize="14sp" - android:fontFamily="sans-serif-medium" - android:layout_gravity="bottom|right"/> + /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_tracker_apps.xml b/app/src/main/res/layout/fragment_tracker_apps.xml new file mode 100644 index 0000000..3341d95 --- /dev/null +++ b/app/src/main/res/layout/fragment_tracker_apps.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:background="@color/white" + android:layout_height="match_parent" + android:layout_width="match_parent" + > + + <include layout="@layout/topbar" /> + + <androidx.core.widget.NestedScrollView + android:layout_height="match_parent" + android:layout_width="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + > + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + tools:context=".main.MainActivity" + > + + <TextView + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="16dp" + android:paddingLeft="32dp" + android:paddingRight="32dp" + android:paddingTop="16dp" + android:text="@string/enable_disable_tracker_info" + android:textColor="@color/black" + android:textSize="14sp" + /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recylcer_view_tracker_apps" + android:layout_height="match_parent" + android:layout_width="match_parent" + tools:listitem="@layout/item_app_toggle" + /> + </LinearLayout> + </androidx.core.widget.NestedScrollView> +</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_trackers.xml b/app/src/main/res/layout/fragment_trackers.xml new file mode 100644 index 0000000..591b2b6 --- /dev/null +++ b/app/src/main/res/layout/fragment_trackers.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:background="@color/white" + android:layout_height="match_parent" + android:layout_width="match_parent" + > + + <include layout="@layout/topbar" /> + + <androidx.core.widget.NestedScrollView + android:layout_height="match_parent" + android:layout_marginBottom="32dp" + android:layout_width="match_parent" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + > + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:paddingLeft="32dp" + android:paddingRight="32dp" + tools:context=".main.MainActivity" + > + + <TextView + android:id="@+id/trackers_info" + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingTop="16dp" + android:text="@string/manage_trackers_info" + android:textColor="@color/black" + android:textSize="14sp" + /> + + <TextView + android:fontFamily="sans-serif-medium" + android:gravity="center_vertical" + android:id="@+id/learn_more_trackers_info" + android:layout_height="48dp" + android:layout_width="wrap_content" + android:text="@string/learn_more" + android:textColor="#007fff" + android:textSize="14sp" + /> + + <ImageView + android:layout_height="300dp" + android:layout_width="wrap_content" + android:scaleType="centerInside" + android:src="@drawable/dummy_trackers_usage" + /> + + <TextView + android:layout_gravity="center_horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingTop="16dp" + android:text="@string/following_trackers_in_use" + android:textColor="@color/black" + android:textSize="16sp" + /> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recylcer_view_trackers" + android:layout_height="match_parent" + android:layout_width="match_parent" + tools:listitem="@layout/item_list_tracker" + /> + </LinearLayout> + </androidx.core.widget.NestedScrollView> +</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/item_permission_apps.xml b/app/src/main/res/layout/item_app_toggle.xml index aec8fec..d0f565f 100644 --- a/app/src/main/res/layout/item_permission_apps.xml +++ b/app/src/main/res/layout/item_app_toggle.xml @@ -2,43 +2,43 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/item_permission_apps" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="16dp" + android:layout_width="match_parent" android:paddingBottom="16dp" android:paddingLeft="32dp" android:paddingRight="32dp" + android:paddingTop="16dp" > <ImageView android:id="@+id/app_icon" - android:layout_width="36dp" - android:layout_height="36dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" + android:layout_height="36dp" + android:layout_width="36dp" android:src="@drawable/ic_facebook" /> <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/app_title" - android:layout_width="match_parent" - android:layout_height="wrap_content" android:layout_centerVertical="true" - android:layout_toStartOf="@+id/togglePermission" + android:layout_height="wrap_content" android:layout_toEndOf="@+id/app_icon" - android:fontFamily="sans-serif-medium" - android:paddingStart="32dp" + android:layout_toStartOf="@+id/toggle" + android:layout_width="match_parent" android:paddingEnd="32dp" + android:paddingStart="32dp" android:textColor="@color/black" android:textSize="16sp" tools:text="Body sensor" /> <Switch - android:id="@+id/togglePermission" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:id="@+id/toggle" android:layout_alignParentEnd="true" android:layout_centerVertical="true" + android:layout_height="wrap_content" + android:layout_width="wrap_content" /> </RelativeLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/item_list_tracker.xml b/app/src/main/res/layout/item_list_tracker.xml new file mode 100644 index 0000000..1b5ecc2 --- /dev/null +++ b/app/src/main/res/layout/item_list_tracker.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ 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 <https://www.gnu.org/licenses/>. + --> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:fontFamily="sans-serif-medium" + android:id="@+id/tracker_title" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:textColor="@color/accent" + android:textSize="16sp" + tools:text="Google Crashlytics" + />
\ No newline at end of file diff --git a/app/src/main/res/layout/item_permission.xml b/app/src/main/res/layout/item_permission.xml index 8f54f64..66d4213 100644 --- a/app/src/main/res/layout/item_permission.xml +++ b/app/src/main/res/layout/item_permission.xml @@ -17,57 +17,57 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/item_permission" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="16dp" + android:layout_width="match_parent" android:paddingBottom="16dp" + android:paddingTop="16dp" > <ImageView android:id="@+id/permission_icon" - android:layout_width="24dp" - android:layout_height="24dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" + android:layout_height="24dp" + android:layout_width="24dp" android:src="@drawable/ic_body_monitor" /> <LinearLayout - android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_toStartOf="@+id/chevron" android:layout_toEndOf="@+id/permission_icon" + android:layout_toStartOf="@+id/chevron" + android:layout_width="match_parent" android:orientation="vertical" - android:paddingStart="32dp" android:paddingEnd="32dp" + android:paddingStart="32dp" > <TextView + android:fontFamily="sans-serif-medium" android:id="@+id/permission_title" - android:layout_width="match_parent" android:layout_height="wrap_content" - android:fontFamily="sans-serif-medium" - tools:text="Body sensor" + android:layout_width="match_parent" android:textColor="@color/black" android:textSize="16sp" + tools:text="Body sensor" /> <TextView android:id="@+id/permission_count" - android:layout_width="match_parent" android:layout_height="wrap_content" - tools:text="3 of 8 apps allowed" + android:layout_width="match_parent" android:textSize="14sp" + tools:text="3 of 8 apps allowed" /> </LinearLayout> <ImageView android:id="@+id/chevron" - android:layout_width="24dp" - android:layout_height="24dp" - android:padding="4dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" + android:layout_height="24dp" + android:layout_width="24dp" + android:padding="4dp" android:src="@drawable/ic_chevron_right_24dp" /> </RelativeLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/topbar.xml b/app/src/main/res/layout/topbar.xml index 9142d79..a493b3b 100644 --- a/app/src/main/res/layout/topbar.xml +++ b/app/src/main/res/layout/topbar.xml @@ -18,20 +18,20 @@ --> <com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_height="wrap_content" android:layout_width="match_parent" - xmlns:app="http://schemas.android.com/apk/res-auto" app:elevation="0dp" > <com.google.android.material.appbar.MaterialToolbar android:background="@color/white" - android:id="@+id/toolbar" - app:titleCentered="true" android:elevation="0dp" + android:id="@+id/toolbar" android:layout_height="?android:attr/actionBarSize" android:layout_width="match_parent" + app:titleCentered="true" tools:layout_height="56dp" /> </com.google.android.material.appbar.AppBarLayout> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f8c6127..4f45122 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -7,4 +7,6 @@ <color name="teal_700">#FF018786</color> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> + + <color name="accent">@lineageos.platform:color/color_default_accent</color> </resources>
\ 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 fd24223..3105ddb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,5 +42,10 @@ <string name="quick_protection">Quick Protection</string> <string name="privacy_dashboard">Privacy Dashboard</string> <string name="click_to_learn_more">Click to learn more</string> - <string name="apps_permissions">\"Apps Permission\"</string> + <string name="apps_permissions">Apps Permission</string> + <string name="trackers">Trackers</string> + <string name="manage_trackers_info">See trackers usage over time and manage trackers available in applications</string> + <string name="following_trackers_in_use">Following trackers are in use</string> + <string name="enable_disable_tracker_info">Enable or disable this tracker for the following apps</string> + <string name="tracker">Tracker</string> </resources>
\ No newline at end of file |