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

Commit 12a48168 authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

fix:3233: weeklyreport infinite loop on sunday

and fix 3240 weeklyreport not dismissed after 24h
parent 0c7d9c04
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import foundation.e.advancedprivacy.domain.usecases.TrackersStateUseCase
import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
import foundation.e.advancedprivacy.domain.usecases.VpnSupervisorUseCase
import foundation.e.advancedprivacy.domain.usecases.WeeklyReportUseCase
import foundation.e.advancedprivacy.externalinterfaces.workers.WeeklyReportWorker
import foundation.e.advancedprivacy.trackers.data.TrackersRepository
import foundation.e.advancedprivacy.trackers.services.UpdateTrackersWorker
import foundation.e.lib.telemetry.Telemetry
@@ -39,12 +38,15 @@ import kotlinx.coroutines.withContext
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.koin.java.KoinJavaComponent.get
import timber.log.Timber

class AdvancedPrivacyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Telemetry.init(BuildConfig.SENTRY_DSN, this, true)

        Timber.plant(Timber.DebugTree())

        startKoin {
            androidContext(this@AdvancedPrivacyApplication)
            modules(appModule)
@@ -59,7 +61,6 @@ class AdvancedPrivacyApplication : Application() {
        get<TrackersRepository>(TrackersRepository::class.java).initTrackersFile()

        UpdateTrackersWorker.periodicUpdate(this@AdvancedPrivacyApplication)
        WeeklyReportWorker.scheduleNext(this@AdvancedPrivacyApplication)

        WarningDialog.startListening(
            get(ShowFeaturesWarningUseCase::class.java),
+3 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import foundation.e.advancedprivacy.domain.usecases.TrackersStatisticsUseCase
import foundation.e.advancedprivacy.domain.usecases.WeeklyReportUseCase
import foundation.e.advancedprivacy.dummy.CityDataSource
import foundation.e.advancedprivacy.externalinterfaces.permissions.IPermissionsPrivacyModule
import foundation.e.advancedprivacy.externalinterfaces.workers.WeeklyReportWorkerScheduler
import foundation.e.advancedprivacy.features.dashboard.DashboardViewModel
import foundation.e.advancedprivacy.features.internetprivacy.InternetPrivacyViewModel
import foundation.e.advancedprivacy.features.location.FakeLocationViewModel
@@ -138,6 +139,8 @@ val appModule = module {
    single { CityDataSource }
    single { ResourcesRepository(androidContext()) }

    single { WeeklyReportWorkerScheduler(androidContext()) }

    singleOf(::FakeLocationStateUseCase)

    single {
+11 −13
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import foundation.e.advancedprivacy.data.repositories.WeeklyReportLocalRepositor
import foundation.e.advancedprivacy.domain.entities.weeklyreport.DisplayableReport
import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReport
import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReportScore
import foundation.e.advancedprivacy.externalinterfaces.workers.WeeklyReportWorkerScheduler
import foundation.e.advancedprivacy.trackers.data.StatsDatabase
import foundation.e.advancedprivacy.trackers.data.TrackersRepository
import java.time.Duration
@@ -32,8 +33,6 @@ import kotlin.collections.component2
import kotlin.random.Random
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@@ -43,44 +42,43 @@ class WeeklyReportUseCase(
    private val trackersRepository: TrackersRepository,
    private val appListRepository: AppListRepository,
    private val weeklyReportRepository: WeeklyReportLocalRepository,
    private val weeklyReportWorkerScheduler: WeeklyReportWorkerScheduler,
    private val statsDatabase: StatsDatabase,
    private val scope: CoroutineScope
) {
    companion object {
        val DISPLAY_DURATION: Duration = Duration.ofDays(1)
    }
    private val _currentReport = MutableStateFlow<DisplayableReport?>(null)
    val currentReport: StateFlow<DisplayableReport?> = _currentReport

    private val displayDuration: Duration = Duration.ofDays(1)

    private var stopDisplayJob: Job? = null

    fun listen() {
        scope.launch {
            updateCurrent()
            val currentReport = currentReport.value?.report
            weeklyReportWorkerScheduler.scheduleNext(currentReport)
            weeklyReportWorkerScheduler.scheduleDismiss(currentReport)
        }
    }

    suspend fun updateCurrent() {
        val weeklyReport = weeklyReportRepository.getLastWeeklyReport() ?: return
        val now = Instant.now()
        val endOfDisplay = weeklyReport.timestamp + displayDuration
        val endOfDisplay = weeklyReport.timestamp + DISPLAY_DURATION
        if (now < endOfDisplay) {
            _currentReport.value = weeklyReport.toDisplayableReport()
            stopDisplayJob = scope.launch {
                delay(endOfDisplay.toEpochMilli() - now.toEpochMilli())
                _currentReport.value = null
                stopDisplayJob = null
            }
        } else {
            _currentReport.value = null
        }
    }

    suspend fun updateWeeklyReport() = withContext(Dispatchers.IO) {
    suspend fun updateWeeklyReport(): WeeklyReport = withContext(Dispatchers.IO) {
        val history = weeklyReportRepository.getLast99Reports()
        val weeklyReport = buildCandidates(Instant.now(), history).first().weeklyReport
        weeklyReportRepository.setLastWeeklyReport(weeklyReport)

        updateCurrent()
        weeklyReport
    }

    suspend fun debugGenerateReportsSinceWeeksAgo(weeksAgo: Int): List<Pair<DisplayableReport?, List<WeeklyReportScore>>> =
+57 −18
Original line number Diff line number Diff line
@@ -23,7 +23,10 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import foundation.e.advancedprivacy.domain.entities.weeklyreport.WeeklyReport
import foundation.e.advancedprivacy.domain.usecases.WeeklyReportUseCase
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import java.time.temporal.WeekFields
@@ -34,17 +37,35 @@ import timber.log.Timber
class WeeklyReportWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) {
    override suspend fun doWork(): Result {
        Timber.d("WeeklyReportWorker starts computing this week report")
        val weeklyReportWorkerScheduler: WeeklyReportWorkerScheduler = get(WeeklyReportWorkerScheduler::class.java)
        val weeklyReportUseCase: WeeklyReportUseCase = get(WeeklyReportUseCase::class.java)
        weeklyReportUseCase.updateWeeklyReport()
        scheduleNext(applicationContext)
        val newReport = weeklyReportUseCase.updateWeeklyReport()
        weeklyReportWorkerScheduler.scheduleNext(newReport)
        weeklyReportWorkerScheduler.scheduleDismiss(newReport)
        return Result.success()
    }
}

class WeeklyReportDismissWorker(appContext: Context, workerParams: WorkerParameters) : CoroutineWorker(appContext, workerParams) {
    override suspend fun doWork(): Result {
        Timber.d("WeeklyReportDismissWorker will dismiss any displayed WeeklyReport")
        val weeklyReportUseCase: WeeklyReportUseCase = get(WeeklyReportUseCase::class.java)
        weeklyReportUseCase.updateCurrent()
        return Result.success()
    }
}

    companion object {
        fun scheduleNext(appContext: Context) {
            var next = ZonedDateTime.now()
class WeeklyReportWorkerScheduler(private val appContext: Context) {
    fun scheduleNext(lastReport: WeeklyReport?) {
        val now = ZonedDateTime.now()
        var next = now
        next = next.with(WeekFields.of(appContext.resources.configuration.locales[0]).dayOfWeek(), 7)
        next = next.truncatedTo(ChronoUnit.DAYS)

        if (next.toLocalDate() == lastReport?.timestamp?.atZone(ZoneId.systemDefault())?.toLocalDate() && now.hour >= 11) {
            next = next.plus(7, ChronoUnit.DAYS)
        }

        next = next.plus(11, ChronoUnit.HOURS)

        val delay = next.toEpochSecond() - ZonedDateTime.now().toEpochSecond()
@@ -60,5 +81,23 @@ class WeeklyReportWorker(appContext: Context, workerParams: WorkerParameters) :
            request
        )
    }

    fun scheduleDismiss(currentReport: WeeklyReport?) {
        val next = currentReport?.timestamp?.plus(WeeklyReportUseCase.DISPLAY_DURATION) ?: return
        val delay = next.toEpochMilli() - Instant.now().toEpochMilli()
        if (delay < 0) {
            return
        }

        Timber.d("Schedule Dismiss of WeeklyReport of ${currentReport.timestamp} at $next, in $delay milliseconds")
        val request = OneTimeWorkRequestBuilder<WeeklyReportDismissWorker>()
            .setInitialDelay(delay, TimeUnit.MILLISECONDS)
            .build()

        WorkManager.getInstance(appContext).enqueueUniqueWork(
            WeeklyReportDismissWorker::class.qualifiedName ?: "",
            ExistingWorkPolicy.REPLACE,
            request
        )
    }
}