From 730c2bc7566c03a9774509ab35bbbbca578ecf72 Mon Sep 17 00:00:00 2001
From: jacquarg <guillaume.jacquart@hoodbrains.com>
Date: Mon, 25 Apr 2022 08:56:18 +0200
Subject: 219 highlight active trackers

---
 app/build.gradle                                   |  4 +-
 app/src/main/AndroidManifest.xml                   |  3 +-
 .../e/privacycentralapp/common/AppsAdapter.kt      |  3 +-
 .../data/repositories/AppListsRepository.kt        |  4 ++
 .../domain/entities/AppWithCounts.kt               | 12 +++--
 .../domain/usecases/TrackersStatisticsUseCase.kt   | 50 +++++++++++++++---
 .../features/dashboard/DashboardFeature.kt         | 60 +++++++++++++---------
 .../features/dashboard/DashboardFragment.kt        | 13 +++++
 .../features/trackers/TrackersFeature.kt           |  1 -
 .../trackers/apptrackers/AppTrackersFeature.kt     | 28 ++++++----
 .../trackers/apptrackers/AppTrackersFragment.kt    |  4 +-
 .../e/privacycentralapp/main/MainActivity.kt       | 22 ++++----
 .../e/privacycentralapp/widget/WidgetUI.kt         | 15 +++++-
 .../res/layout/apptrackers_item_tracker_toggle.xml | 12 ++---
 app/src/main/res/layout/fragment_dashboard.xml     | 17 +++++-
 app/src/main/res/layout/widget.xml                 | 15 ++++++
 app/src/main/res/values-fr/strings.xml             |  2 +-
 app/src/main/res/values/strings.xml                |  6 ++-
 build.gradle                                       |  2 +-
 19 files changed, 200 insertions(+), 73 deletions(-)

