summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2022-03-03 07:23:34 +0000
committerGuillaume Jacquart <guillaume.jacquart@hoodbrains.com>2022-03-03 07:23:34 +0000
commit72a66e8640c21683e99e4e7d866253fe205d14f0 (patch)
tree17ee151ea3a5e5f90b8d48ecea88b78b6bb938f0
parented659e60de259fe51b811af96a589c6bb9fd7d35 (diff)
parente9e22d2fdbde4e9679337fa681d60b3fdbfeace7 (diff)
Merge branch 'UX_upgrades' into 'main'
Ux upgrades See merge request e/privacy-central/privacycentralapp!20
-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/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt30
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt31
-rw-r--r--app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt29
-rw-r--r--app/src/main/res/layout/apptrackers_fragment.xml7
-rw-r--r--app/src/main/res/layout/trackers_item_app.xml36
-rw-r--r--app/src/main/res/values/strings.xml4
-rw-r--r--build.gradle2
16 files changed, 244 insertions, 76 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/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt
index 16cd4a0..b35fbca 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFeature.kt
@@ -17,6 +17,7 @@
package foundation.e.privacycentralapp.features.trackers.apptrackers
+import android.net.Uri
import android.util.Log
import foundation.e.flowmvi.Actor
import foundation.e.flowmvi.Reducer
@@ -63,17 +64,24 @@ class AppTrackersFeature(
return null
}
}
+
+ fun getTrackersCount() = trackers?.size ?: 0
+ fun getBlockedTrackersCount(): Int = if (isBlockingActivated)
+ getTrackersCount() - (whitelist?.size ?: 0)
+ else 0
}
sealed class SingleEvent {
data class ErrorEvent(val error: Any) : SingleEvent()
object NewStatisticsAvailableSingleEvent : SingleEvent()
+ data class OpenUrlEvent(val url: Uri) : SingleEvent()
}
sealed class Action {
data class InitAction(val packageName: String) : Action()
data class BlockAllToggleAction(val isBlocked: Boolean) : Action()
data class ToggleTrackerAction(val tracker: Tracker, val isBlocked: Boolean) : Action()
+ data class ClickTracker(val tracker: Tracker) : Action()
object FetchStatistics : Action()
}
@@ -87,9 +95,12 @@ class AppTrackersFeature(
object NewStatisticsAvailablesEffect : Effect()
data class QuickPrivacyUpdatedEffect(val enabled: Boolean) : Effect()
object QuickPrivacyDisabledWarningEffect : Effect()
+ data class OpenUrlEffect(val url: Uri) : Effect()
}
companion object {
+
+ private const val exodusBaseUrl = "https://reports.exodus-privacy.eu.org/fr/trackers/"
fun create(
initialState: State = State(),
coroutineScope: CoroutineScope,
@@ -179,6 +190,17 @@ class AppTrackersFeature(
} ?: run { flowOf(Effect.ErrorEffect("No appDesc.")) }
} else flowOf(Effect.NoEffect)
}
+ is Action.ClickTracker -> {
+ flowOf(
+ action.tracker.getExodusId()?.let {
+ try {
+ Effect.OpenUrlEffect(Uri.parse(exodusBaseUrl + it))
+ } catch (e: Exception) {
+ Effect.ErrorEffect("Invalid Url")
+ }
+ } ?: Effect.NoEffect
+ )
+ }
is Action.FetchStatistics -> flowOf(
state.appDesc?.uid?.let {
Effect.AvailableTrackersListEffect(
@@ -196,9 +218,17 @@ class AppTrackersFeature(
SingleEvent.ErrorEvent(R.string.apptrackers_error_quickprivacy_disabled)
is Effect.NewStatisticsAvailablesEffect ->
SingleEvent.NewStatisticsAvailableSingleEvent
+ is Effect.OpenUrlEffect ->
+ SingleEvent.OpenUrlEvent(effect.url)
else -> null
}
}
)
}
}
+
+fun Tracker.getExodusId(): String? {
+ return if (id.startsWith("exodus_")) {
+ id.substringAfter("exodus_")
+ } else null
+}
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt
index 440edf7..8e2dc3b 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/AppTrackersFragment.kt
@@ -17,6 +17,8 @@
package foundation.e.privacycentralapp.features.trackers.apptrackers
+import android.content.ActivityNotFoundException
+import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
@@ -76,6 +78,12 @@ class AppTrackersFragment :
is SingleEvent.NewStatisticsAvailableSingleEvent -> {
viewModel.submitAction(Action.FetchStatistics)
}
+ is SingleEvent.OpenUrlEvent ->
+ try {
+ startActivity(Intent(Intent.ACTION_VIEW, event.url))
+ } catch (e: ActivityNotFoundException) {
+ displayToast("No application to see webpages")
+ }
}
}
}
@@ -104,14 +112,13 @@ class AppTrackersFragment :
binding.trackers.apply {
layoutManager = LinearLayoutManager(requireContext())
setHasFixedSize(true)
- adapter = ToggleTrackersAdapter(R.layout.apptrackers_item_tracker_toggle) { tracker, isBlocked ->
- viewModel.submitAction(
- Action.ToggleTrackerAction(
- tracker,
- isBlocked
- )
- )
- }
+ adapter = ToggleTrackersAdapter(
+ R.layout.apptrackers_item_tracker_toggle,
+ onToggleSwitch = { tracker, isBlocked ->
+ viewModel.submitAction(Action.ToggleTrackerAction(tracker, isBlocked))
+ },
+ onClickTitle = { viewModel.submitAction(Action.ClickTracker(it)) }
+ )
}
}
@@ -121,6 +128,14 @@ class AppTrackersFragment :
}
override fun render(state: State) {
+
+ binding.trackersCountSummary.text = if (state.getTrackersCount() == 0) ""
+ else getString(
+ R.string.apptrackers_trackers_count_summary,
+ state.getBlockedTrackersCount(),
+ state.getTrackersCount()
+ )
+
binding.blockAllToggle.isChecked = state.isBlockingActivated
binding.trackersListTitle.isVisible = state.isBlockingActivated
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt
index 580a60c..82f2d2c 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/trackers/apptrackers/ToggleTrackersAdapter.kt
@@ -17,11 +17,14 @@
package foundation.e.privacycentralapp.features.trackers.apptrackers
+import android.text.SpannableString
+import android.text.style.UnderlineSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Switch
import android.widget.TextView
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import foundation.e.privacycentralapp.R
@@ -29,9 +32,9 @@ import foundation.e.privacymodules.trackers.Tracker
class ToggleTrackersAdapter(
private val itemsLayout: Int,
- private val listener: (Tracker, Boolean) -> Unit
-) :
- RecyclerView.Adapter<ToggleTrackersAdapter.ViewHolder>() {
+ private val onToggleSwitch: (Tracker, Boolean) -> Unit,
+ private val onClickTitle: (Tracker) -> Unit
+) : RecyclerView.Adapter<ToggleTrackersAdapter.ViewHolder>() {
var isEnabled = true
@@ -42,7 +45,17 @@ class ToggleTrackersAdapter(
val toggleOverlay: View = view.findViewById(R.id.toggle_clicker)
fun bind(item: Pair<Tracker, Boolean>, isEnabled: Boolean) {
- title.text = item.first.label
+ val text = item.first.label
+ if (item.first.website != null) {
+ title.setTextColor(ContextCompat.getColor(title.context, R.color.accent))
+ val spannable = SpannableString(text)
+ spannable.setSpan(UnderlineSpan(), 0, spannable.length, 0)
+ title.text = spannable
+ } else {
+ title.setTextColor(ContextCompat.getColor(title.context, R.color.black))
+ title.text = text
+ }
+
toggle.isChecked = item.second
toggle.isEnabled = isEnabled
toggleOverlay.isVisible = !isEnabled
@@ -62,10 +75,14 @@ class ToggleTrackersAdapter(
.inflate(itemsLayout, parent, false)
val holder = ViewHolder(view)
holder.toggle.setOnClickListener {
- listener(dataSet[holder.adapterPosition].first, holder.toggle.isChecked)
+ onToggleSwitch(dataSet[holder.adapterPosition].first, holder.toggle.isChecked)
}
holder.toggleOverlay.setOnClickListener {
- listener(dataSet[holder.adapterPosition].first, false)
+ onToggleSwitch(dataSet[holder.adapterPosition].first, false)
+ }
+
+ holder.title.setOnClickListener {
+ onClickTitle(dataSet[holder.adapterPosition].first)
}
return holder
diff --git a/app/src/main/res/layout/apptrackers_fragment.xml b/app/src/main/res/layout/apptrackers_fragment.xml
index 13f58e3..17d992e 100644
--- a/app/src/main/res/layout/apptrackers_fragment.xml
+++ b/app/src/main/res/layout/apptrackers_fragment.xml
@@ -41,6 +41,13 @@
android:layout_width="match_parent"
android:orientation="vertical"
>
+ <TextView
+ android:id="@+id/trackers_count_summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="16sp"
+ android:padding="16dp"
+ />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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 8c2a0ba..49b6c95 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -40,7 +40,7 @@
<string name="ipscrambling_hide_ip_label">Hide IP address</string>
<string name="ipscrambling_hide_ip_subtitle">I am anonymous on the internet</string>
<string name="ipscrambling_tor_disclaimer"><b>Note:</b> when active, this setting will slow down your Internet connectivity speed (uses the Tor network).</string>
- <string name="ipscrambling_location_label">Force a country of origin:</string>
+ <string name="ipscrambling_location_label">My internet activity must appear from:</string>
<string name="ipscrambling_any_location">Random country</string>
<string name="ipscrambling_select_app">Apply this setting to all selected apps:</string>
<string name="ipscrambling_error_quickprivacy_disabled">Enabled Quick Privacy to use functionalities</string>
@@ -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>
@@ -74,6 +75,7 @@
<string name="apptrackers_no_trackers_yet_block_off">No trackers were detected yet. If new trackers are detected they will be updated here.</string>
<string name="apptrackers_no_trackers_yet_block_on">No trackers were detected yet. All future trackers will be blocked.</string>
<string name="apptrackers_error_quickprivacy_disabled">Enable Quick Privacy to be able to activate/deactivate trackers.</string>
+ <string name="apptrackers_trackers_count_summary">%1$d blocked trackers out of %2$d detected trackers</string>
<!-- -->
<string name="quick_protection_info">Quick protection enables these settings when turned on</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",
],