diff --git a/app/src/main/java/foundation/e/advancedprivacy/common/ChipSpan.kt b/app/src/main/java/foundation/e/advancedprivacy/common/ChipSpan.kt
new file mode 100644
index 0000000000000000000000000000000000000000..19cd47d8c5b903ef46d318a9e710969dce95b2fb
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/common/ChipSpan.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2025 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 .
+ */
+
+package foundation.e.advancedprivacy.common
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Rect
+import android.graphics.RectF
+import android.text.style.ReplacementSpan
+import androidx.annotation.ColorRes
+import androidx.core.content.ContextCompat
+import foundation.e.advancedprivacy.common.extensions.dpToPx
+import foundation.e.advancedprivacy.common.extensions.dpToPxF
+
+class ChipSpan(
+ context: Context,
+ paddingHorizontal: Int,
+ radius: Int,
+ height: Int,
+ @ColorRes backgroundColorId: Int,
+ @ColorRes textColorId: Int
+
+) : ReplacementSpan() {
+ private val paddingPx = paddingHorizontal.dpToPx(context)
+ private val chipHeight = height.dpToPxF(context)
+ private val radiusPx = radius.dpToPxF(context)
+
+ private val textColor = ContextCompat.getColor(context, textColorId)
+ private val backgroundPaint = Paint().apply {
+ color = ContextCompat.getColor(context, backgroundColorId)
+ style = Paint.Style.FILL
+ isAntiAlias = true
+ }
+
+ private var size: Int = 0
+
+ override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
+ val textRect = Rect()
+ paint.getTextBounds(text, start, end, textRect)
+ size = textRect.width() + 2 * paddingPx
+ return size
+ }
+
+ override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
+ val top = y + (paint.fontMetrics.ascent + paint.fontMetrics.descent - chipHeight) / 2
+ val backgroundRect = RectF(
+ x,
+ top,
+ x + size,
+ top + chipHeight
+ )
+ canvas.drawRoundRect(backgroundRect, radiusPx, radiusPx, backgroundPaint)
+
+ paint.color = textColor
+ canvas.drawText(text.slice(start until end).toString(), x + paddingPx, y.toFloat(), paint)
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt
index c12a6cde691d91f020af10f2efc2e9bfcfb7d588..19ddc85260701f84f94d2eee8e4d99aed7bca217 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/TrackersAndAppsListsUseCase.kt
@@ -60,7 +60,7 @@ class TrackersAndAppsListsUseCase(
}
private suspend fun get5MostTrackedAppsLastMonth(): List {
- val countByApIds = statsDatabase.getCallsByAppIds(since = Period.MONTH.getPeriodStart().epochSecond)
+ val countByApIds = statsDatabase.getCallsByAppIds(start = Period.MONTH.getPeriodStart(), end = Instant.now())
val countByApps = mutableMapOf()
countByApIds.forEach { (apId, count) ->
diff --git a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/WeeklyReportUseCase.kt b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/WeeklyReportUseCase.kt
index 08eff4168d7a6b21ef5be7d7c125d2c7cce679ff..c61b7bad92090bd640ad5638e62dfa6ee1e59a64 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/WeeklyReportUseCase.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/domain/usecases/WeeklyReportUseCase.kt
@@ -27,6 +27,8 @@ import foundation.e.advancedprivacy.trackers.data.TrackersRepository
import java.time.Duration
import java.time.Instant
import java.time.temporal.ChronoUnit
+import kotlin.collections.component1
+import kotlin.collections.component2
import kotlin.random.Random
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -107,9 +109,15 @@ class WeeklyReportUseCase(
return candidates.map { computeScore(it, history) }.sortedBy { it.score }
}
- private fun addCallPerAppCandidates(candidates: MutableList, endOfWeek: Instant) {
- val anyApp = appListRepository.displayableApps.value.first().id
- val callsPerWeek = Random.nextInt(10000)
+ private suspend fun addCallPerAppCandidates(candidates: MutableList, endOfWeek: Instant) {
+ val startOfWeek = getStartOfWeek(endOfWeek)
+ val (appIdWithMostCalls, callsPerWeek) = statsDatabase.getCallsByAppIds(startOfWeek, endOfWeek).maxBy { it.value }
+ val app = appListRepository.getAppById(appIdWithMostCalls)?.id
+
+ // TODO: get second one on fail ?
+ if (app == null) { // appId isn't on the phone anymore, skip.
+ return
+ }
val hoursInWeek = 7 * 24
candidates.add(
@@ -117,7 +125,7 @@ class WeeklyReportUseCase(
endOfWeek,
WeeklyReport.StatType.CALLS_PER_APP,
WeeklyReport.LabelId.CALLS_PER_APP_1,
- anyApp,
+ app,
listOf((callsPerWeek / hoursInWeek).toString())
)
)
@@ -127,7 +135,7 @@ class WeeklyReportUseCase(
endOfWeek,
WeeklyReport.StatType.CALLS_PER_APP,
WeeklyReport.LabelId.CALLS_PER_APP_2,
- anyApp,
+ app,
listOf((callsPerWeek / hoursInWeek).toString())
)
)
@@ -137,7 +145,7 @@ class WeeklyReportUseCase(
endOfWeek,
WeeklyReport.StatType.CALLS_PER_APP,
WeeklyReport.LabelId.CALLS_PER_APP_3,
- anyApp,
+ app,
listOf((callsPerWeek / 7).toString())
)
)
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/CallsPerAppViewFactory.kt b/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/CallsPerAppViewFactory.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6f21a407869948326bf09809ffcaad22dd192f3c
--- /dev/null
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/CallsPerAppViewFactory.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2025 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 .
+ */
+
+package foundation.e.advancedprivacy.features.weeklyreport
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.text.buildSpannedString
+import androidx.core.text.inSpans
+import foundation.e.advancedprivacy.R
+import foundation.e.advancedprivacy.common.ChipSpan
+import foundation.e.advancedprivacy.databinding.WeeklyReportItemCallsPerAppBinding
+import foundation.e.advancedprivacy.databinding.WeeklyReportItemCallsPerAppForSharingBinding
+import foundation.e.advancedprivacy.domain.entities.weeklyreport.DisplayableReport
+import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReport
+
+class CallsPerAppViewFactory(private val context: Context) {
+ fun getShareTitle(): CharSequence {
+ return context.getString(R.string.weeklyreport_share_title_base)
+ }
+
+ fun createView(report: DisplayableReport, inflater: LayoutInflater, viewGroup: ViewGroup): View? {
+ if (report !is DisplayableReport.ReportWithApp) {
+ return null
+ }
+
+ val binding = WeeklyReportItemCallsPerAppBinding.inflate(inflater, viewGroup, false)
+
+ binding.appIcon.setImageDrawable(report.app.icon)
+
+ binding.title.text = getTitle(report)
+ binding.description.text = buildCallsPerAppDescription(report, false)
+ return binding.root
+ }
+
+ fun createViewForSharing(report: DisplayableReport, inflater: LayoutInflater, viewGroup: ViewGroup): View? {
+ if (report !is DisplayableReport.ReportWithApp) {
+ return null
+ }
+
+ val binding = WeeklyReportItemCallsPerAppForSharingBinding.inflate(inflater, viewGroup, false)
+ binding.appIcon.setImageDrawable(report.app.icon)
+ binding.title.text = getTitle(report)
+ binding.description.text = buildCallsPerAppDescription(report, true)
+ return binding.root
+ }
+
+ fun getTitle(report: DisplayableReport): CharSequence {
+ return context.getString(
+ when (report.report.labelId) {
+ WeeklyReport.LabelId.CALLS_PER_APP_1 ->
+ R.string.weeklyreport_label_calls_per_app_1_title
+
+ WeeklyReport.LabelId.CALLS_PER_APP_2 ->
+ R.string.weeklyreport_label_calls_per_app_2_title
+
+ WeeklyReport.LabelId.CALLS_PER_APP_3 ->
+ R.string.weeklyreport_label_calls_per_app_3_title
+
+ else ->
+ R.string.empty
+ }
+ )
+ }
+
+ fun getDescriptionForNotification(report: DisplayableReport): String {
+ if (report !is DisplayableReport.ReportWithApp) {
+ return ""
+ }
+ return buildCallsPerAppDescription(report, forSharing = false).toString()
+ }
+
+ fun getIconForNotification(report: DisplayableReport): Drawable? {
+ return if (report is DisplayableReport.ReportWithApp) {
+ report.app.icon
+ } else {
+ null
+ }
+ }
+
+ private fun buildCallsPerAppDescription(report: DisplayableReport.ReportWithApp, forSharing: Boolean = false): CharSequence {
+ val labelId = report.report.labelId
+ val desc1 = context.getString(
+ when (labelId) {
+ WeeklyReport.LabelId.CALLS_PER_APP_1 -> R.string.weeklyreport_label_calls_per_app_1_description_1
+ WeeklyReport.LabelId.CALLS_PER_APP_2 -> R.string.weeklyreport_label_calls_per_app_2_description_1
+ WeeklyReport.LabelId.CALLS_PER_APP_3 -> R.string.weeklyreport_label_calls_per_app_3_description_1
+ else -> R.string.empty
+ },
+ report.app.label
+ )
+
+ val counts = report.report.secondaryValues.firstOrNull() ?: ""
+
+ val desc2 = context.getString(
+ when {
+ labelId == WeeklyReport.LabelId.CALLS_PER_APP_1 && forSharing ->
+ R.string.weeklyreport_label_calls_per_app_1_description_2_sharing
+ labelId == WeeklyReport.LabelId.CALLS_PER_APP_1 ->
+ R.string.weeklyreport_label_calls_per_app_1_description_2
+ labelId == WeeklyReport.LabelId.CALLS_PER_APP_2 && forSharing ->
+ R.string.weeklyreport_label_calls_per_app_2_description_2_sharing
+ labelId == WeeklyReport.LabelId.CALLS_PER_APP_2 ->
+ R.string.weeklyreport_label_calls_per_app_2_description_2
+ labelId == WeeklyReport.LabelId.CALLS_PER_APP_3 && forSharing ->
+ R.string.weeklyreport_label_calls_per_app_3_description_2_sharing
+ labelId == WeeklyReport.LabelId.CALLS_PER_APP_3 ->
+ R.string.weeklyreport_label_calls_per_app_3_description_2
+ else -> R.string.empty
+ }
+ )
+
+ return buildSpannedString {
+ append(desc1)
+ append(" ")
+ inSpans(
+ ChipSpan(
+ context = context,
+ paddingHorizontal = 8,
+ radius = 12,
+ height = 16,
+ backgroundColorId = if (forSharing) R.color.share_blue_highlight_number else R.color.divider,
+ textColorId = if (forSharing) R.color.white else R.color.secondary_text
+ )
+ ) {
+ append(counts)
+ }
+ append(" ")
+ append(desc2)
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/WeeklyReportViewFactory.kt b/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/WeeklyReportViewFactory.kt
index 9e9857e1342343215d3afe5b220b720ff83ad95f..25fe0d850a77a8e5452ab2b962cbd929322e2d27 100644
--- a/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/WeeklyReportViewFactory.kt
+++ b/app/src/main/java/foundation/e/advancedprivacy/features/weeklyreport/WeeklyReportViewFactory.kt
@@ -38,6 +38,7 @@ import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReport
class WeeklyReportViewFactory(private val context: Context) {
val newTrackersViewFactory: NewTrackersViewFactory by lazy { NewTrackersViewFactory(context) }
val trackerWithMostAppsViewFactory: TrackerWithMostAppsViewFactory by lazy { TrackerWithMostAppsViewFactory(context) }
+ val callsPerAppViewFactory: CallsPerAppViewFactory by lazy { CallsPerAppViewFactory(context) }
fun getShareTitle(report: DisplayableReport): CharSequence {
return when (report.report.statType) {
@@ -45,6 +46,8 @@ class WeeklyReportViewFactory(private val context: Context) {
newTrackersViewFactory.getShareTitle(report)
WeeklyReport.StatType.TRACKER_WITH_MOST_APPS ->
trackerWithMostAppsViewFactory.getShareTitle()
+ WeeklyReport.StatType.CALLS_PER_APP ->
+ callsPerAppViewFactory.getShareTitle()
else -> context.getString(R.string.weeklyreport_share_title_base)
}
}
@@ -55,6 +58,8 @@ class WeeklyReportViewFactory(private val context: Context) {
newTrackersViewFactory.createView(report, inflater, viewGroup, onClick)
WeeklyReport.StatType.TRACKER_WITH_MOST_APPS ->
trackerWithMostAppsViewFactory.createView(report, inflater, viewGroup, onClick)
+ WeeklyReport.StatType.CALLS_PER_APP ->
+ callsPerAppViewFactory.createView(report, inflater, viewGroup)
else -> null
} ?: createDefaultView(report, inflater, viewGroup)
}
@@ -109,6 +114,9 @@ class WeeklyReportViewFactory(private val context: Context) {
newTrackersViewFactory.createViewForSharing(report, inflater, viewGroup)
WeeklyReport.StatType.TRACKER_WITH_MOST_APPS ->
trackerWithMostAppsViewFactory.createViewForSharing(report, inflater, viewGroup)
+ WeeklyReport.StatType.CALLS_PER_APP ->
+ callsPerAppViewFactory.createViewForSharing(report, inflater, viewGroup)
+
else -> null
} ?: createDefaultView(report, inflater, viewGroup)
}
@@ -128,6 +136,9 @@ class WeeklyReportViewFactory(private val context: Context) {
newTrackersViewFactory.getTitle(report)
WeeklyReport.StatType.TRACKER_WITH_MOST_APPS ->
trackerWithMostAppsViewFactory.getTitle()
+ WeeklyReport.StatType.CALLS_PER_APP ->
+ callsPerAppViewFactory.getTitle(report)
+
else -> report.report.labelId.name
}
}
@@ -138,6 +149,9 @@ class WeeklyReportViewFactory(private val context: Context) {
newTrackersViewFactory.getDescriptionForNotification(report)
WeeklyReport.StatType.TRACKER_WITH_MOST_APPS ->
trackerWithMostAppsViewFactory.getDescriptionForNotification(report)
+ WeeklyReport.StatType.CALLS_PER_APP ->
+ callsPerAppViewFactory.getDescriptionForNotification(report)
+
else -> getDefaultDescription(report).toString()
}
}
@@ -158,6 +172,8 @@ class WeeklyReportViewFactory(private val context: Context) {
newTrackersViewFactory.getIconForNotification(report)
WeeklyReport.StatType.TRACKER_WITH_MOST_APPS ->
trackerWithMostAppsViewFactory.getIconForNotification()
+ WeeklyReport.StatType.CALLS_PER_APP ->
+ callsPerAppViewFactory.getIconForNotification(report)
else -> null
}
}
diff --git a/app/src/main/res/layout/fragment_trackers.xml b/app/src/main/res/layout/fragment_trackers.xml
index 8d0cb39f383911de35d8222c5ae59a8eea0b3531..c14164e49d48aea3734ba46e2c864452989911d7 100644
--- a/app/src/main/res/layout/fragment_trackers.xml
+++ b/app/src/main/res/layout/fragment_trackers.xml
@@ -62,6 +62,10 @@
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/weeklyreport_section_title"
+ android:layout_marginTop="24dp"
+ android:textFontWeight="500"
+ android:textSize="14sp"
+ android:lineHeight="24dp"
/>
+
+
+ >
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/weekly_report_item_calls_per_app_for_sharing.xml b/app/src/main/res/layout/weekly_report_item_calls_per_app_for_sharing.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8438893db8689ca6f7c99498a570800179f1e6e8
--- /dev/null
+++ b/app/src/main/res/layout/weekly_report_item_calls_per_app_for_sharing.xml
@@ -0,0 +1,65 @@
+
+
+
+ >
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 111f3cc706c04debbaf060072224d33712cfa4ff..3ca581e1545c67a1bc24d2772ac8060766151d86 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -187,4 +187,20 @@
Wöchentlicher Bericht
Eine wöchentliche Benachrichtigung, die eine Zusammenfassung der Tracker zeigt und Änderungen sowie Updates hervorhebt.
+
+ Top-Datenschutz-Verletzer:
+
+ %s machte
+ Tracking-Versuche pro Stunde in dieser Woche.
+ Tracking-Versuche pro Stunde in dieser Woche auf meinem Smartphone.
+
+ Invasivste App:
+ %s hat Datenlecks
+ mal pro Stunde in dieser Woche.
+ mal pro Stunde in dieser Woche auf meinem Smartphone.
+
+ Größter Datenlecker:
+ %s hat Daten gesendet
+ mal pro Tag in dieser Woche.
+ mal pro Tag in dieser Woche auf meinem Smartphone.
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 92c48eea74a94fb0f67ecaa39fa1b8768d3c8b40..d040f70f9072e64a9bfe7aceeccc138753146ddb 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -160,8 +160,6 @@
Informe semanal:
https://e.foundation/wp-content/uploads/2025/01/White_Paper_-_Privacy_-_ES.pdf
- ¿Impresionado?
-
¡Corre la voz!
¡Nuevo espía en la ciudad!
Esta semana
@@ -187,4 +185,21 @@
Informe Semanal
Una notificación semanal que muestra un resumen de los rastreadores, destacando cualquier cambio y actualización en ellos.
+
+ ¿Impresionado?
+ Principal infractor de privacidad:
+ %s ha realizado
+ intentos de seguimiento por hora esta semana.
+ intentos de seguimiento por hora esta semana en mi smartphone.
+
+ Aplicación más invasiva:
+ %s ha intentado filtrar datos
+ veces por hora esta semana.
+ veces por hora esta semana en mi smartphone.
+
+ Mayor filtrador de datos:
+ %s ha intentado enviar datos
+ veces por día esta semana.
+ veces por día esta semana en mi smartphone.
+
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 6e31124436c0272d67db9432641853a498d72154..817a2c5d900ab2cab9466ea7907a60f8e3a5568e 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -185,4 +185,21 @@
Rapport Hebdomadaire
Une notification hebdomadaire présentant un résumé des pisteurs, mettant en évidence les changements et mises à jour.
+
+ Principal contrevenant à la vie privée :
+
+ %s a éffectué
+ tentatives de pistage par heure cette semaine.
+ tentatives de pistage par heure cette semaine sur mon smartphone.
+
+ Application la plus invasive :
+ %s a tenté de divulguer des données
+ fois par heure cette semaine.
+ fois par heure cette semaine sur mon smartphone.
+
+ Plus grand divulgateur de données :
+ %s a tenté d\'envoyer des données
+ fois par jour cette semaine.
+ fois par jour cette semaine sur mon smartphone.
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index f9ef4a3096e771801acc9b65d2c51d6229cd1442..8bed1dfd74ccf1b6584ca2a33118f9d8403e8747 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -188,4 +188,21 @@
Rapporto Settimanale
Una notifica settimanale che mostra un riepilogo dei tracker, evidenziando eventuali modifiche e aggiornamenti.
+
+ Principale violatore della privacy:
+
+ %s ha fatto
+ tentativi di tracciamento all\'ora questa settimana.
+ tentativi di tracciamento all\'ora questa settimana sul mio smartphone.
+
+ App più invasiva:
+ %s ha tentato di divulgare dati
+ volte all\'ora questa settimana.
+ volte all\'ora questa settimana sul mio smartphone.
+
+ Maggiore divulgatore di dati:
+ %s ha tentato di inviare dati
+ volte al giorno questa settimana.
+ volte al giorno questa settimana sul mio smartphone.
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7df349f37fa26310995c9e1684e7d0e4248985bf..989ca8fbc2d57beadaf18ab72647abb18ba2fce1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -218,6 +218,21 @@
is present in %s apps on my phone.
View apps
+ Top Privacy Offender:
+
+ %s made
+ tracking attempts per hour this week.
+ tracking attempts per hour this week on my smartphone.
+
+ Most Invasive App:
+ %s leaked data
+ times per hour this week.
+ times per hour this week on my smartphone.
+ Biggest Data Leaker:
+ %s sent data
+ times per day this week.
+ times per day this week on my smartphone.
+
@string/app_name
@@ -250,7 +265,6 @@
Highlight that the trackers are actually logged and blocked by Advanced Privacy
Tracker control is on
This could impact the functioning of some applications.
-
Weekly Report
A weekly notification showing a summary of the trackers, highlighting any changes and updates in them.
diff --git a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt
index 49b9f4c1c60ebceadec7774cf54a0cc7d551d6f6..7c6222c53e512a4fe2e96e8b7e98b1987aef05e3 100644
--- a/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt
+++ b/trackers/src/main/java/foundation/e/advancedprivacy/trackers/data/StatsDatabase.kt
@@ -371,11 +371,11 @@ class StatsDatabase(
}
}
- suspend fun getCallsByAppIds(since: Long): Map = withContext(Dispatchers.IO) {
+ suspend fun getCallsByAppIds(start: Instant, end: Instant): Map = withContext(Dispatchers.IO) {
synchronized(lock) {
val db = readableDatabase
- val selection = "$COLUMN_NAME_TIMESTAMP >= ?"
- val selectionArg = arrayOf("" + since)
+ val selection = "$COLUMN_NAME_TIMESTAMP >= ? AND $COLUMN_NAME_TIMESTAMP <= ?"
+ val selectionArg = arrayOf("" + start.epochSecond, "" + end.epochSecond)
val projection = "$COLUMN_NAME_APPID, " +
"SUM($COLUMN_NAME_NUMBER_CONTACTED) $PROJECTION_NAME_CONTACTED_SUM"
val cursor = db.rawQuery(