From bdf9198bb069f2ab9619cb4d3a85f5c2cbf9f0cd Mon Sep 17 00:00:00 2001 From: jacquarg Date: Tue, 5 Apr 2022 18:53:40 +0200 Subject: 5217 - Display blocked trackers, 5216 : add leaks in tooltips --- .../e/privacycentralapp/common/GraphHolder.kt | 67 ++- .../privacycentralapp/common/NavToolbarFragment.kt | 1 - .../domain/entities/TrackersPeriodicStatistics.kt | 2 +- .../domain/usecases/TrackersStatisticsUseCase.kt | 8 +- .../features/dashboard/DashboardFeature.kt | 8 +- .../features/dashboard/DashboardFragment.kt | 2 +- .../features/trackers/TrackersFragment.kt | 4 +- .../e/privacycentralapp/widget/WidgetUI.kt | 51 +- app/src/main/res/drawable/ic_legend_blocked.xml | 21 + app/src/main/res/drawable/ic_legend_leaked.xml | 21 + app/src/main/res/layout/fragment_dashboard.xml | 49 ++ app/src/main/res/layout/trackers_item_graph.xml | 49 ++ app/src/main/res/layout/widget.xml | 522 +++++++++++++++++---- app/src/main/res/values/strings.xml | 2 + 14 files changed, 671 insertions(+), 136 deletions(-) create mode 100644 app/src/main/res/drawable/ic_legend_blocked.xml create mode 100644 app/src/main/res/drawable/ic_legend_leaked.xml (limited to 'app/src') diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/GraphHolder.kt b/app/src/main/java/foundation/e/privacycentralapp/common/GraphHolder.kt index 929d838..aba3a95 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/GraphHolder.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/GraphHolder.kt @@ -19,9 +19,14 @@ package foundation.e.privacycentralapp.common import android.content.Context import android.graphics.Canvas +import android.text.Spannable +import android.text.SpannableStringBuilder +import android.text.style.DynamicDrawableSpan +import android.text.style.ImageSpan import android.view.View import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.core.text.toSpannable import androidx.core.view.isVisible import com.github.mikephil.charting.charts.BarChart import com.github.mikephil.charting.components.MarkerView @@ -37,7 +42,7 @@ import foundation.e.privacycentralapp.R import foundation.e.privacycentralapp.extensions.dpToPxF class GraphHolder(val barChart: BarChart, val context: Context, val isMarkerAbove: Boolean = true) { - var data = emptyList() + var data = emptyList>() set(value) { field = value refreshDataSet() @@ -80,7 +85,17 @@ class GraphHolder(val barChart: BarChart, val context: Context, val isMarkerAbov setOnChartValueSelectedListener(object : OnChartValueSelectedListener { override fun onValueSelected(e: Entry?, h: Highlight?) { - h?.let { periodMarker.setLabel(labels.getOrNull(it.x.toInt())) } + h?.let { + val index = it.x.toInt() + if (index > 0 && + index < labels.size && + index < this@GraphHolder.data.size + ) { + val period = labels[index] + val (blocked, leaked) = this@GraphHolder.data[index] + periodMarker.setLabel(period, blocked, leaked) + } + } isHighlighted = true refreshDataSet() } @@ -95,18 +110,31 @@ class GraphHolder(val barChart: BarChart, val context: Context, val isMarkerAbov private fun refreshDataSet() { val trackersDataSet = BarDataSet( - data.mapIndexed { index, value -> BarEntry(index.toFloat(), value.toFloat()) }, + data.mapIndexed { index, value -> + BarEntry( + index.toFloat(), + floatArrayOf(value.first.toFloat(), value.second.toFloat()) + ) + }, "" ).apply { - color = ContextCompat.getColor( - context, - if (isHighlighted) R.color.blue_unselected else R.color.accent + + val blockedColor = ContextCompat.getColor(context, R.color.accent) + val leakedColor = ContextCompat.getColor(context, R.color.red_off) + + // ColorUtils.setAlphaComponent() + colors = listOf( + blockedColor, + // if (isHighlighted) R.color.blue_unselected else R.color.accent + leakedColor ) + setDrawValues(false) - highLightColor = ContextCompat.getColor( - context, R.color.accent - ) - highLightAlpha = 255 + + // highLightColor = ContextCompat.getColor( + // context, R.color.accent + // ) + // highLightAlpha = 255 } barChart.data = BarData(trackersDataSet) @@ -162,8 +190,23 @@ class PeriodMarkerView(context: Context, private val isMarkerAbove: Boolean = tr } } - fun setLabel(label: String?) { - findViewById(R.id.label).text = label + fun setLabel(period: String, blocked: Int, leaked: Int) { + val span = SpannableStringBuilder(period) + span.append(": $blocked ") + span.setSpan( + ImageSpan(context, R.drawable.ic_legend_blocked, DynamicDrawableSpan.ALIGN_BASELINE), + span.length - 1, + span.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + span.append(" $leaked ") + span.setSpan( + ImageSpan(context, R.drawable.ic_legend_leaked, DynamicDrawableSpan.ALIGN_BASELINE), + span.length - 1, + span.length, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE + ) + findViewById(R.id.label).text = span.toSpannable() } override fun refreshContent(e: Entry?, highlight: Highlight?) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt index c28c871..6955405 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/common/NavToolbarFragment.kt @@ -19,7 +19,6 @@ package foundation.e.privacycentralapp.common import androidx.annotation.LayoutRes import com.google.android.material.appbar.MaterialToolbar -import foundation.e.privacycentralapp.R abstract class NavToolbarFragment(@LayoutRes contentLayoutId: Int) : ToolbarFragment(contentLayoutId) { diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackersPeriodicStatistics.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackersPeriodicStatistics.kt index 8a27d6d..b3a6ade 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackersPeriodicStatistics.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/entities/TrackersPeriodicStatistics.kt @@ -18,7 +18,7 @@ package foundation.e.privacycentralapp.domain.entities data class TrackersPeriodicStatistics( - val calls: List, + val callsBlockedNLeaked: List>, val periods: List, val trackersCount: Int ) 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 69dd0d8..4262055 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 @@ -53,7 +53,7 @@ class TrackersStatisticsUseCase( fun getDayStatistics(): Pair { return TrackersPeriodicStatistics( - calls = trackTrackersPrivacyModule.getPastDayTrackersCalls(), + callsBlockedNLeaked = trackTrackersPrivacyModule.getPastDayTrackersCalls(), periods = buildDayLabels(), trackersCount = trackTrackersPrivacyModule.getPastDayTrackersCount() ) to trackTrackersPrivacyModule.getTrackersCount() @@ -107,17 +107,17 @@ class TrackersStatisticsUseCase( return with(trackTrackersPrivacyModule) { Triple( TrackersPeriodicStatistics( - calls = getPastDayTrackersCalls(), + callsBlockedNLeaked = getPastDayTrackersCalls(), periods = buildDayLabels(), trackersCount = getPastDayTrackersCount() ), TrackersPeriodicStatistics( - calls = getPastMonthTrackersCalls(), + callsBlockedNLeaked = getPastMonthTrackersCalls(), periods = buildMonthLabels(), trackersCount = getPastMonthTrackersCount() ), TrackersPeriodicStatistics( - calls = getPastYearTrackersCalls(), + callsBlockedNLeaked = getPastYearTrackersCalls(), periods = buildYearLabels(), trackersCount = getPastYearTrackersCount() ) 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 87f5e42..8a4ee54 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 @@ -57,7 +57,7 @@ class DashboardFeature( // val graphData val trackersCount: Int? = null, val activeTrackersCount: Int? = null, - val dayStatistics: List? = null, + val dayStatistics: List>? = null, val dayLabels: List? = null ) @@ -84,7 +84,7 @@ class DashboardFeature( data class UpdateStateEffect(val isEnabled: Boolean) : Effect() data class IpScramblingModeUpdatedEffect(val mode: InternetPrivacyMode) : Effect() data class TrackersStatisticsUpdatedEffect( - val dayStatistics: List, + val dayStatistics: List>, val dayLabels: List, val dayTrackersCount: Int, val trackersCount: Int @@ -151,7 +151,7 @@ class DashboardFeature( trackersStatisticsUseCase.getDayStatistics().let { (dayStatistics, trackersCount) -> Effect.TrackersStatisticsUpdatedEffect( - dayStatistics = dayStatistics.calls, + dayStatistics = dayStatistics.callsBlockedNLeaked, dayLabels = dayStatistics.periods, dayTrackersCount = dayStatistics.trackersCount, trackersCount = trackersCount @@ -176,7 +176,7 @@ class DashboardFeature( trackersStatisticsUseCase.getDayStatistics().let { (dayStatistics, trackersCount) -> Effect.TrackersStatisticsUpdatedEffect( - dayStatistics = dayStatistics.calls, + dayStatistics = dayStatistics.callsBlockedNLeaked, dayLabels = dayStatistics.periods, dayTrackersCount = dayStatistics.trackersCount, trackersCount = trackersCount 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 dec3234..51dee3d 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 @@ -217,7 +217,7 @@ class DashboardFragment : ) ) - if (state.dayStatistics?.all { it == 0 } == true) { + if (state.dayStatistics?.all { it.first == 0 && it.second == 0 } == true) { binding.graph.visibility = View.INVISIBLE binding.graphLegend.isVisible = false binding.graphEmpty.isVisible = true 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 c0212a5..893f4ba 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 @@ -137,13 +137,13 @@ class TrackersFragment : graphHolder: GraphHolder, graphBinding: TrackersItemGraphBinding ) { - if (statistics.calls.all { it == 0 }) { + if (statistics.callsBlockedNLeaked.all { it.first == 0 && it.second == 0 }) { graphBinding.graph.visibility = View.INVISIBLE graphBinding.graphEmpty.isVisible = true } else { graphBinding.graph.isVisible = true graphBinding.graphEmpty.isVisible = false - graphHolder.data = statistics.calls + graphHolder.data = statistics.callsBlockedNLeaked graphHolder.labels = statistics.periods graphBinding.trackersCountLabel.text = getString(R.string.trackers_count_label, statistics.trackersCount) 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 443683a..2529f6c 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt @@ -39,7 +39,7 @@ data class State( val isAllTrackersBlocked: Boolean = false, val locationMode: LocationMode = LocationMode.REAL_LOCATION, val internetPrivacyMode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP, - val dayStatistics: List = emptyList(), + val dayStatistics: List> = emptyList(), val activeTrackersCount: Int = 0, ) { val isTrackersDenied get() = isQuickPrivacyEnabled && isAllTrackersBlocked @@ -123,23 +123,33 @@ fun render( setViewVisibility(R.id.state_ip_address_loader, if (loading) View.VISIBLE else View.GONE) - if (state.dayStatistics.all { it == 0 }) { + if (state.dayStatistics.all { it.first == 0 && it.second == 0 }) { setViewVisibility(R.id.graph, View.GONE) setViewVisibility(R.id.graph_legend, View.GONE) setViewVisibility(R.id.graph_empty, View.VISIBLE) + setViewVisibility(R.id.graph_legend_values, 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) val graphHeightPx = 26.dpToPxF(context) val maxValue = - state.dayStatistics.maxOrNull().let { if (it == null || it == 0) 1 else it } + state.dayStatistics + .map { it.first + it.second } + .maxOrNull() + .let { if (it == null || it == 0) 1 else it } val ratio = graphHeightPx / maxValue - state.dayStatistics.zip(barIds).forEach { (value, viewId) -> - val topPadding = graphHeightPx - value * ratio - setViewPadding(viewId, 0, topPadding.toInt(), 0, 0) + state.dayStatistics.forEachIndexed { index, (blocked, leaked) -> + // blocked (the bar below) + val middlePadding = graphHeightPx - blocked * ratio + setViewPadding(blockedBarIds[index], 0, middlePadding.toInt(), 0, 0) + + // leacked (the bar above) + val topPadding = graphHeightPx - (blocked + leaked) * ratio + setViewPadding(leakedBarIds[index], 0, topPadding.toInt(), 0, 0) } setTextViewText( @@ -155,7 +165,7 @@ fun render( appWidgetManager.updateAppWidget(ComponentName(context, Widget::class.java), views) } -private val barIds = listOf( +private val blockedBarIds = listOf( R.id.widget_graph_bar_0, R.id.widget_graph_bar_1, R.id.widget_graph_bar_2, @@ -181,3 +191,30 @@ private val barIds = listOf( R.id.widget_graph_bar_22, R.id.widget_graph_bar_23 ) + +private val leakedBarIds = listOf( + R.id.widget_leaked_graph_bar_0, + R.id.widget_leaked_graph_bar_1, + R.id.widget_leaked_graph_bar_2, + R.id.widget_leaked_graph_bar_3, + R.id.widget_leaked_graph_bar_4, + R.id.widget_leaked_graph_bar_5, + R.id.widget_leaked_graph_bar_6, + R.id.widget_leaked_graph_bar_7, + R.id.widget_leaked_graph_bar_8, + R.id.widget_leaked_graph_bar_9, + R.id.widget_leaked_graph_bar_10, + R.id.widget_leaked_graph_bar_11, + R.id.widget_leaked_graph_bar_12, + R.id.widget_leaked_graph_bar_13, + R.id.widget_leaked_graph_bar_14, + R.id.widget_leaked_graph_bar_15, + R.id.widget_leaked_graph_bar_16, + R.id.widget_leaked_graph_bar_17, + R.id.widget_leaked_graph_bar_18, + R.id.widget_leaked_graph_bar_19, + R.id.widget_leaked_graph_bar_20, + R.id.widget_leaked_graph_bar_21, + R.id.widget_leaked_graph_bar_22, + R.id.widget_leaked_graph_bar_23 +) diff --git a/app/src/main/res/drawable/ic_legend_blocked.xml b/app/src/main/res/drawable/ic_legend_blocked.xml new file mode 100644 index 0000000..15f8c56 --- /dev/null +++ b/app/src/main/res/drawable/ic_legend_blocked.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_legend_leaked.xml b/app/src/main/res/drawable/ic_legend_leaked.xml new file mode 100644 index 0000000..a96501f --- /dev/null +++ b/app/src/main/res/drawable/ic_legend_leaked.xml @@ -0,0 +1,21 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml index 53ec617..ff64733 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -188,6 +188,55 @@ android:text="@string/dashboard_state_ipaddress_off" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toBottomOf="@+id/graph" /> + + + + + + + + + + + + + + - - + + + + + - + + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + + - + + + + + - + + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + - + + + + + - + + + + + - + + + + - + android:layout_marginRight="1.5dp" + > + + + - - - - - - + + + + + + + + + + + System Congratulations! No trackers are profiling you. + Blocked leaks + Allowed leaks @string/app_name -- cgit v1.2.1