Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1fa0641 authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

feat:8998: revert display last tracker call in tracker notification

parent e043734f
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
    <!-- Use STATUS_BAR_SERVICE permission to hide the persistent notification -->
    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />

    <application
+0 −2
Original line number Diff line number Diff line
@@ -239,11 +239,9 @@ val appModule = module {
        NotificationsPresenter(
            context = androidContext(),
            getQuickPrivacyStateUseCase = get(),
            trackersStatisticsUseCase = get(),
            permissionsPrivacyModule = get(),
            weeklyReportUseCase = get(),
            weeklyReportViewFactory = get(),
            notificationTrackerFlag = get(named("notificationTrackerFlag")),
            appScope = get()
        )
    }
+4 −41
Original line number Diff line number Diff line
@@ -31,18 +31,15 @@ import foundation.e.advancedprivacy.domain.entities.CHANNEL_FIRST_BOOT
import foundation.e.advancedprivacy.domain.entities.CHANNEL_IPSCRAMBLING_FLAG
import foundation.e.advancedprivacy.domain.entities.CHANNEL_TRACKER_FLAG
import foundation.e.advancedprivacy.domain.entities.CHANNEL_WEEKLYREPORT
import foundation.e.advancedprivacy.domain.entities.DisplayableTrackerCall
import foundation.e.advancedprivacy.domain.entities.FeatureMode
import foundation.e.advancedprivacy.domain.entities.FeatureState
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FAKE_LOCATION_FLAG
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_FIRST_BOOT
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_IPSCRAMBLING_FLAG
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_TRACKER_FLAG
import foundation.e.advancedprivacy.domain.entities.NOTIFICATION_WEEKLYREPORT
import foundation.e.advancedprivacy.domain.entities.NotificationContent
import foundation.e.advancedprivacy.domain.entities.weeklyreport.DisplayableReport
import foundation.e.advancedprivacy.domain.usecases.GetQuickPrivacyStateUseCase
import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
import foundation.e.advancedprivacy.domain.usecases.WeeklyReportUseCase
import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
import foundation.e.advancedprivacy.features.weeklyreport.WeeklyReportViewFactory
@@ -55,23 +52,18 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.ZoneId
import java.time.format.DateTimeFormatter


