summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorAmit Kumar <amitkma@e.email>2021-06-16 00:33:46 +0530
committerAmit Kumar <amitkma@e.email>2021-06-16 00:33:46 +0530
commitf4fe8a4d881deef7e43ffe296df804c8c2c7a657 (patch)
treee7b74935a6f86aa123804804c537eb1390c1d48a /app/src/main
parentffbb686bd571185217c0e9e0fef0f164be9a07f7 (diff)
Add tracker UI and integrate lineage blocker service
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt3
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/PrivacyCentralApplication.kt8
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/ToolbarFragment.kt2
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt1
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/TrackersDataSource.kt93
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt23
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFeature.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/permissions/PermissionAppsAdapter.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt59
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt78
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersAdapter.kt56
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt121
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt63
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt40
-rw-r--r--app/src/main/res/drawable/dummy_trackers_usage.pngbin0 -> 16635 bytes
-rw-r--r--app/src/main/res/layout/fragment_permission_apps.xml2
-rw-r--r--app/src/main/res/layout/fragment_tracker_apps.xml38
-rw-r--r--app/src/main/res/layout/fragment_trackers.xml37
-rw-r--r--app/src/main/res/layout/item_app_toggle.xml (renamed from app/src/main/res/layout/item_permission_apps.xml)4
-rw-r--r--app/src/main/res/layout/item_list_tracker.xml29
-rw-r--r--app/src/main/res/values/colors.xml2
-rw-r--r--app/src/main/res/values/strings.xml5
22 files changed, 638 insertions, 38 deletions
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/DummyDataSource.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
index fe61354..dd6112d 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/DummyDataSource.kt
@@ -227,4 +227,5 @@ object DummyDataSource {
_appsUsingLocationPerm.value = _populatedPermissions.value[permissionId].packagesAllowed
}
}
+
}
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..9485a26
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackersDataSource.kt
@@ -0,0 +1,93 @@
+/*
+ * 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 (Demo)",
+ domain = "google.com",
+ trackedApps = listOf(facebook, firefox)
+ )
+
+ val facebookAds = Tracker(
+ "Facebook (Demo)",
+ domain = "google.com",
+ trackedApps = listOf(facebook, whatsapp)
+ )
+ val rubiconTracker = Tracker(
+ "Rubicon Projects",
+ domain = "google.com",
+ trackedApps = listOf(google, blisslauncher, youtube)
+ )
+ val googleAnalytics = Tracker(
+ "Google Analytics",
+ domain = "google.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 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
+ }
+} \ No newline at end of file
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 dc02c91..a273b88 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
@@ -93,6 +94,7 @@ 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()
@@ -129,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)
@@ -153,8 +160,20 @@ class DashboardFeature(
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)
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..04e3f04
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.setOnCheckedChangeListener { _, isChecked ->
+ listener(tracker, 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..67ae0cc
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.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()
+
+ 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)
+ }
+ }
+ }
+ }
+
+ private fun displayToast(message: String) {
+ Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
+ .show()
+ }
+
+ override fun getTitle(): String = getString(R.string.tracker)
+
+ override fun render(state: TrackersFeature.State) {
+ state.currentSelectedTracker?.let { tracker ->
+ view?.findViewById<RecyclerView>(R.id.recylcer_view_tracker_apps)?.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ setHasFixedSize(true)
+ adapter = TrackerAppsAdapter(tracker) { tracker, grant ->
+ viewModel.submitAction(
+ TrackersFeature.Action.ToggleTrackerAction(
+ tracker,
+ 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..f9fbf63
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt
@@ -0,0 +1,121 @@
+/*
+ * 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("PermissionsFeature", 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()
+ }
+
+ 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()
+ }
+
+ 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
+ }
+ },
+ 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(
+ state.currentSelectedTracker,
+ action.grant
+ )
+ flowOf(Effect.TrackerToggleEffect(result))
+ } else {
+ flowOf(Effect.ErrorEffect("Can't toggle tracker"))
+ }
+ }
+ }
+ },
+ singleEventProducer = { _, _, effect ->
+ when (effect) {
+ is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
+ is Effect.TrackerToggleEffect -> SingleEvent.BlockerErrorEvent
+ else -> null
+ }
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
index d4085b4..d0242d3 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt
@@ -17,42 +17,63 @@
package foundation.e.privacycentralapp.features.trackers
-import android.content.Context
import android.os.Bundle
import android.view.View
-import android.widget.Button
-import android.widget.Toast
+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 lineageos.blockers.BlockerInterface
+import kotlinx.coroutines.flow.Flow
class TrackersFragment :
- NavToolbarFragment(R.layout.fragment_trackers) {
+ NavToolbarFragment(R.layout.fragment_trackers),
+ MVIView<TrackersFeature.State, TrackersFeature.Action> {
- private lateinit var blockerService: BlockerInterface
+ private val viewModel: TrackersViewModel by viewModels()
+ private lateinit var trackersAdapter: TrackersAdapter
- override fun onAttach(context: Context) {
- super.onAttach(context)
- blockerService = BlockerInterface.getInstance(context)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.trackersFeature.takeView(this, this@TrackersFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(TrackersFeature.Action.ObserveTrackers)
+ }
}
- private fun displayToast(message: String) {
- Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
- .show()
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ trackersAdapter = TrackersAdapter {
+ 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(): String {
- return "Trackers"
+ return getString(R.string.trackers)
}
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
- bindClickListeners(view)
- }
-
- private fun bindClickListeners(view: View) {
- view.findViewById<Button>(R.id.triggerBlockerService).setOnClickListener {
- blockerService.runTest()
+ override fun render(state: TrackersFeature.State) {
+ if (state.currentSelectedTracker != null) {
+ requireActivity().supportFragmentManager.commit {
+ add<TrackerAppsFragment>(R.id.container)
+ setReorderingAllowed(true)
+ addToBackStack("trackers")
+ }
+ } else {
+ 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..79ae146
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt
@@ -0,0 +1,40 @@
+/*
+ * 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 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) {
+ viewModelScope.launch {
+ _actions.emit(action)
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/res/drawable/dummy_trackers_usage.png b/app/src/main/res/drawable/dummy_trackers_usage.png
new file mode 100644
index 0000000..9b7e090
--- /dev/null
+++ b/app/src/main/res/drawable/dummy_trackers_usage.png
Binary files differ
diff --git a/app/src/main/res/layout/fragment_permission_apps.xml b/app/src/main/res/layout/fragment_permission_apps.xml
index 65f4169..760c891 100644
--- a/app/src/main/res/layout/fragment_permission_apps.xml
+++ b/app/src/main/res/layout/fragment_permission_apps.xml
@@ -32,7 +32,7 @@
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:listitem="@layout/item_permission_apps"
+ tools:listitem="@layout/item_app_toggle"
android:id="@+id/recylcer_view_permission_apps"/>
</LinearLayout>
</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..bb31b5b
--- /dev/null
+++ b/app/src/main/res/layout/fragment_tracker_apps.xml
@@ -0,0 +1,38 @@
+<?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"
+ >
+
+ <include layout="@layout/topbar"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="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:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingLeft="32dp"
+ android:paddingRight="32dp"
+ android:text="@string/enable_disable_tracker_info"
+ android:textColor="@color/black"
+ android:textSize="14sp"
+ />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/item_app_toggle"
+ android:id="@+id/recylcer_view_tracker_apps"/>
+ </LinearLayout>
+</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
index e11f4cc..13fcab3 100644
--- a/app/src/main/res/layout/fragment_trackers.xml
+++ b/app/src/main/res/layout/fragment_trackers.xml
@@ -26,21 +26,48 @@
>
<TextView
- android:id="@+id/internet_activity_privacy_info"
+ android:id="@+id/trackers_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingTop="16dp"
- android:text="@string/internet_activity_privacy_info"
+ android:text="@string/manage_trackers_info"
android:textColor="@color/black"
android:textSize="14sp"
/>
- <Button android:id="@+id/triggerBlockerService"
- android:layout_height="wrap_content"
+ <TextView
+ android:id="@+id/learn_more_trackers_info"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="center_vertical"
+ android:text="@string/learn_more"
+ android:textColor="#007fff"
+ android:textSize="14sp"
+ />
+
+ <ImageView
+ android:layout_height="300dp"
android:layout_width="wrap_content"
- android:text="Trigger Blocker"/>
+ android:scaleType="centerInside"
+ android:src="@drawable/dummy_trackers_usage"
/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:paddingTop="16dp"
+ android:text="@string/following_trackers_in_use"
+ android:textColor="@color/black"
+ android:textSize="16sp"
+ />
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/item_list_tracker"
+ android:id="@+id/recylcer_view_trackers"/>
</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..7d02ec0 100644
--- a/app/src/main/res/layout/item_permission_apps.xml
+++ b/app/src/main/res/layout/item_app_toggle.xml
@@ -24,7 +24,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
- android:layout_toStartOf="@+id/togglePermission"
+ android:layout_toStartOf="@+id/toggle"
android:layout_toEndOf="@+id/app_icon"
android:fontFamily="sans-serif-medium"
android:paddingStart="32dp"
@@ -35,7 +35,7 @@
/>
<Switch
- android:id="@+id/togglePermission"
+ android:id="@+id/toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
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/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..eb799d1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -43,4 +43,9 @@
<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="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