diff --git a/app/build.gradle b/app/build.gradle
index 357a205..7749f8f 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.6.1'
-    implementation 'foundation.e:privacymodule.api:1.0.0'
+    implementation 'foundation.e:privacymodule.trackerfilter:0.7.0'
+    implementation 'foundation.e:privacymodule.api:1.1.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.2'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d285b6f..b288588 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -63,7 +63,8 @@
             android:launchMode="singleTask">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.INFO"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+<!--                <category android:name="android.intent.category.INFO"/>-->
             </intent-filter>
         </activity>
     </application>
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 ed47cff..16b0144 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/common/AppsAdapter.kt
@@ -41,7 +41,8 @@ class AppsAdapter(
             counts.text = itemView.context.getString(
                 R.string.trackers_app_trackers_counts,
                 item.blockedTrackersCount,
-                item.trackersCount
+                item.trackersCount,
+                item.leaks
             )
             icon.setImageDrawable(item.icon)
 
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 4b19083..1f23516 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
@@ -66,6 +66,10 @@ class AppListsRepository(
         return appDescriptions.value.first.find { it.packageName == packageName }
     }
 
+    fun getApplicationDescription(appUid: Int): ApplicationDescription? {
+        return appDescriptions.value.first.find { it.uid == appUid }
+    }
+
     fun foldForHiddenSystemApp(appUid: Int, appValueGetter: (Int) -> Int): Int {
         return if (appUid == dummySystemApp.uid) {
             getHiddenSystemApps().fold(0) { acc, app ->
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
index 682dfc8..0b76c7b 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/AppWithCounts.kt
@@ -27,13 +27,17 @@ data class AppWithCounts(
     var icon: Drawable?,
     val isWhitelisted: Boolean = false,
     val trackersCount: Int = 0,
-    val whiteListedTrackersCount: Int = 0
+    val whiteListedTrackersCount: Int = 0,
+    val blockedLeaks: Int = 0,
+    val leaks: Int = 0,
 ) {
     constructor(
         app: ApplicationDescription,
         isWhitelisted: Boolean,
         trackersCount: Int,
-        whiteListedTrackersCount: Int
+        whiteListedTrackersCount: Int,
+        blockedLeaks: Int,
+        leaks: Int,
     ) :
         this(
             packageName = app.packageName,
@@ -42,7 +46,9 @@ data class AppWithCounts(
             icon = app.icon,
             isWhitelisted = isWhitelisted,
             trackersCount = trackersCount,
-            whiteListedTrackersCount = whiteListedTrackersCount
+            whiteListedTrackersCount = whiteListedTrackersCount,
+            blockedLeaks = blockedLeaks,
+            leaks = leaks
         )
 
     val blockedTrackersCount get() = if (isWhitelisted) 0
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 6b4e098..d8eed5c 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
@@ -22,6 +22,7 @@ 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.permissions.data.ApplicationDescription
 import foundation.e.privacymodules.trackers.IBlockTrackersPrivacyModule
 import foundation.e.privacymodules.trackers.ITrackTrackersPrivacyModule
 import foundation.e.privacymodules.trackers.Tracker
@@ -35,12 +36,14 @@ import java.time.format.DateTimeFormatter
 import java.time.temporal.ChronoUnit
 
 class TrackersStatisticsUseCase(
-    // TODO private
-    val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule,
+    private val trackTrackersPrivacyModule: ITrackTrackersPrivacyModule,
     private val blockTrackersPrivacyModule: IBlockTrackersPrivacyModule,
     private val appListsRepository: AppListsRepository,
     private val resources: Resources
 ) {
+    fun initAppList() {
+        appListsRepository.getVisibleApps()
+    }
 
     fun listenUpdates(): Flow<Unit> = callbackFlow {
         val listener = object : ITrackTrackersPrivacyModule.Listener {
@@ -60,6 +63,12 @@ class TrackersStatisticsUseCase(
         ) to trackTrackersPrivacyModule.getTrackersCount()
     }
 
+    fun getMostLeakedApp(): ApplicationDescription? {
+        return appListsRepository.getApplicationDescription(
+            trackTrackersPrivacyModule.getPastDayMostLeakedApp()
+        )
+    }
+
     fun getDayTrackersCalls() = trackTrackersPrivacyModule.getPastDayTrackersCalls()
 
     fun getDayTrackersCount() = trackTrackersPrivacyModule.getPastDayTrackersCount()
@@ -136,23 +145,48 @@ class TrackersStatisticsUseCase(
         return trackers.sortedBy { it.label.lowercase() }
     }
 
+    fun getCalls(appUid: Int): Pair<Int, Int> {
+        return if (appUid == appListsRepository.dummySystemApp.uid) {
+            appListsRepository.getHiddenSystemApps().map {
+                trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(it.uid)
+            }.reduce { (accBlocked, accLeaked), (blocked, leaked) ->
+                accBlocked + blocked to accLeaked + leaked
+            }
+        } else trackTrackersPrivacyModule.getPastDayTrackersCallsForApp(appUid)
+    }
+
     fun getAppsWithCounts(): Flow<List<AppWithCounts>> {
         val trackersCounts = trackTrackersPrivacyModule.getTrackersCountByApp()
+
         return appListsRepository.getVisibleApps()
             .map { apps ->
+                val callsByApp = trackTrackersPrivacyModule.getPastDayTrackersCallsByApps()
                 apps.map { app ->
+                    val (blockedLeaks, leaks) = callsByApp.getOrDefault(app.uid, 0 to 0)
                     AppWithCounts(
-                        app,
-                        !blockTrackersPrivacyModule.isBlockingEnabled() ||
+                        app = app,
+                        isWhitelisted = !blockTrackersPrivacyModule.isBlockingEnabled() ||
                             blockTrackersPrivacyModule.isWhitelisted(app.uid),
-                        appListsRepository.foldForHiddenSystemApp(app.uid) {
+                        trackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) {
                             trackersCounts.getOrDefault(it, 0)
                         },
-                        appListsRepository.foldForHiddenSystemApp(app.uid) {
+                        whiteListedTrackersCount = appListsRepository.foldForHiddenSystemApp(app.uid) {
                             blockTrackersPrivacyModule.getWhiteList(it).size
-                        }
+                        },
+                        blockedLeaks = blockedLeaks,
+                        leaks = leaks
                     )
-                }
+                }.sortedWith(
+                    Comparator { o1, o2 ->
+                        val leaks = o2.leaks - o1.leaks
+                        if (leaks != 0) leaks else {
+                            val whitelisted = o2.whiteListedTrackersCount - o1.whiteListedTrackersCount
+                            if (whitelisted != 0) whitelisted else {
+                                o2.trackersCount - o1.trackersCount
+                            }
+                        }
+                    }
+                )
             }
     }
 
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
index 3ed3168..5192229 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFeature.kt
@@ -27,6 +27,7 @@ import foundation.e.privacycentralapp.domain.entities.LocationMode
 import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState
 import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase
 import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase
+import foundation.e.privacymodules.permissions.data.ApplicationDescription
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
@@ -66,6 +67,7 @@ class DashboardFeature(
         object NavigateToInternetActivityPrivacySingleEvent : SingleEvent()
         object NavigateToLocationSingleEvent : SingleEvent()
         object NavigateToPermissionsSingleEvent : SingleEvent()
+        data class NavigateToAppDetailsEvent(val appDesc: ApplicationDescription) : SingleEvent()
         object NewStatisticsAvailableSingleEvent : SingleEvent()
         data class ToastMessageSingleEvent(val message: Int) : SingleEvent()
     }
@@ -79,6 +81,7 @@ class DashboardFeature(
         object ShowTrackers : Action()
         object FetchStatistics : Action()
         object CloseQuickPrivacyDisabledMessage : Action()
+        object ShowMostLeakedApp : Action()
     }
 
     sealed class Effect {
@@ -102,6 +105,7 @@ class DashboardFeature(
         object FirstIPTrackerActivationEffect : Effect()
         data class LocationHiddenUpdatedEffect(val isLocationHidden: Boolean) : Effect()
         data class ShowQuickPrivacyDisabledMessageEffect(val show: Boolean) : Effect()
+        data class OpenAppDetailsEffect(val appDesc: ApplicationDescription) : Effect()
     }
 
     companion object {
@@ -146,29 +150,32 @@ class DashboardFeature(
                             }
                         }
 
-                        Action.InitAction -> merge(
-                            getPrivacyStateUseCase.quickPrivacyState.map {
-                                Effect.UpdateStateEffect(it)
-                            },
-                            getPrivacyStateUseCase.isIpHidden.map {
-                                Effect.IpScramblingModeUpdatedEffect(it)
-                            },
-                            trackersStatisticsUseCase.listenUpdates().map {
-                                Effect.NewStatisticsAvailablesEffect
-                            },
-                            getPrivacyStateUseCase.isTrackersDenied.map {
-                                Effect.TrackersBlockedUpdatedEffect(it)
-                            },
-                            getPrivacyStateUseCase.isLocationHidden.map {
-                                Effect.LocationHiddenUpdatedEffect(it)
-                            },
-                            getPrivacyStateUseCase.locationMode.map {
-                                Effect.UpdateLocationModeEffect(it)
-                            },
-                            getPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map {
-                                Effect.ShowQuickPrivacyDisabledMessageEffect(it)
-                            },
-                        )
+                        Action.InitAction -> {
+                            trackersStatisticsUseCase.initAppList()
+                            merge(
+                                getPrivacyStateUseCase.quickPrivacyState.map {
+                                    Effect.UpdateStateEffect(it)
+                                },
+                                getPrivacyStateUseCase.isIpHidden.map {
+                                    Effect.IpScramblingModeUpdatedEffect(it)
+                                },
+                                trackersStatisticsUseCase.listenUpdates().map {
+                                    Effect.NewStatisticsAvailablesEffect
+                                },
+                                getPrivacyStateUseCase.isTrackersDenied.map {
+                                    Effect.TrackersBlockedUpdatedEffect(it)
+                                },
+                                getPrivacyStateUseCase.isLocationHidden.map {
+                                    Effect.LocationHiddenUpdatedEffect(it)
+                                },
+                                getPrivacyStateUseCase.locationMode.map {
+                                    Effect.UpdateLocationModeEffect(it)
+                                },
+                                getPrivacyStateUseCase.showQuickPrivacyDisabledMessage.map {
+                                    Effect.ShowQuickPrivacyDisabledMessageEffect(it)
+                                },
+                            )
+                        }
                         Action.ShowFakeMyLocationAction -> flowOf(Effect.OpenFakeMyLocationEffect)
                         Action.ShowAppsPermissions -> flowOf(Effect.OpenAppsPermissionsEffect)
                         Action.ShowInternetActivityPrivacyAction -> flowOf(
@@ -193,6 +200,12 @@ class DashboardFeature(
                             getPrivacyStateUseCase.resetQuickPrivacyDisabledMessage()
                             flowOf(Effect.NoEffect)
                         }
+                        is Action.ShowMostLeakedApp -> {
+                            Log.d("mostleak", "Action.ShowMostLeakedApp")
+                            flowOf(
+                                trackersStatisticsUseCase.getMostLeakedApp()?.let { Effect.OpenAppDetailsEffect(appDesc = it) } ?: Effect.NoEffect
+                            )
+                        }
                     }
                 },
                 singleEventProducer = { _, _, effect ->
@@ -211,6 +224,7 @@ class DashboardFeature(
                             SingleEvent.ToastMessageSingleEvent(
                                 message = R.string.dashboard_first_ipscrambling_activation
                             )
+                        is Effect.OpenAppDetailsEffect -> SingleEvent.NavigateToAppDetailsEvent(effect.appDesc)
                         else -> null
                     }
                 }
diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
index 3f849a6..398a594 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/features/dashboard/DashboardFragment.kt
@@ -46,6 +46,7 @@ import foundation.e.privacycentralapp.features.dashboard.DashboardFeature.State
 import foundation.e.privacycentralapp.features.internetprivacy.InternetPrivacyFragment
 import foundation.e.privacycentralapp.features.location.FakeLocationFragment
 import foundation.e.privacycentralapp.features.trackers.TrackersFragment
+import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFragment
 import kotlinx.coroutines.FlowPreview
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
@@ -118,6 +119,13 @@ class DashboardFragment :
                             addToBackStack("dashboard")
                         }
                     }
+                    is DashboardFeature.SingleEvent.NavigateToAppDetailsEvent -> {
+                        requireActivity().supportFragmentManager.commit {
+                            replace<AppTrackersFragment>(R.id.container, args = AppTrackersFragment.buildArgs(event.appDesc.label.toString(), event.appDesc.packageName))
+                            setReorderingAllowed(true)
+                            addToBackStack("dashboard")
+                        }
+                    }
                     DashboardFeature.SingleEvent.NewStatisticsAvailableSingleEvent -> {
                         viewModel.submitAction(DashboardFeature.Action.FetchStatistics)
                     }
@@ -138,6 +146,9 @@ class DashboardFragment :
 
         graphHolder = GraphHolder(binding.graph, requireContext())
 
+        binding.leakingAppButton.setOnClickListener {
+            viewModel.submitAction(DashboardFeature.Action.ShowMostLeakedApp)
+        }
         binding.togglePrivacyCentral.setOnClickListener {
             viewModel.submitAction(DashboardFeature.Action.TogglePrivacyAction)
         }
@@ -247,10 +258,12 @@ class DashboardFragment :
         if (state.dayStatistics?.all { it.first == 0 && it.second == 0 } == true) {
             binding.graph.visibility = View.INVISIBLE
             binding.graphLegend.isVisible = false
+            binding.leakingAppButton.isVisible = false
             binding.graphEmpty.isVisible = true
         } else {
             binding.graph.isVisible = true
             binding.graphLegend.isVisible = true
+            binding.leakingAppButton.isVisible = true
             binding.graphEmpty.isVisible = false
             state.dayStatistics?.let { graphHolder.data = it }
             state.dayLabels?.let { graphHolder.labels = it }
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 34ddfbe..25443e9 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
@@ -109,7 +109,6 @@ class TrackersFeature(
             actor = { state, action ->
                 when (action) {
                     Action.InitAction -> merge<Effect>(
-                        flowOf(Effect.NewStatisticsAvailablesEffect),
                         trackersStatisticsUseCase.listenUpdates().map {
                             Effect.NewStatisticsAvailablesEffect
                         },
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 ad82337..f6d7d67 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
@@ -55,8 +55,10 @@ class AppTrackersFeature(
         val isBlockingActivated: Boolean = false,
         val trackers: List<Tracker>? = null,
         val whitelist: List<String>? = null,
+        val leaked: Int = 0,
+        val blocked: Int = 0,
         val isQuickPrivacyEnabled: Boolean = false,
-        val showQuickPrivacyDisabledMessage: Boolean = false
+        val showQuickPrivacyDisabledMessage: Boolean = false,
     ) {
         fun getTrackersStatus(): List<Pair<Tracker, Boolean>>? {
             if (trackers != null && whitelist != null) {
@@ -92,7 +94,11 @@ class AppTrackersFeature(
         data class ErrorEffect(val message: Any) : Effect()
         data class SetAppEffect(val appDesc: ApplicationDescription) : Effect()
         data class AppTrackersBlockingActivatedEffect(val isBlockingActivated: Boolean) : Effect()
-        data class AvailableTrackersListEffect(val trackers: List<Tracker>) : Effect()
+        data class AvailableTrackersListEffect(
+            val trackers: List<Tracker>,
+            val blocked: Int,
+            val leaked: Int
+        ) : Effect()
         data class TrackersWhitelistUpdateEffect(val whitelist: List<String>) : Effect()
         object NewStatisticsAvailablesEffect : Effect()
         data class QuickPrivacyUpdatedEffect(val enabled: Boolean) : Effect()
@@ -114,7 +120,11 @@ class AppTrackersFeature(
             reducer = { state, effect ->
                 when (effect) {
                     is Effect.SetAppEffect -> state.copy(appDesc = effect.appDesc)
-                    is Effect.AvailableTrackersListEffect -> state.copy(trackers = effect.trackers)
+                    is Effect.AvailableTrackersListEffect -> state.copy(
+                        trackers = effect.trackers,
+                        leaked = effect.leaked,
+                        blocked = effect.blocked
+                    )
 
                     is Effect.AppTrackersBlockingActivatedEffect ->
                         state.copy(isBlockingActivated = effect.isBlockingActivated)
@@ -135,7 +145,6 @@ class AppTrackersFeature(
                             .getApplicationDescription(action.packageName)?.let { appDesc ->
                                 merge<Effect>(
                                     flow {
-
                                         emit(Effect.SetAppEffect(appDesc))
                                         emit(
                                             Effect.AppTrackersBlockingActivatedEffect(
@@ -147,11 +156,6 @@ class AppTrackersFeature(
                                                 trackersStateUseCase.getTrackersWhitelistIds(appDesc.uid)
                                             )
                                         )
-                                        emit(
-                                            Effect.AvailableTrackersListEffect(
-                                                trackers = trackersStatisticsUseCase.getTrackers(appDesc.uid)
-                                            )
-                                        )
                                     },
                                     trackersStatisticsUseCase.listenUpdates().map {
                                         Effect.NewStatisticsAvailablesEffect
@@ -208,8 +212,12 @@ class AppTrackersFeature(
                     }
                     is Action.FetchStatistics -> flowOf(
                         state.appDesc?.uid?.let {
+                            val (blocked, leaked) = trackersStatisticsUseCase.getCalls(it)
+
                             Effect.AvailableTrackersListEffect(
-                                trackers = trackersStatisticsUseCase.getTrackers(it)
+                                trackers = trackersStatisticsUseCase.getTrackers(it),
+                                leaked = leaked,
+                                blocked = blocked,
                             )
                         } ?: Effect.ErrorEffect("No appDesc.")
                     )
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 7e606af..406d26c 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
@@ -143,7 +143,9 @@ class AppTrackersFragment :
         else getString(
             R.string.apptrackers_trackers_count_summary,
             state.getBlockedTrackersCount(),
-            state.getTrackersCount()
+            state.getTrackersCount(),
+            state.blocked,
+            state.leaked
         )
 
         binding.blockAllToggle.isChecked = state.isBlockingActivated
diff --git a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
index 5159827..92a156a 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/main/MainActivity.kt
@@ -27,21 +27,11 @@ import androidx.fragment.app.commit
 import foundation.e.privacycentralapp.PrivacyCentralApplication
 import foundation.e.privacycentralapp.R
 import foundation.e.privacycentralapp.features.dashboard.DashboardFragment
+import foundation.e.privacycentralapp.features.trackers.TrackersFragment
 import kotlinx.coroutines.FlowPreview
 
 @FlowPreview
 open class MainActivity : FragmentActivity(R.layout.activity_main) {
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        if (savedInstanceState == null) {
-            supportFragmentManager.commit {
-                setReorderingAllowed(true)
-                add<DashboardFragment>(R.id.container)
-            }
-        }
-    }
-
     override fun onPostCreate(savedInstanceState: Bundle?) {
         super.onPostCreate(savedInstanceState)
         handleIntent(intent)
@@ -65,6 +55,10 @@ open class MainActivity : FragmentActivity(R.layout.activity_main) {
                     containerViewId = R.id.container,
                     args = intent.extras
                 )
+                ACTION_VIEW_TRACKERS -> {
+                    add<TrackersFragment>(R.id.container)
+                    addToBackStack("dashboard")
+                }
                 else -> add<DashboardFragment>(R.id.container)
             }
         }
@@ -82,11 +76,17 @@ open class MainActivity : FragmentActivity(R.layout.activity_main) {
 
     companion object {
         private const val ACTION_HIGHLIGHT_LEAKS = "ACTION_HIGHLIGHT_LEAKS"
+        private const val ACTION_VIEW_TRACKERS = "ACTION_VIEW_TRACKERS"
 
         fun createHighlightLeaksIntent(context: Context, highlightIndex: Int) =
             Intent(context, MainActivity::class.java).apply {
                 action = ACTION_HIGHLIGHT_LEAKS
                 putExtras(DashboardFragment.buildArgs(highlightIndex))
             }
+
+        fun createTrackersIntent(context: Context) =
+            Intent(context, MainActivity::class.java).apply {
+                action = ACTION_VIEW_TRACKERS
+            }
     }
 }
diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt
index 81859e6..ca93617 100644
--- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt
+++ b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt
@@ -127,11 +127,23 @@ fun render(
             setViewVisibility(R.id.graph_legend, View.GONE)
             setViewVisibility(R.id.graph_empty, View.VISIBLE)
             setViewVisibility(R.id.graph_legend_values, View.GONE)
+            setViewVisibility(R.id.graph_view_trackers_btn, View.GONE)
         } else {
             setViewVisibility(R.id.graph, View.VISIBLE)
             setViewVisibility(R.id.graph_legend, View.VISIBLE)
             setViewVisibility(R.id.graph_empty, View.GONE)
             setViewVisibility(R.id.graph_legend_values, View.VISIBLE)
+            setViewVisibility(R.id.graph_view_trackers_btn, View.VISIBLE)
+
+            val pIntent = PendingIntent.getActivity(
+                context,
+                REQUEST_CODE_TRACKERS,
+                MainActivity.createTrackersIntent(context),
+                FLAG_UPDATE_CURRENT
+            )
+
+            setOnClickPendingIntent(R.id.graph_view_trackers_btn, pIntent)
+            // setOnClickPendingIntent(R.id.graph_view_trackers_btn_icon, pIntent)
 
             val graphHeightPx = 26.dpToPxF(context)
             val maxValue =
@@ -146,7 +158,7 @@ fun render(
                 val middlePadding = graphHeightPx - blocked * ratio
                 setViewPadding(blockedBarIds[index], 0, middlePadding.toInt(), 0, 0)
 
-                // leacked (the bar above)
+                // leaked (the bar above)
                 val topPadding = graphHeightPx - (blocked + leaked) * ratio
                 setViewPadding(leakedBarIds[index], 0, topPadding.toInt(), 0, 0)
 
@@ -254,4 +266,5 @@ private val leakedBarIds = listOf(
 
 private const val REQUEST_CODE_DASHBOARD = 1
 private const val REQUEST_CODE_TOGGLE = 2
+private const val REQUEST_CODE_TRACKERS = 3
 private const val REQUEST_CODE_HIGHLIGHT = 100
diff --git a/app/src/main/res/layout/apptrackers_item_tracker_toggle.xml b/app/src/main/res/layout/apptrackers_item_tracker_toggle.xml
index 0371390..db7086f 100644
--- a/app/src/main/res/layout/apptrackers_item_tracker_toggle.xml
+++ b/app/src/main/res/layout/apptrackers_item_tracker_toggle.xml
@@ -20,10 +20,10 @@
         android:textSize="14sp"
         tools:text="Body sensor"
         />
-        <Switch
-            android:id="@+id/toggle"
-            android:layout_width="wrap_content"
-            android:layout_height="24dp"
-            android:checked="true"
-            />
+    <Switch
+        android:id="@+id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="24dp"
+        android:checked="true"
+        />
 </androidx.appcompat.widget.LinearLayoutCompat>
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index d79dea1..f6beca6 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -30,7 +30,7 @@
                     android:layout_height="25dp"
                     android:layout_width="24dp"
                     android:layout_margin="16dp"
-                    android:src="@drawable/ic_shield_off"
+                    android:src="@drawable/ic_shield_off_white"
                     android:scaleType="fitCenter"
                     />
                 <TextView
@@ -247,6 +247,21 @@ android:text="@string/dashboard_state_ipaddress_off"
                     android:text="@string/graph_legend_allowed"
                     />
 
+                <TextView
+                    android:id="@+id/leaking_app_button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textSize="12sp"
+                    android:textColor="@color/primary_text"
+                    android:drawableEnd="@drawable/ic_chevron_right_24dp"
+                    android:text="@string/dashboard_graph_leaking_app"
+                    app:layout_constraintRight_toRightOf="parent"
+                    app:layout_constraintBottom_toBottomOf="@+id/graph_legend_blocked_icon"
+                    app:layout_constraintTop_toTopOf="@+id/graph_legend_blocked_icon"
+                    android:gravity="center_vertical|end"
+                    android:layout_marginRight="16dp"
+                    />
+
                 <TextView
                     android:id="@+id/graph_empty"
                     android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/widget.xml b/app/src/main/res/layout/widget.xml
index e55d8a3..999a888 100644
--- a/app/src/main/res/layout/widget.xml
+++ b/app/src/main/res/layout/widget.xml
@@ -687,6 +687,7 @@
         android:layout_marginTop="16dp"
         android:layout_marginHorizontal="24dp"
         android:layout_marginBottom="24dp"
+        android:gravity="center_vertical"
         >
 
         <ImageView
@@ -726,6 +727,20 @@
             android:textSize="12sp"
             android:text="@string/graph_legend_allowed"
             />
+
+        <TextView
+            android:id="@+id/graph_view_trackers_btn"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:layout_height="wrap_content"
+            android:textSize="12sp"
+            android:textAlignment="textEnd"
+            android:gravity="center_vertical|end"
+            android:textColor="@color/on_primary_high_emphasis"
+            android:drawableEnd="@drawable/ic_chevron_right_24dp"
+            android:drawableTint="@color/on_primary_high_emphasis"
+            android:text="@string/widget_graph_view_trackers"
+            />
     </LinearLayout>
 
     <TextView
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 47970a1..c0a8582 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -62,7 +62,7 @@
     <string name="trackers_title">Gérer les pisteurs des applications</string>
     <string name="trackers_count_label">%d pisteurs</string>
     <string name="trackers_applist_title">Gérer les pisteurs utilisés dans les applications :</string>
-    <string name="trackers_app_trackers_counts">%1$d pisteurs bloqués sur %2$d</string>
+    <string name="trackers_app_trackers_counts">%1$d pisteurs bloqués sur %2$d, </string>
     <string name="apptrackers_block_all_toggle">Bloquer les pisteurs</string>
     <string name="apptrackers_trackers_list_title">Optez pour les pisteurs que vous souhaitez activer/désactiver.</string>
     <string name="apptrackers_no_trackers_yet_block_off">Pour l\'instant, aucun pisteurs n\'a été détecté. Si de nouveaux pisteurs sont détectés, ils seront mis à jour ici.</string>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 489dff2..c1f15f0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -27,6 +27,7 @@
     <string name="dashboard_graph_label">Personal data leakage:</string>
     <string name="dashboard_graph_period">Today</string>
     <string name="dashboard_graph_trackers_legend">%s trackers have profiled you in the last 24 hours</string>
+    <string name="dashboard_graph_leaking_app">View</string>
     <string name="dashboard_first_ipscrambling_activation">While your IP is faked, your internet speed is likely to be reduced.</string>
 
     <string name="dashboard_am_i_tracked_title">Manage apps\' trackers</string>
@@ -77,7 +78,7 @@
     <string name="trackers_graph_hours_period_format">HH:mm</string>
     <string name="trackers_graph_days_period_format">MMMM d - EEE</string>
     <string name="trackers_graph_months_period_format">MMMM yyyy</string>
-    <string name="trackers_app_trackers_counts">%1$d blocked trackers out of %2$d</string>
+    <string name="trackers_app_trackers_counts">%1$d blocked trackers out of %2$d, %3$d leaks</string>
 
     <!-- App Trackers -->
     <string name="apptrackers_block_all_toggle">Block trackers</string>
@@ -85,7 +86,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="apptrackers_trackers_count_summary">%1$d blocked trackers out of %2$d detected trackers, %3$d blocked leaks and %4$d allowed leaks.</string>
     <string name="apptrackers_error_no_app">App not installed.</string>
 
     <!-- -->
@@ -140,6 +141,7 @@
     <string name="widget_state_ipaddress_off">@string/dashboard_state_ipaddress_off</string>
     <string name="widget_state_ipaddress_on">@string/dashboard_state_ipaddress_on</string>
     <string name="widget_graph_trackers_legend">@string/dashboard_graph_trackers_legend</string>
+    <string name="widget_graph_view_trackers">View</string>
 
     <string name="first_notification_title">Discover Advanced Privacy</string>
     <string name="first_notification_summary">Tap to find out how to easily block trackers, fake your location &amp; hide your IP address.</string>
diff --git a/build.gradle b/build.gradle
index c7815e7..be0a6cf 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ buildscript {
             'major': 1,
             'minor': 0,
             'patch': 0,
-            'build': "milestone-5",
+            'build': "RC-1",
         ],
     ]
 
-- 
cgit v1.2.1