// @SuppressLint("MissingPermission")
class NotificationsPresenter(
    private val context: Context,
    private val getQuickPrivacyStateUseCase: GetQuickPrivacyStateUseCase,
    private val trackersStatisticsUseCase: TrackersStatisticsUseCase,
    private val permissionsPrivacyModule: IPermissionsPrivacyModule,
    private val weeklyReportUseCase: WeeklyReportUseCase,
    private val weeklyReportViewFactory: WeeklyReportViewFactory,
    private val notificationTrackerFlag: NotificationContent,
    private val appScope: CoroutineScope
) {
    companion object {
        private const val HIDE_CHANNEL_DELAY = 30000L
        private const val HIDE_CHANNEL_DELAY_IN_MS = 30000L
    }

    private val notificationManager = NotificationManagerCompat.from(context)
@@ -115,7 +107,7 @@ class NotificationsPresenter(
            channelDescription = R.string.notifications_ipscrambling_channel_description
        )

        createTrackerForegroundserviceChannel()
        createTrackerForegroundServiceChannel()

        createWeeklyReportChannel()

@@ -146,8 +138,6 @@ class NotificationsPresenter(
                notificationManager.cancel(NOTIFICATION_WEEKLYREPORT)
            }
        }.launchIn(appScope)

        trackersStatisticsUseCase.lastCall.map(::showLastTrackerCall).launchIn(appScope)
    }

    private fun createNotificationFirstBootChannel() {
@@ -175,7 +165,7 @@ class NotificationsPresenter(
        NotificationManagerCompat.from(context).createNotificationChannel(channel)
    }

    private fun createTrackerForegroundserviceChannel() {
    private fun createTrackerForegroundServiceChannel() {
        val channel = NotificationChannel(
            CHANNEL_TRACKER_FLAG,
            context.getString(R.string.notifications_tracker_channel_name),
@@ -188,7 +178,7 @@ class NotificationsPresenter(
        notificationManager.createNotificationChannel(channel)

        appScope.launch {
            delay(HIDE_CHANNEL_DELAY)
            delay(HIDE_CHANNEL_DELAY_IN_MS)
            permissionsPrivacyModule.hideNotificationsChannel(CHANNEL_TRACKER_FLAG)
        }
    }
@@ -261,33 +251,6 @@ class NotificationsPresenter(
        )
    }

    private fun showLastTrackerCall(lastCall: DisplayableTrackerCall?) {
        val builder = notificationBuilder(context = context, content = notificationTrackerFlag)
        if (lastCall != null) {
            builder.setContentTitle(context.getString(R.string.notifications_tracker_detected_title))
            builder.setContentText(
                context.getString(
                    if (lastCall.wasBlocked) {
                        R.string.notifications_tracker_blocked_leak
                    } else {
                        R.string.notifications_tracker_allowed_leak
                    },
                    lastCall.displayableApp.label,
                    lastCall.tracker.label,
                    DateTimeFormatter.ofPattern(
                        context.getString(R.string.notifications_tracker_timestamp_format)
                    ).format(lastCall.timestamp.atZone(ZoneId.systemDefault()))

                )
            )
        }

        notificationManager.notify(
            NOTIFICATION_TRACKER_FLAG,
            builder.build()
        )
    }

    private fun hideFlagNotification(id: Int) {
        NotificationManagerCompat.from(context).cancel(id)
    }
+0 −28
Original line number Diff line number Diff line
/*
 * 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 <https://www.gnu.org/licenses/>.
 */

package foundation.e.advancedprivacy.domain.entities

import foundation.e.advancedprivacy.trackers.domain.entities.Tracker
import java.time.Instant

data class DisplayableTrackerCall(
    val displayableApp: DisplayableApp,
    val tracker: Tracker,
    val timestamp: Instant,
    val wasBlocked: Boolean
)
+2 −34
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 - 2025 E FOUNDATION
 * Copyright (C) 2022 - 2024 MURENA SAS
 * Copyright (C) 2021 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
@@ -24,8 +24,6 @@ import foundation.e.advancedprivacy.data.repositories.AppListRepository
import foundation.e.advancedprivacy.data.repositories.ResourcesRepository
import foundation.e.advancedprivacy.domain.entities.ApplicationDescription
import foundation.e.advancedprivacy.domain.entities.DisplayableApp
import foundation.e.advancedprivacy.domain.entities.DisplayableTrackerCall
import foundation.e.advancedprivacy.domain.entities.TrackerCall
import foundation.e.advancedprivacy.domain.entities.TrackersPeriodicStatistics
import foundation.e.advancedprivacy.features.trackers.Period
import foundation.e.advancedprivacy.trackers.data.StatsDatabase
@@ -36,47 +34,17 @@ import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn

class TrackersStatisticsUseCase(
    private val whitelistRepository: WhitelistRepository,
    private val trackersRepository: TrackersRepository,
    private val appListRepository: AppListRepository,
    private val statsDatabase: StatsDatabase,
    private val resourcesRepository: ResourcesRepository,
    backgroundScope: CoroutineScope
    private val resourcesRepository: ResourcesRepository
) {
    companion object {
        private const val LASTCALL_DISPLAY_DURATION = 5 * 60 * 1000L
    }

    val lastCall: Flow<DisplayableTrackerCall?> = statsDatabase.newDataAvailable
        .mapNotNull(::prepareNotification)
        .shareIn(scope = backgroundScope, started = WhileSubscribed(), replay = 0)

    private fun prepareNotification(trackerCall: TrackerCall): DisplayableTrackerCall? {
        val displayableApp = appListRepository.getAppById(trackerCall.apId)
        val tracker = trackersRepository.getTracker(trackerCall.trackerId)

        return if (tracker != null && displayableApp != null) {
            DisplayableTrackerCall(
                displayableApp = displayableApp,
                tracker = tracker,
                timestamp = trackerCall.timestamp,
                wasBlocked = trackerCall.wasBlocked
            )
        } else {
            null
        }
    }

    fun initAppList() {
        appListRepository.refreshAppDescriptions()
    }
Loading