From b257062edf90567a11805c53d9eebf9b2c25c77d Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Fri, 17 Nov 2023 11:56:05 +0100 Subject: [PATCH 1/2] 1458: toggle app without trackers --- .../domain/entities/TrackersAndAppsLists.kt | 26 ++++++++++ .../usecases/TrackersAndAppsListsUseCase.kt | 19 ++++++-- .../features/trackers/ListsTabPagerAdapter.kt | 40 +++++++++++----- .../features/trackers/TrackersFragment.kt | 2 +- .../features/trackers/TrackersState.kt | 6 ++- .../features/trackers/TrackersViewModel.kt | 12 ++++- app/src/main/res/color/chip_background.xml | 22 +++++++++ app/src/main/res/color/chip_text.xml | 22 +++++++++ app/src/main/res/drawable/ic_close.xml | 9 ++++ .../main/res/layout/trackers_apps_list.xml | 47 +++++++++++++++++++ app/src/main/res/values/strings.xml | 4 +- 11 files changed, 184 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt create mode 100644 app/src/main/res/color/chip_background.xml create mode 100644 app/src/main/res/color/chip_text.xml create mode 100644 app/src/main/res/drawable/ic_close.xml create mode 100644 app/src/main/res/layout/trackers_apps_list.xml 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 00000000..2113b3a3 --- /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 8292a6d1..6a00b942 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 getAppsAndTrackersCounts(): 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 24204100..e9a046f7 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 b016c5e2..b88c55ea 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 7f5fdfeb..1685c081 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 31da8ca7..aaffab4c 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.getAppsAndTrackersCounts().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 00000000..c7f8c8aa --- /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 00000000..1de4d221 --- /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 00000000..e9ad3c66 --- /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 00000000..63f4b241 --- /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 aa338373..0ea10946 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 -- GitLab From 50a1938bf6b3b340a6faf876dab38e0323d8d473 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Mon, 4 Dec 2023 09:21:38 +0100 Subject: [PATCH 2/2] 1458: review fixes. --- .../domain/usecases/TrackersAndAppsListsUseCase.kt | 2 +- .../e/advancedprivacy/features/trackers/TrackersViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 6a00b942..ea07e8fa 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 @@ -32,7 +32,7 @@ class TrackersAndAppsListsUseCase( private val appListsRepository: AppListsRepository, ) { - suspend fun getAppsAndTrackersCounts(): TrackersAndAppsLists { + suspend fun getTrackersAndAppsLists(): TrackersAndAppsLists { val trackersAndAppsIds = statsDatabase.getDistinctTrackerAndApp() val trackersAndApps = mapIdsToEntities(trackersAndAppsIds) val (countByApp, countByTracker) = foldToCountByEntityMaps(trackersAndApps) 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 aaffab4c..77da291f 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,7 +62,7 @@ class TrackersViewModel( } } - trackersAndAppsListsUseCase.getAppsAndTrackersCounts().let { lists -> + trackersAndAppsListsUseCase.getTrackersAndAppsLists().let { lists -> _state.update { it.copy( trackers = lists.trackers, -- GitLab