diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2113b3a3f0e30837a5b1aeef330c2175cf10a2b4
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 MURENA SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package foundation.e.advancedprivacy.domain.entities
+
+import foundation.e.advancedprivacy.features.trackers.AppWithTrackersCount
+import foundation.e.advancedprivacy.features.trackers.TrackerWithAppsCount
+
+data class TrackersAndAppsLists(
+ val trackers: List,
+ val allApps: List,
+ val appsWithTrackers: List
+)
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt
index 8292a6d1d9cd85913699926faf3728cbaf2f3742..ea07e8fa10a7f5d909a5601d490bcbe0fb9bbcf6 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt
@@ -18,6 +18,7 @@ package foundation.e.advancedprivacy.domain.usecases
import foundation.e.advancedprivacy.data.repositories.AppListsRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
+import foundation.e.advancedprivacy.domain.entities.TrackersAndAppsLists
import foundation.e.advancedprivacy.features.trackers.AppWithTrackersCount
import foundation.e.advancedprivacy.features.trackers.TrackerWithAppsCount
import foundation.e.advancedprivacy.trackers.data.StatsDatabase
@@ -31,14 +32,16 @@ class TrackersAndAppsListsUseCase(
private val appListsRepository: AppListsRepository,
) {
- suspend fun getAppsAndTrackersCounts(): Pair, List> {
+ suspend fun getTrackersAndAppsLists(): TrackersAndAppsLists {
val trackersAndAppsIds = statsDatabase.getDistinctTrackerAndApp()
val trackersAndApps = mapIdsToEntities(trackersAndAppsIds)
val (countByApp, countByTracker) = foldToCountByEntityMaps(trackersAndApps)
- val appList = buildAppList(countByApp)
- val trackerList = buildTrackerList(countByTracker)
- return appList to trackerList
+ return TrackersAndAppsLists(
+ trackers = buildTrackerList(countByTracker),
+ allApps = buildAllAppList(countByApp),
+ appsWithTrackers = buildAppList(countByApp)
+ )
}
private fun buildTrackerList(countByTracker: Map): List {
@@ -47,12 +50,18 @@ class TrackersAndAppsListsUseCase(
}.sortedByDescending { it.appsCount }
}
- private suspend fun buildAppList(countByApp: Map): List {
+ private suspend fun buildAllAppList(countByApp: Map): List {
return appListsRepository.apps().first().map { app: ApplicationDescription ->
AppWithTrackersCount(app = app, trackersCount = countByApp[app] ?: 0)
}.sortedByDescending { it.trackersCount }
}
+ private fun buildAppList(countByApp: Map): List {
+ return countByApp.map { (app, count) ->
+ AppWithTrackersCount(app = app, trackersCount = count)
+ }.sortedByDescending { it.trackersCount }
+ }
+
private suspend fun mapIdsToEntities(trackersAndAppsIds: List>): List> {
return trackersAndAppsIds.mapNotNull { (trackerId, apId) ->
trackersRepository.getTracker(trackerId)?.let { tracker ->
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/ListsTabPagerAdapter.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/ListsTabPagerAdapter.kt
index 242041003892acfa6376d2513a78c26e8823164e..e9a046f73ea0c2d77216987c3ff96a9303284ed9 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/ListsTabPagerAdapter.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/ListsTabPagerAdapter.kt
@@ -26,6 +26,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.divider.MaterialDividerItemDecoration
import foundation.e.advancedprivacy.R
+import foundation.e.advancedprivacy.databinding.TrackersAppsListBinding
import foundation.e.advancedprivacy.databinding.TrackersListBinding
const val TAB_APPS = 0
@@ -35,25 +36,29 @@ class ListsTabPagerAdapter(
private val context: Context,
private val viewModel: TrackersViewModel,
) : RecyclerView.Adapter() {
- private var apps: List = emptyList()
- private var trackers: List = emptyList()
+ private var uiState: TrackersState = TrackersState()
- fun updateDataSet(apps: List?, trackers: List?) {
- this.apps = apps ?: emptyList()
- this.trackers = trackers ?: emptyList()
+ fun updateDataSet(state: TrackersState) {
+ uiState = state
notifyDataSetChanged()
}
override fun getItemViewType(position: Int): Int = position
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListsTabViewHolder {
- val binding = TrackersListBinding.inflate(LayoutInflater.from(context), parent, false)
+ val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
TAB_APPS -> {
- ListsTabViewHolder.AppsListViewHolder(binding, viewModel)
+ ListsTabViewHolder.AppsListViewHolder(
+ TrackersAppsListBinding.inflate(inflater, parent, false),
+ viewModel
+ )
}
else -> {
- ListsTabViewHolder.TrackersListViewHolder(binding, viewModel)
+ ListsTabViewHolder.TrackersListViewHolder(
+ TrackersListBinding.inflate(inflater, parent, false),
+ viewModel
+ )
}
}
}
@@ -65,10 +70,10 @@ class ListsTabPagerAdapter(
override fun onBindViewHolder(holder: ListsTabViewHolder, position: Int) {
when (position) {
TAB_APPS -> {
- (holder as ListsTabViewHolder.AppsListViewHolder).onBind(apps)
+ (holder as ListsTabViewHolder.AppsListViewHolder).onBind(uiState)
}
TAB_TRACKERS -> {
- (holder as ListsTabViewHolder.TrackersListViewHolder).onBind(trackers)
+ (holder as ListsTabViewHolder.TrackersListViewHolder).onBind(uiState.trackers ?: emptyList())
}
}
}
@@ -93,16 +98,25 @@ class ListsTabPagerAdapter(
}
class AppsListViewHolder(
- private val binding: TrackersListBinding,
+ private val binding: TrackersAppsListBinding,
private val viewModel: TrackersViewModel
) : ListsTabViewHolder(binding.root) {
init {
setupRecyclerView(binding.list)
binding.list.adapter = AppsAdapter(viewModel)
+ binding.toggleNoTrackerApps.setOnClickListener { viewModel.onToggleHideNoTrackersApps() }
}
- fun onBind(apps: List) {
- (binding.list.adapter as AppsAdapter).dataSet = apps
+ fun onBind(uiState: TrackersState) {
+ (binding.list.adapter as AppsAdapter).dataSet = (
+ if (uiState.hideNoTrackersApps)
+ uiState.appsWithTrackers else uiState.allApps
+ ) ?: emptyList()
+
+ binding.toggleNoTrackerApps.apply {
+ isCloseIconVisible = uiState.hideNoTrackersApps
+ isChecked = uiState.hideNoTrackersApps
+ }
}
}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
index b016c5e21560b085a94d61d685ba6cc10485dc09..b88c55ea21b100a099deda3d1aaee19964f78eb8 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt
@@ -209,7 +209,7 @@ class TrackersFragment : NavToolbarFragment(R.layout.fragment_trackers) {
state.yearStatistics?.let { renderGraph(it, yearGraphHolder!!, binding.graphYear) }
updatePagerHeight()
- tabAdapter.updateDataSet(state.apps, state.trackers)
+ tabAdapter.updateDataSet(state)
}
private fun renderGraph(
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersState.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersState.kt
index 7f5fdfeb2253a91307a217b05d09e9723908cddf..1685c081fe65f8ab45af0954d54599f7001e96d0 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersState.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersState.kt
@@ -26,8 +26,10 @@ data class TrackersState(
val dayStatistics: TrackersPeriodicStatistics? = null,
val monthStatistics: TrackersPeriodicStatistics? = null,
val yearStatistics: TrackersPeriodicStatistics? = null,
- val apps: List? = null,
- val trackers: List? = null
+ val allApps: List? = null,
+ val trackers: List? = null,
+ val appsWithTrackers: List? = null,
+ val hideNoTrackersApps: Boolean = false
)
data class AppWithTrackersCount(
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt
index 31da8ca79a76928073c71c0c4a982f4d46a37d21..77da291fb0148bf7a5abb8dcfaae9a8b9555dfd2 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersViewModel.kt
@@ -62,9 +62,13 @@ class TrackersViewModel(
}
}
- trackersAndAppsListsUseCase.getAppsAndTrackersCounts().let { (appList, trackerList) ->
+ trackersAndAppsListsUseCase.getTrackersAndAppsLists().let { lists ->
_state.update {
- it.copy(apps = appList, trackers = trackerList)
+ it.copy(
+ trackers = lists.trackers,
+ allApps = lists.allApps,
+ appsWithTrackers = lists.appsWithTrackers
+ )
}
}
}
@@ -82,6 +86,10 @@ class TrackersViewModel(
_singleEvents.emit(SingleEvent.OpenUrl(Uri.parse(URL_LEARN_MORE_ABOUT_TRACKERS)))
}
+ fun onToggleHideNoTrackersApps() = viewModelScope.launch {
+ _state.update { it.copy(hideNoTrackersApps = !it.hideNoTrackersApps) }
+ }
+
sealed class SingleEvent {
data class ErrorEvent(val error: String) : SingleEvent()
data class OpenUrl(val url: Uri) : SingleEvent()
diff --git a/app/src/main/res/color/chip_background.xml b/app/src/main/res/color/chip_background.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c7f8c8aa0fa1837e1a0afee4dbb2c02da67ec9a8
--- /dev/null
+++ b/app/src/main/res/color/chip_background.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/color/chip_text.xml b/app/src/main/res/color/chip_text.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1de4d221171fc7a8bad8c3280ee78104e4570698
--- /dev/null
+++ b/app/src/main/res/color/chip_text.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e9ad3c66b33bf75fd643e0e62c2efdc1d824f93c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_close.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/trackers_apps_list.xml b/app/src/main/res/layout/trackers_apps_list.xml
new file mode 100644
index 0000000000000000000000000000000000000000..63f4b241f715b48dc4327ab12f5e1864f3af560d
--- /dev/null
+++ b/app/src/main/res/layout/trackers_apps_list.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index aa33837317195d890793229d9edd2dea5f989d9a..0ea109467514345fc45a36fb0f6e727984da8a8e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -97,12 +97,12 @@
24 hours
past month
past year
-
@string/ipscrambling_app_list_infos
- Trackers Activity Summary
Apps
Trackers
+ Trackers Activity Summary
+ Apps with trackers
%s trackers detected
detected in %s apps