summaryrefslogtreecommitdiff
path: root/app/src/main/java/foundation/e
diff options
context:
space:
mode:
authorjacquarg <guillaume.jacquart@hoodbrains.com>2021-11-05 11:20:01 +0100
committerjacquarg <guillaume.jacquart@hoodbrains.com>2021-11-05 11:20:01 +0100
commit2d210ca863561ac68445e588d1405d9847716347 (patch)
tree2601b2a04c391d704c2473e629030b3c4c730636 /app/src/main/java/foundation/e
parenta484bf584f4163c8a0a1260e81d598fdec87ff3b (diff)
Embed trackerfilter aar, ui fixes.
Diffstat (limited to 'app/src/main/java/foundation/e')
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt20
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt54
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt12
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/dummy/TrackTrackersPrivacyMock.kt81
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt2
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt61
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt101
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt14
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt17
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt171
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt126
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt58
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt68
13 files changed, 592 insertions, 193 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
index 1ba235b..f36405d 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt
@@ -24,6 +24,7 @@ import foundation.e.privacycentralapp.data.repositories.LocalStateRepository
import foundation.e.privacycentralapp.domain.usecases.AppListUseCase
import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.privacycentralapp.domain.usecases.IpScramblingStateUseCase
+import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase
import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase
import foundation.e.privacycentralapp.dummy.TrackTrackersPrivacyMock
import foundation.e.privacycentralapp.features.dashboard.DashBoardViewModelFactory
@@ -31,12 +32,15 @@ import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyVi
import foundation.e.privacycentralapp.features.location.FakeLocationViewModelFactory
import foundation.e.privacycentralapp.features.location.LocationApiDelegate
import foundation.e.privacycentralapp.features.trackers.TrackersViewModelFactory
+import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersViewModelFactory
import foundation.e.privacymodules.ipscrambler.IpScramblerModule
import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule
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 foundation.e.trackerfilter.api.BlockTrackersPrivacyModule
+import foundation.e.trackerfilter.api.TrackTrackersPrivacyModule
import kotlinx.coroutines.GlobalScope
import lineageos.blockers.BlockerInterface
@@ -67,9 +71,13 @@ class DependencyContainer constructor(val app: Application) {
LocationApiDelegate(fakeLocationModule, permissionsModule, appDesc)
}
+ private val blockTrackersPrivacyModule by lazy { BlockTrackersPrivacyModule.getInstance(context) }
+
// Repositories
private val localStateRepository by lazy { LocalStateRepository(context) }
- private val trackTrackersPrivacyModule by lazy { TrackTrackersPrivacyMock() }
+ private val trackTrackersPrivacyModule by lazy { TrackTrackersPrivacyModule.getInstance(context) }
+
+ private val trackersPrivacyMock by lazy { TrackTrackersPrivacyMock() }
// Usecases
private val getQuickPrivacyStateUseCase by lazy {
GetQuickPrivacyStateUseCase(localStateRepository)
@@ -81,7 +89,11 @@ class DependencyContainer constructor(val app: Application) {
AppListUseCase(permissionsModule)
}
private val trackersStatisticsUseCase by lazy {
- TrackersStatisticsUseCase(trackTrackersPrivacyModule)
+ TrackersStatisticsUseCase(trackersPrivacyMock)
+ }
+
+ private val trackersStateUseCase by lazy {
+ TrackersStateUseCase(trackersPrivacyMock, trackersPrivacyMock, permissionsModule)
}
// ViewModelFactories
@@ -102,4 +114,8 @@ class DependencyContainer constructor(val app: Application) {
val trackersViewModelFactory by lazy {
TrackersViewModelFactory(getQuickPrivacyStateUseCase, trackersStatisticsUseCase, appListUseCase)
}
+
+ val appTrackersViewModelFactory by lazy {
+ AppTrackersViewModelFactory(trackersStateUseCase)
+ }
}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
new file mode 100644
index 0000000..cabe6a1
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStateUseCase.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.domain.usecases
+
+import foundation.e.privacymodules.permissions.PermissionsPrivacyModule
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule
+import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule
+import foundation.e.privacymodules.trackers.Tracker
+
+class TrackersStateUseCase(
+ private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule,
+ private val trackersPrivacyModule: ITrackTrackersPrivacyModule,
+ private val permissionsPrivacyModule: PermissionsPrivacyModule
+) {
+ fun getApplicationPermission(packageName: String): ApplicationDescription {
+ return permissionsPrivacyModule.getApplicationDescription(packageName)
+ }
+
+ fun getTrackers(appUid: Int): List<Tracker> {
+ return trackersPrivacyModule.getTrackersForApp(appUid)
+ }
+
+ fun isWhitelisted(appUid: Int): Boolean {
+ return blockTrackersPrivacyModule.isWhitelisted(appUid)
+ }
+
+ fun getTrackersWhitelistIds(appUid: Int): List<Int> {
+ return blockTrackersPrivacyModule.getWhiteList(appUid).map { it.id }
+ }
+
+ fun toggleAppWhitelist(appUid: Int, isWhitelisted: Boolean) {
+ blockTrackersPrivacyModule.setWhiteListed(appUid, isWhitelisted)
+ }
+
+ fun blockTracker(appUid: Int, tracker: Tracker, isBlocked: Boolean) {
+ blockTrackersPrivacyModule.setWhiteListed(tracker, appUid, !isBlocked)
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
index 33c3f64..dc0b92b 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt
@@ -17,19 +17,19 @@
package foundation.e.privacycentralapp.domain.usecases
-import foundation.e.privacycentralapp.dummy.TrackTrackersPrivacyMock
+import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule
class TrackersStatisticsUseCase(
- private val trackTrackersPrivacyModule: TrackTrackersPrivacyMock
+ private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule
) {
- fun getPast24HoursTrackersCalls(): List<Int> {
- return trackTrackersPrivacyModule.getPast24HoursTrackersCalls()
+ fun getPastDayTrackersCalls(): List<Int> {
+ return trackTrackersPrivacyModule.getPastDayTrackersCalls()
}
fun getDayMonthYearStatistics(): Triple<List<Int>, List<Int>, List<Int>> {
return Triple(
- trackTrackersPrivacyModule.getPast24HoursTrackersCalls(),
+ trackTrackersPrivacyModule.getPastDayTrackersCalls(),
trackTrackersPrivacyModule.getPastMonthTrackersCalls(),
trackTrackersPrivacyModule.getPastYearTrackersCalls()
)
@@ -37,7 +37,7 @@ class TrackersStatisticsUseCase(
fun getDayMonthYearCounts(): Triple<Int, Int, Int> {
return Triple(
- trackTrackersPrivacyModule.getPast24HoursTrackersCount(),
+ trackTrackersPrivacyModule.getPastDayTrackersCount(),
trackTrackersPrivacyModule.getPastMonthTrackersCount(),
trackTrackersPrivacyModule.getPastYearTrackersCount()
)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackTrackersPrivacyMock.kt b/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackTrackersPrivacyMock.kt
index 55ca6ec..5b1eb9e 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackTrackersPrivacyMock.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/dummy/TrackTrackersPrivacyMock.kt
@@ -17,11 +17,20 @@
package foundation.e.privacycentralapp.dummy
+import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule
import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule
import foundation.e.privacymodules.trackers.Tracker
-class TrackTrackersPrivacyMock : ITrackTrackersPrivacyModule {
- override fun getPast24HoursTrackersCalls(): List<Int> {
+class TrackTrackersPrivacyMock :
+ ITrackTrackersPrivacyModule,
+ IBlockTrackersPrivacyModule {
+
+ private val trackers = listOf(
+ Tracker(1, "Crashlytics", null),
+ Tracker(2, label = "Facebook", null)
+ )
+
+ override fun getPastDayTrackersCalls(): List<Int> {
return listOf(
2000, 2300, 130, 2500, 1000, 2000,
2000, 2300, 130, 2500, 1000, 2000,
@@ -30,7 +39,7 @@ class TrackTrackersPrivacyMock : ITrackTrackersPrivacyModule {
)
}
- override fun getPast24HoursTrackersCount(): Int {
+ override fun getPastDayTrackersCount(): Int {
return 30
}
@@ -64,9 +73,67 @@ class TrackTrackersPrivacyMock : ITrackTrackersPrivacyModule {
}
override fun getTrackersForApp(appUid: Int): List<Tracker> {
- return listOf(
- Tracker("Crashlytics", null),
- Tracker(label = "Facebook", null)
- )
+ return trackers
+ }
+
+ private var isBlockingEnabled = false
+ private val appWhitelist = mutableSetOf<Int>()
+ private val trackersWhitelist = mutableMapOf<Int, MutableSet<Tracker>>()
+
+ override fun addListener(listener: IBlockTrackersPrivacyModule.Listener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun removeListener(listener: IBlockTrackersPrivacyModule.Listener) {
+ TODO("Not yet implemented")
+ }
+
+ override fun clearListeners() {
+ TODO("Not yet implemented")
+ }
+
+ override fun disableBlocking() {}
+
+ override fun enableBlocking() {}
+
+ override fun getWhiteList(appUid: Int): List<Tracker> {
+ return trackersWhitelist[appUid]?.toList() ?: emptyList()
+ }
+
+ override fun getWhiteListedApp(): List<Int> {
+ return appWhitelist.toList()
+ }
+
+ override fun isBlockingEnabled(): Boolean {
+ return isBlockingEnabled
+ }
+
+ override fun isWhiteListEmpty(): Boolean {
+ return appWhitelist.isEmpty() &&
+ (trackersWhitelist.isEmpty() || trackersWhitelist.values.all { it.isEmpty() })
+ }
+
+ override fun isWhitelisted(appUid: Int): Boolean {
+ return appUid in appWhitelist
+ }
+
+ override fun setWhiteListed(tracker: Tracker, appUid: Int, isWhiteListed: Boolean) {
+ if (appUid !in trackersWhitelist) {
+ trackersWhitelist[appUid] = mutableSetOf<Tracker>()
+ }
+
+ if (isWhiteListed) {
+ trackersWhitelist[appUid]?.add(tracker)
+ } else {
+ trackersWhitelist[appUid]?.remove(tracker)
+ }
+ }
+
+ override fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) {
+ if (isWhiteListed) {
+ appWhitelist.add(appUid)
+ } else {
+ appWhitelist.remove(appUid)
+ }
}
}
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 d38d4f6..a6ac87a 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
@@ -190,7 +190,7 @@ class DashboardFeature(
Effect.IpScramblingModeUpdatedEffect(it)
},
flow {
- emit(Effect.TrackersStatisticsUpdatedEffect(trackersStatisticsUseCase.getPast24HoursTrackersCalls()))
+ emit(Effect.TrackersStatisticsUpdatedEffect(trackersStatisticsUseCase.getPastDayTrackersCalls()))
}
)
/*
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
deleted file mode 100644
index ae236b9..0000000
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsAdapter.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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
deleted file mode 100644
index fff24dc..0000000
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackerAppsFragment.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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/TrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt
index 0394abb..0120fae 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt
@@ -64,7 +64,7 @@ class TrackersFeature(
sealed class SingleEvent {
data class ErrorEvent(val error: String) : SingleEvent()
- data class OpenAppDetailsEvent(val packageName: String) : SingleEvent()
+ data class OpenAppDetailsEvent(val appDesc: ApplicationDescription) : SingleEvent()
object BlockerErrorEvent : SingleEvent()
}
@@ -93,7 +93,7 @@ class TrackersFeature(
data class AvailableAppsListEffect(
val apps: List<ApplicationDescription>
) : Effect()
- data class OpenAppDetailsEffect(val packageName: String) : Effect()
+ data class OpenAppDetailsEffect(val appDesc: ApplicationDescription) : Effect()
object QuickPrivacyDisabledWarningEffect : Effect()
data class TrackersLoadedEffect(val trackers: List<Tracker>) : Effect()
data class TrackerSelectedEffect(val tracker: Tracker) : Effect()
@@ -159,9 +159,11 @@ class TrackersFeature(
)
is Action.ClickAppAction -> flowOf(
- if (getPrivacyStateUseCase.isQuickPrivacyEnabled)
- Effect.OpenAppDetailsEffect(action.packageName)
- else Effect.QuickPrivacyDisabledWarningEffect
+ if (getPrivacyStateUseCase.isQuickPrivacyEnabled) {
+ state.apps?.find { it.packageName == action.packageName }?.let {
+ Effect.OpenAppDetailsEffect(it)
+ } ?: run { Effect.ErrorEffect("Can't find back app.") }
+ } else Effect.QuickPrivacyDisabledWarningEffect
)
Action.ObserveTrackers -> TrackersDataSource.trackers.map {
Effect.TrackersLoadedEffect(
@@ -202,7 +204,7 @@ class TrackersFeature(
singleEventProducer = { _, _, effect ->
when (effect) {
is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
- is Effect.OpenAppDetailsEffect -> SingleEvent.OpenAppDetailsEvent(effect.packageName)
+ is Effect.OpenAppDetailsEffect -> SingleEvent.OpenAppDetailsEvent(effect.appDesc)
is Effect.TrackerToggleEffect -> {
if (!effect.result) 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 441f39a..a259f0b 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
@@ -21,6 +21,8 @@ import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.content.ContextCompat
+import androidx.fragment.app.add
+import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
@@ -36,6 +38,7 @@ import foundation.e.privacycentralapp.common.NavToolbarFragment
import foundation.e.privacycentralapp.databinding.FragmentTrackersBinding
import foundation.e.privacycentralapp.databinding.TrackersItemGraphBinding
import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf
+import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFragment
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
@@ -65,7 +68,11 @@ class TrackersFragment :
displayToast(event.error)
}
is TrackersFeature.SingleEvent.OpenAppDetailsEvent -> {
- displayToast(event.packageName)
+ requireActivity().supportFragmentManager.commit {
+ add<AppTrackersFragment>(R.id.container, args = AppTrackersFragment.buildArgs(event.appDesc.label.toString(), event.appDesc.packageName))
+ setReorderingAllowed(true)
+ addToBackStack("apptrackers")
+ }
}
}
}
@@ -108,14 +115,6 @@ class TrackersFragment :
)
}
}
-
- //
- // requireActivity().supportFragmentManager.commit {
- // val bundle = bundleOf("TRACKER" to it.name)
- // add<TrackerAppsFragment>(R.id.container, args = bundle)
- // setReorderingAllowed(true)
- // addToBackStack("trackers")
- // }
}
override fun getTitle() = getString(R.string.trackers_title)
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt
new file mode 100644
index 0000000..a62ed16
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.apptrackers
+
+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.domain.usecases.TrackersStateUseCase
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
+import foundation.e.privacymodules.trackers.Tracker
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+// Define a state machine for Tracker feature.
+class AppTrackersFeature(
+ initialState: State,
+ coroutineScope: CoroutineScope,
+ reducer: Reducer<State, Effect>,
+ actor: Actor<State, Action, Effect>,
+ singleEventProducer: SingleEventProducer<State, Action, Effect, SingleEvent>
+) : BaseFeature<AppTrackersFeature.State, AppTrackersFeature.Action, AppTrackersFeature.Effect, AppTrackersFeature.SingleEvent>(
+ initialState,
+ actor,
+ reducer,
+ coroutineScope,
+ { message -> Log.d("TrackersFeature", message) },
+ singleEventProducer
+) {
+ data class State(
+ val appDesc: ApplicationDescription? = null,
+ val isBlockingActivated: Boolean = false,
+ val trackers: List<Tracker>? = null,
+ val whitelist: List<Int>? = null
+ ) {
+ fun getTrackersStatus(): List<Pair<Tracker, Boolean>>? {
+ if (trackers != null && whitelist != null) {
+ return trackers.map { it to (it.id !in whitelist) }
+ } else {
+ return null
+ }
+ }
+ }
+
+ sealed class SingleEvent {
+ data class ErrorEvent(val error: String) : SingleEvent()
+ }
+
+ sealed class Action {
+ data class InitAction(val packageName: String) : Action()
+ data class BlockAllToggleAction(val isBlocked: Boolean) : Action()
+ data class ToggleTrackerAction(val tracker: Tracker, val isBlocked: Boolean) : Action()
+ }
+
+ sealed class Effect {
+ data class SetAppEffect(val appDesc: ApplicationDescription) : Effect()
+ data class AppTrackersBlockingActivatedEffect(val isBlockingActivated: Boolean) : Effect()
+ data class AvailableTrackersListEffect(
+ val isBlockingActivated: Boolean,
+ val trackers: List<Tracker>,
+ val whitelist: List<Int>
+ ) : Effect()
+ data class TrackersWhitelistUpdateEffect(val whitelist: List<Int>) : Effect()
+
+ // object QuickPrivacyDisabledWarningEffect : Effect()
+ data class ErrorEffect(val message: String) : Effect()
+ }
+
+ companion object {
+ fun create(
+ initialState: State = State(),
+ coroutineScope: CoroutineScope,
+ trackersStateUseCase: TrackersStateUseCase
+ ) = AppTrackersFeature(
+ initialState, coroutineScope,
+ reducer = { state, effect ->
+ when (effect) {
+ is Effect.SetAppEffect -> state.copy(appDesc = effect.appDesc)
+ is Effect.AvailableTrackersListEffect -> state.copy(
+ isBlockingActivated = effect.isBlockingActivated,
+ trackers = effect.trackers,
+ whitelist = effect.whitelist
+ )
+
+ is Effect.AppTrackersBlockingActivatedEffect ->
+ state.copy(isBlockingActivated = effect.isBlockingActivated)
+
+ is Effect.TrackersWhitelistUpdateEffect ->
+ state.copy(whitelist = effect.whitelist)
+ is Effect.ErrorEffect -> state
+ }
+ },
+ actor = { state, action ->
+ when (action) {
+ is Action.InitAction -> merge(
+ flow {
+ val appDesc =
+ trackersStateUseCase.getApplicationPermission(action.packageName)
+ emit(Effect.SetAppEffect(appDesc))
+
+ emit(
+ Effect.AvailableTrackersListEffect(
+ isBlockingActivated = !trackersStateUseCase.isWhitelisted(
+ appDesc.uid
+ ),
+ trackers = trackersStateUseCase.getTrackers(appDesc.uid),
+ whitelist = trackersStateUseCase.getTrackersWhitelistIds(appDesc.uid)
+ )
+ )
+ }
+ )
+ is Action.BlockAllToggleAction ->
+ state.appDesc?.uid?.let { appUid ->
+ flow {
+ trackersStateUseCase.toggleAppWhitelist(appUid, !action.isBlocked)
+
+ emit(
+ Effect.AppTrackersBlockingActivatedEffect(
+ !trackersStateUseCase.isWhitelisted(
+ appUid
+ )
+ )
+ )
+ }
+ } ?: run { flowOf(Effect.ErrorEffect("No appDesc.")) }
+ is Action.ToggleTrackerAction -> {
+ state.appDesc?.uid?.let { appUid ->
+ flow {
+ trackersStateUseCase.blockTracker(
+ appUid,
+ action.tracker,
+ action.isBlocked
+ )
+ emit(
+ Effect.TrackersWhitelistUpdateEffect(
+ trackersStateUseCase.getTrackersWhitelistIds(appUid)
+ )
+ )
+ }
+ } ?: run { flowOf(Effect.ErrorEffect("No appDesc.")) }
+ }
+ }
+ },
+ singleEventProducer = { _, _, effect ->
+ when (effect) {
+ is Effect.ErrorEffect -> SingleEvent.ErrorEvent(effect.message)
+ else -> null
+ }
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt
new file mode 100644
index 0000000..508aa5a
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.apptrackers
+
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.core.os.bundleOf
+import androidx.core.view.isVisible
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.recyclerview.widget.LinearLayoutManager
+import foundation.e.flowmvi.MVIView
+import foundation.e.privacycentralapp.DependencyContainer
+import foundation.e.privacycentralapp.PrivacyCentralApplication
+import foundation.e.privacycentralapp.R
+import foundation.e.privacycentralapp.common.NavToolbarFragment
+import foundation.e.privacycentralapp.databinding.ApptrackersFragmentBinding
+import foundation.e.privacycentralapp.extensions.viewModelProviderFactoryOf
+import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFeature.Action
+import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFeature.SingleEvent
+import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFeature.State
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+
+class AppTrackersFragment :
+ NavToolbarFragment(R.layout.apptrackers_fragment),
+ MVIView<State, Action> {
+ companion object {
+ private val PARAM_LABEL = "PARAM_LABEL"
+ private val PARAM_PACKAGE_NAME = "PARAM_PACKAGE_NAME"
+ fun buildArgs(label: String, packageName: String): Bundle = bundleOf(
+ PARAM_LABEL to label,
+ PARAM_PACKAGE_NAME to packageName
+ )
+ }
+
+ private val dependencyContainer: DependencyContainer by lazy {
+ (this.requireActivity().application as PrivacyCentralApplication).dependencyContainer
+ }
+
+ private val viewModel: AppTrackersViewModel by viewModels {
+ viewModelProviderFactoryOf {
+ dependencyContainer.appTrackersViewModelFactory.create()
+ }
+ }
+
+ private lateinit var binding: ApptrackersFragmentBinding
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycleScope.launchWhenStarted {
+ viewModel.feature.takeView(this, this@AppTrackersFragment)
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.feature.singleEvents.collect { event ->
+ when (event) {
+ is SingleEvent.ErrorEvent -> displayToast(event.error)
+ }
+ }
+ }
+ lifecycleScope.launchWhenStarted {
+ viewModel.submitAction(
+ Action.InitAction(requireArguments().getString(PARAM_PACKAGE_NAME))
+ )
+ }
+ }
+
+ private fun displayToast(message: String) {
+ Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT)
+ .show()
+ }
+
+ override fun getTitle(): String = requireArguments().getString(PARAM_LABEL)
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ binding = ApptrackersFragmentBinding.bind(view)
+
+ // binding.blockAllToggle.setOnCheckedChangeListener { _, isChecked ->
+ // viewModel.submitAction(Action.BlockAllToggleAction(isChecked))
+ // }
+
+ binding.trackers.apply {
+ layoutManager = LinearLayoutManager(requireContext())
+ setHasFixedSize(true)
+ adapter = ToggleTrackersAdapter(R.layout.apptrackers_item_tracker_toggle) { tracker, isBlocked ->
+ viewModel.submitAction(
+ Action.ToggleTrackerAction(
+ tracker,
+ isBlocked
+ )
+ )
+ }
+ }
+ }
+
+ override fun render(state: AppTrackersFeature.State) {
+ // binding.blockAllToggle.isChecked = state.isBlockingActivated
+
+ state.getTrackersStatus()?.let {
+ binding.trackers.isVisible = true
+ binding.trackers.post {
+ (binding.trackers.adapter as ToggleTrackersAdapter?)?.dataSet = it
+ }
+ binding.noTrackersYet.isVisible = false
+ }
+ }
+
+ override fun actions(): Flow<Action> = viewModel.actions
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt
new file mode 100644
index 0000000..8acbcac
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersViewModel.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.apptrackers
+
+import android.util.Log
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import foundation.e.privacycentralapp.common.Factory
+import foundation.e.privacycentralapp.domain.usecases.TrackersStateUseCase
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.launch
+
+class AppTrackersViewModel(
+ private val trackersStateUseCase: TrackersStateUseCase
+) : ViewModel() {
+
+ private val _actions = MutableSharedFlow<AppTrackersFeature.Action>()
+ val actions = _actions.asSharedFlow()
+
+ val feature: AppTrackersFeature by lazy {
+ AppTrackersFeature.create(
+ coroutineScope = viewModelScope,
+ trackersStateUseCase = trackersStateUseCase
+ )
+ }
+
+ fun submitAction(action: AppTrackersFeature.Action) {
+ Log.d("TrackersViewModel", "submitting action")
+ viewModelScope.launch {
+ _actions.emit(action)
+ }
+ }
+}
+
+class AppTrackersViewModelFactory(
+ private val trackersStateUseCase: TrackersStateUseCase
+) :
+ Factory<AppTrackersViewModel> {
+ override fun create(): AppTrackersViewModel {
+ return AppTrackersViewModel(trackersStateUseCase)
+ }
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt
new file mode 100644
index 0000000..f23ebf5
--- /dev/null
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.apptrackers
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.widget.SwitchCompat
+import androidx.recyclerview.widget.RecyclerView
+import foundation.e.privacycentralapp.R
+import foundation.e.privacymodules.trackers.Tracker
+
+class ToggleTrackersAdapter(
+ private val itemsLayout: Int,
+ private val listener: (Tracker, Boolean) -> Unit
+) :
+ RecyclerView.Adapter<ToggleTrackersAdapter.ViewHolder>() {
+
+ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+ val title: TextView = view.findViewById(R.id.title)
+
+ val toggle: SwitchCompat = view.findViewById(R.id.toggle)
+
+ fun bind(item: Pair<Tracker, Boolean>) {
+ title.text = item.first.label
+ toggle.isChecked = item.second
+ }
+ }
+
+ var dataSet: List<Pair<Tracker, Boolean>> = emptyList()
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val view = LayoutInflater.from(parent.context)
+ .inflate(itemsLayout, parent, false)
+ val holder = ViewHolder(view)
+ holder.toggle.setOnCheckedChangeListener { _, isChecked ->
+ listener(dataSet[holder.adapterPosition].first, isChecked)
+ }
+ return holder
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val permission = dataSet[position]
+ holder.bind(permission)
+ }
+
+ override fun getItemCount(): Int = dataSet.size
+}