summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/build.gradle4
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt4
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt17
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/data/repositories/AppListsRepository.kt8
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt50
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/domain/usecases/TrackersStatisticsUseCase.kt23
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFeature.kt57
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersFragment.kt6
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/TrackersViewModel.kt12
-rw-r--r--app/src/main/res/layout/trackers_item_app.xml36
-rw-r--r--app/src/main/res/values/strings.xml1
-rw-r--r--build.gradle2
12 files changed, 159 insertions, 61 deletions
diff --git a/app/build.gradle b/app/build.gradle
index b771285..3716977 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -110,8 +110,8 @@ dependencies {
//googleImplementation project(":privacymodulesgoogle")
// include the e specific version of the modules, just for the e flavor
- implementation 'foundation.e:privacymodule.trackerfilter:0.3.0'
- implementation 'foundation.e:privacymodule.api:0.5.0'
+ implementation 'foundation.e:privacymodule.trackerfilter:0.4.0'
+ implementation 'foundation.e:privacymodule.api:0.6.0'
e29Implementation 'foundation.e:privacymodule.e-29:0.4.2'
e30Implementation 'foundation.e:privacymodule.e-30:0.4.2'
implementation 'foundation.e:privacymodule.tor:0.2.1'
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>
diff --git a/build.gradle b/build.gradle
index cbd41b1..86a50bb 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,7 +9,7 @@ buildscript {
'targetSdk' : 29,
'version' : [
'major': 0,
- 'minor': 5,
+ 'minor': 6,
'patch': 0,
'build': "milestone-3",
],