diff options
author | jacquarg <guillaume.jacquart@hoodbrains.com> | 2022-02-28 09:30:55 +0100 |
---|---|---|
committer | jacquarg <guillaume.jacquart@hoodbrains.com> | 2022-03-01 10:06:22 +0100 |
commit | e9e22d2fdbde4e9679337fa681d60b3fdbfeace7 (patch) | |
tree | 17ee151ea3a5e5f90b8d48ecea88b78b6bb938f0 /app/src/main | |
parent | e6ba99e31da49df559da60e8d012e40813ee14f0 (diff) |
Show trackers counts for each apps in tracekr screen, #4589
Diffstat (limited to 'app/src/main')
10 files changed, 156 insertions, 58 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 76a9539..6b4035e 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -89,7 +89,7 @@ class DependencyContainer constructor(val app: Application) { private val appListUseCase = AppListUseCase(appListsRepository) private val trackersStatisticsUseCase by lazy { - TrackersStatisticsUseCase(trackTrackersPrivacyModule, appListsRepository, context.resources) + TrackersStatisticsUseCase(trackTrackersPrivacyModule, blockTrackersPrivacyModule, appListsRepository, context.resources) } private val trackersStateUseCase by lazy { @@ -119,7 +119,7 @@ class DependencyContainer constructor(val app: Application) { } val trackersViewModelFactory by lazy { - TrackersViewModelFactory(trackersStatisticsUseCase, appListUseCase) + TrackersViewModelFactory(trackersStatisticsUseCase) } val appTrackersViewModelFactory by lazy { diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt index d66ce76..07cf125 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt @@ -24,7 +24,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import foundation.e.privacycentralapp.R -import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacycentralapp.domain.entities.AppWithCounts class AppsAdapter( private val itemsLayout: Int, @@ -34,15 +34,20 @@ class AppsAdapter( class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val appName: TextView = view.findViewById(R.id.title) - - fun bind(item: ApplicationDescription) { + val counts: TextView = view.findViewById(R.id.counts) + val icon: ImageView = view.findViewById(R.id.icon) + fun bind(item: AppWithCounts) { appName.text = item.label - - itemView.findViewById<ImageView>(R.id.icon).setImageDrawable(item.icon) + counts.text = itemView.context.getString( + R.string.trackers_app_trackers_counts, + item.blockedTrackersCount, + item.trackersCount + ) + icon.setImageDrawable(item.icon) } } - var dataSet: List<ApplicationDescription> = emptyList() + var dataSet: List<AppWithCounts> = emptyList() set(value) { field = value notifyDataSetChanged() diff --git a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt index 4718923..958a536 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt @@ -50,6 +50,14 @@ class AppListsRepository( return appDescriptions.value.second } + fun foldForHiddenSystemApp(appUid: Int, appValueGetter: (Int) -> Int): Int { + return if (appUid == dummySystemApp.uid) { + getHiddenSystemApps().fold(0) { acc, app -> + acc + appValueGetter(app.uid) + } + } else appValueGetter(appUid) + } + private val pm get() = context.packageManager private val appDescriptions = MutableStateFlow( diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt new file mode 100644 index 0000000..682dfc8 --- /dev/null +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 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.entities + +import android.graphics.drawable.Drawable +import foundation.e.privacymodules.permissions.data.ApplicationDescription + +data class AppWithCounts( + val packageName: String, + val uid: Int, + var label: CharSequence?, + var icon: Drawable?, + val isWhitelisted: Boolean = false, + val trackersCount: Int = 0, + val whiteListedTrackersCount: Int = 0 +) { + constructor( + app: ApplicationDescription, + isWhitelisted: Boolean, + trackersCount: Int, + whiteListedTrackersCount: Int + ) : + this( + packageName = app.packageName, + uid = app.uid, + label = app.label, + icon = app.icon, + isWhitelisted = isWhitelisted, + trackersCount = trackersCount, + whiteListedTrackersCount = whiteListedTrackersCount + ) + + val blockedTrackersCount get() = if (isWhitelisted) 0 + else trackersCount - whiteListedTrackersCount +} 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 9a8b12a..ad8f565 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 @@ -20,18 +20,22 @@ package foundation.e.privacycentralapp.domain.usecases import android.content.res.Resources import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.data.repositories.AppListsRepository +import foundation.e.privacycentralapp.domain.entities.AppWithCounts import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics +import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule import foundation.e.privacymodules.trackers.Tracker import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.map import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.time.temporal.ChronoUnit class TrackersStatisticsUseCase( private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule, + private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule, private val appListsRepository: AppListsRepository, private val resources: Resources ) { @@ -124,4 +128,23 @@ class TrackersStatisticsUseCase( return trackers.sortedBy { it.label.lowercase() } } + + fun getAppsWithCounts(): Flow<List<AppWithCounts>> { + val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp() + return appListsRepository.getVisibleApps() + .map { apps -> + apps.map { app -> + AppWithCounts( + app, + blockTrackersPrivacyModule.isWhitelisted(app.uid), + appListsRepository.foldForHiddenSystemApp(app.uid) { + trackersCounts.getOrDefault(it, 0) + }, + appListsRepository.foldForHiddenSystemApp(app.uid) { + blockTrackersPrivacyModule.getWhiteList(it).size + } + ) + } + } + } } 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 e2eb58d..a606e49 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 @@ -22,11 +22,9 @@ 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.entities.AppWithCounts import foundation.e.privacycentralapp.domain.entities.TrackersPeriodicStatistics -import foundation.e.privacycentralapp.domain.usecases.AppListUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase -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 @@ -52,13 +50,12 @@ class TrackersFeature( val dayStatistics: TrackersPeriodicStatistics? = null, val monthStatistics: TrackersPeriodicStatistics? = null, val yearStatistics: TrackersPeriodicStatistics? = null, - val apps: List<ApplicationDescription>? = null, - val trackers: List<Tracker> = emptyList() + val apps: List<AppWithCounts>? = null, ) sealed class SingleEvent { data class ErrorEvent(val error: String) : SingleEvent() - data class OpenAppDetailsEvent(val appDesc: ApplicationDescription) : SingleEvent() + data class OpenAppDetailsEvent(val appDesc: AppWithCounts) : SingleEvent() object NewStatisticsAvailableSingleEvent : SingleEvent() } @@ -75,9 +72,9 @@ class TrackersFeature( val yearStatistics: TrackersPeriodicStatistics? = null ) : Effect() data class AvailableAppsListEffect( - val apps: List<ApplicationDescription> + val apps: List<AppWithCounts> ) : Effect() - data class OpenAppDetailsEffect(val appDesc: ApplicationDescription) : Effect() + data class OpenAppDetailsEffect(val appDesc: AppWithCounts) : Effect() object QuickPrivacyDisabledWarningEffect : Effect() data class ErrorEffect(val message: String) : Effect() object NewStatisticsAvailablesEffect : Effect() @@ -87,8 +84,7 @@ class TrackersFeature( fun create( initialState: State = State(), coroutineScope: CoroutineScope, - trackersStatisticsUseCase: TrackersStatisticsUseCase, - appListUseCase: AppListUseCase + trackersStatisticsUseCase: TrackersStatisticsUseCase ) = TrackersFeature( initialState, coroutineScope, reducer = { state, effect -> @@ -106,7 +102,19 @@ class TrackersFeature( }, actor = { state, action -> when (action) { - Action.InitAction -> merge<TrackersFeature.Effect>( + Action.InitAction -> merge<Effect>( + flowOf(Effect.NewStatisticsAvailablesEffect), + trackersStatisticsUseCase.listenUpdates().map { + Effect.NewStatisticsAvailablesEffect + } + ) + + is Action.ClickAppAction -> flowOf( + state.apps?.find { it.packageName == action.packageName }?.let { + Effect.OpenAppDetailsEffect(it) + } ?: run { Effect.ErrorEffect("Can't find back app.") } + ) + is Action.FetchStatistics -> merge<Effect>( flow { trackersStatisticsUseCase.getDayMonthYearStatistics() .let { (day, month, year) -> @@ -114,36 +122,15 @@ class TrackersFeature( Effect.TrackersStatisticsLoadedEffect( dayStatistics = day, monthStatistics = month, - yearStatistics = year + yearStatistics = year, ) ) } }, - appListUseCase.getAppsUsingInternet().map { apps -> - Effect.AvailableAppsListEffect(apps) - }, - trackersStatisticsUseCase.listenUpdates().map { - Effect.NewStatisticsAvailablesEffect + trackersStatisticsUseCase.getAppsWithCounts().map { + Effect.AvailableAppsListEffect(it) } ) - - is Action.ClickAppAction -> flowOf( - state.apps?.find { it.packageName == action.packageName }?.let { - Effect.OpenAppDetailsEffect(it) - } ?: run { Effect.ErrorEffect("Can't find back app.") } - ) - is Action.FetchStatistics -> flow { - trackersStatisticsUseCase.getDayMonthYearStatistics() - .let { (day, month, year) -> - emit( - Effect.TrackersStatisticsLoadedEffect( - dayStatistics = day, - monthStatistics = month, - yearStatistics = year, - ) - ) - } - } } }, singleEventProducer = { _, _, effect -> 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 3b22f89..0f686b4 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 @@ -18,10 +18,11 @@ 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.add import androidx.fragment.app.commit +import androidx.fragment.app.replace import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -70,7 +71,7 @@ class TrackersFragment : } is TrackersFeature.SingleEvent.OpenAppDetailsEvent -> { requireActivity().supportFragmentManager.commit { - add<AppTrackersFragment>(R.id.container, args = AppTrackersFragment.buildArgs(event.appDesc.label.toString(), event.appDesc.packageName)) + replace<AppTrackersFragment>(R.id.container, args = AppTrackersFragment.buildArgs(event.appDesc.label.toString(), event.appDesc.packageName)) setReorderingAllowed(true) addToBackStack("apptrackers") } @@ -113,6 +114,7 @@ class TrackersFragment : } override fun onResume() { + Log.d("TestCounts", "OnResume") super.onResume() viewModel.submitAction(TrackersFeature.Action.FetchStatistics) } 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 index e3a97cc..c2a1822 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt @@ -20,15 +20,13 @@ package foundation.e.privacycentralapp.features.trackers import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import foundation.e.privacycentralapp.common.Factory -import foundation.e.privacycentralapp.domain.usecases.AppListUseCase import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch class TrackersViewModel( - private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val appListUseCase: AppListUseCase + private val trackersStatisticsUseCase: TrackersStatisticsUseCase ) : ViewModel() { private val _actions = MutableSharedFlow<TrackersFeature.Action>() @@ -38,8 +36,7 @@ class TrackersViewModel( TrackersFeature.create( coroutineScope = viewModelScope, - trackersStatisticsUseCase = trackersStatisticsUseCase, - appListUseCase = appListUseCase + trackersStatisticsUseCase = trackersStatisticsUseCase ) } @@ -51,11 +48,10 @@ class TrackersViewModel( } class TrackersViewModelFactory( - private val trackersStatisticsUseCase: TrackersStatisticsUseCase, - private val appListUseCase: AppListUseCase + private val trackersStatisticsUseCase: TrackersStatisticsUseCase ) : Factory<TrackersViewModel> { override fun create(): TrackersViewModel { - return TrackersViewModel(trackersStatisticsUseCase, appListUseCase) + return TrackersViewModel(trackersStatisticsUseCase) } } diff --git a/app/src/main/res/layout/trackers_item_app.xml b/app/src/main/res/layout/trackers_item_app.xml index b368664..310c792 100644 --- a/app/src/main/res/layout/trackers_item_app.xml +++ b/app/src/main/res/layout/trackers_item_app.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.appcompat.widget.LinearLayoutCompat - xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/container" android:layout_height="52dp" android:layout_width="match_parent" @@ -16,23 +16,49 @@ android:layout_height="32dp" android:layout_width="32dp" android:src="@drawable/ic_facebook" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> <TextView android:id="@+id/title" - android:layout_centerVertical="true" android:layout_height="wrap_content" android:layout_width="0dp" - android:layout_weight="1" android:maxLines="1" android:ellipsize="end" android:layout_marginStart="16dp" android:textSize="14sp" + android:textColor="@color/black_text" tools:text="Body sensor" + app:layout_constraintLeft_toRightOf="@+id/icon" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toTopOf="@+id/counts" + app:layout_constraintRight_toLeftOf="@+id/arrow" + app:layout_constraintVertical_chainStyle="packed" + /> + <TextView + android:id="@+id/counts" + android:layout_height="wrap_content" + android:layout_width="0dp" + android:maxLines="1" + android:ellipsize="end" + android:layout_marginStart="16dp" + android:textSize="14sp" + android:textColor="@color/grey_text_2" + tools:text="1 tracker blocked out of 4" + app:layout_constraintLeft_toRightOf="@+id/icon" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@+id/title" + app:layout_constraintRight_toLeftOf="@+id/arrow" /> <ImageView + android:id="@+id/arrow" android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_chevron_right_24dp" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" /> -</androidx.appcompat.widget.LinearLayoutCompat> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 822244b..49b6c95 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,6 +67,7 @@ <string name="trackers_graph_hours_period_format">HH:mm</string> <string name="trackers_graph_days_period_format">MMM d - EEE</string> <string name="trackers_graph_months_period_format">MMM yyyy</string> + <string name="trackers_app_trackers_counts">%1$d blocked trackers out of %2$d</string> <!-- App Trackers --> <string name="apptrackers_block_all_toggle">Block trackers</string> |