Loading app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ 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<TrackerWithAppsCount>, val allApps: List<AppWithTrackersCount>, val appsWithTrackers: List<AppWithTrackersCount> ) app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt +14 −5 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -31,14 +32,16 @@ class TrackersAndAppsListsUseCase( private val appListsRepository: AppListsRepository, ) { suspend fun getAppsAndTrackersCounts(): Pair<List<AppWithTrackersCount>, List<TrackerWithAppsCount>> { 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<Tracker, Int>): List<TrackerWithAppsCount> { Loading @@ -47,12 +50,18 @@ class TrackersAndAppsListsUseCase( }.sortedByDescending { it.appsCount } } private suspend fun buildAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { private suspend fun buildAllAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { return appListsRepository.apps().first().map { app: ApplicationDescription -> AppWithTrackersCount(app = app, trackersCount = countByApp[app] ?: 0) }.sortedByDescending { it.trackersCount } } private fun buildAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { return countByApp.map { (app, count) -> AppWithTrackersCount(app = app, trackersCount = count) }.sortedByDescending { it.trackersCount } } private suspend fun mapIdsToEntities(trackersAndAppsIds: List<Pair<String, String>>): List<Pair<Tracker, ApplicationDescription>> { return trackersAndAppsIds.mapNotNull { (trackerId, apId) -> trackersRepository.getTracker(trackerId)?.let { tracker -> Loading app/src/main/java/foundation/e/advancedprivacy/features/trackers/ListsTabPagerAdapter.kt +27 −13 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -35,25 +36,29 @@ class ListsTabPagerAdapter( private val context: Context, private val viewModel: TrackersViewModel, ) : RecyclerView.Adapter<ListsTabPagerAdapter.ListsTabViewHolder>() { private var apps: List<AppWithTrackersCount> = emptyList() private var trackers: List<TrackerWithAppsCount> = emptyList() private var uiState: TrackersState = TrackersState() fun updateDataSet(apps: List<AppWithTrackersCount>?, trackers: List<TrackerWithAppsCount>?) { 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 ) } } } Loading @@ -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()) } } } Loading @@ -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<AppWithTrackersCount>) { (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 } } } Loading app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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( Loading app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersState.kt +4 −2 Original line number Diff line number Diff line Loading @@ -26,8 +26,10 @@ data class TrackersState( val dayStatistics: TrackersPeriodicStatistics? = null, val monthStatistics: TrackersPeriodicStatistics? = null, val yearStatistics: TrackersPeriodicStatistics? = null, val apps: List<AppWithTrackersCount>? = null, val trackers: List<TrackerWithAppsCount>? = null val allApps: List<AppWithTrackersCount>? = null, val trackers: List<TrackerWithAppsCount>? = null, val appsWithTrackers: List<AppWithTrackersCount>? = null, val hideNoTrackersApps: Boolean = false ) data class AppWithTrackersCount( Loading Loading
app/src/main/java/foundation/e/advancedprivacy/domain/entities/TrackersAndAppsLists.kt 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ 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<TrackerWithAppsCount>, val allApps: List<AppWithTrackersCount>, val appsWithTrackers: List<AppWithTrackersCount> )
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt +14 −5 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -31,14 +32,16 @@ class TrackersAndAppsListsUseCase( private val appListsRepository: AppListsRepository, ) { suspend fun getAppsAndTrackersCounts(): Pair<List<AppWithTrackersCount>, List<TrackerWithAppsCount>> { 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<Tracker, Int>): List<TrackerWithAppsCount> { Loading @@ -47,12 +50,18 @@ class TrackersAndAppsListsUseCase( }.sortedByDescending { it.appsCount } } private suspend fun buildAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { private suspend fun buildAllAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { return appListsRepository.apps().first().map { app: ApplicationDescription -> AppWithTrackersCount(app = app, trackersCount = countByApp[app] ?: 0) }.sortedByDescending { it.trackersCount } } private fun buildAppList(countByApp: Map<ApplicationDescription, Int>): List<AppWithTrackersCount> { return countByApp.map { (app, count) -> AppWithTrackersCount(app = app, trackersCount = count) }.sortedByDescending { it.trackersCount } } private suspend fun mapIdsToEntities(trackersAndAppsIds: List<Pair<String, String>>): List<Pair<Tracker, ApplicationDescription>> { return trackersAndAppsIds.mapNotNull { (trackerId, apId) -> trackersRepository.getTracker(trackerId)?.let { tracker -> Loading
app/src/main/java/foundation/e/advancedprivacy/features/trackers/ListsTabPagerAdapter.kt +27 −13 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -35,25 +36,29 @@ class ListsTabPagerAdapter( private val context: Context, private val viewModel: TrackersViewModel, ) : RecyclerView.Adapter<ListsTabPagerAdapter.ListsTabViewHolder>() { private var apps: List<AppWithTrackersCount> = emptyList() private var trackers: List<TrackerWithAppsCount> = emptyList() private var uiState: TrackersState = TrackersState() fun updateDataSet(apps: List<AppWithTrackersCount>?, trackers: List<TrackerWithAppsCount>?) { 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 ) } } } Loading @@ -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()) } } } Loading @@ -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<AppWithTrackersCount>) { (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 } } } Loading
app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersFragment.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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( Loading
app/src/main/java/foundation/e/advancedprivacy/features/trackers/TrackersState.kt +4 −2 Original line number Diff line number Diff line Loading @@ -26,8 +26,10 @@ data class TrackersState( val dayStatistics: TrackersPeriodicStatistics? = null, val monthStatistics: TrackersPeriodicStatistics? = null, val yearStatistics: TrackersPeriodicStatistics? = null, val apps: List<AppWithTrackersCount>? = null, val trackers: List<TrackerWithAppsCount>? = null val allApps: List<AppWithTrackersCount>? = null, val trackers: List<TrackerWithAppsCount>? = null, val appsWithTrackers: List<AppWithTrackersCount>? = null, val hideNoTrackersApps: Boolean = false ) data class AppWithTrackersCount( Loading