Loading app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt +3 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading @@ -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), Loading app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -138,6 +139,8 @@ val appModule = module { single { CityDataSource } single { ResourcesRepository(androidContext()) } single { WeeklyReportWorkerScheduler(androidContext()) } singleOf(::FakeLocationStateUseCase) single { Loading app/src/main/java/foundation/e/advancedprivacy/data/repositories/WeeklyReportLocalRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -64,4 +64,8 @@ class WeeklyReportLocalRepository( }.getOrNull() } ?: emptyList() } suspend fun clearAllReports() { return store.removeKey(weeklyReportsKey) } } app/src/main/java/foundation/e/advancedprivacy/domain/entities/weeklyreport/WeeklyReport.kt +6 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package foundation.e.advancedprivacy.domain.entities.weeklyreport import foundation.e.advancedprivacy.core.utils.InstantSerializer import java.time.Instant import java.time.LocalDate import java.time.ZoneId import kotlinx.serialization.Serializable @Serializable Loading @@ -30,6 +32,10 @@ data class WeeklyReport( val primaryValue: String, val secondaryValues: List<String> ) { fun getDate(): LocalDate { return timestamp.atZone(ZoneId.systemDefault()).toLocalDate() } @Serializable enum class StatType { CALLS_PER_APP, Loading app/src/main/java/foundation/e/advancedprivacy/domain/usecases/WeeklyReportUseCase.kt +15 −16 Original line number Diff line number Diff line Loading @@ -22,18 +22,19 @@ 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 import java.time.DayOfWeek import java.time.DayOfWeek.SUNDAY import java.time.Instant import java.time.LocalDate 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 import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch Loading @@ -43,44 +44,42 @@ 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 REPORT_DAY_OF_WEEK: DayOfWeek = SUNDAY const val REPORT_HOUR: Long = 11L } 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 if (now < endOfDisplay) { if (LocalDate.now() == weeklyReport.getDate()) { _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>>> = Loading Loading
app/src/main/java/foundation/e/advancedprivacy/AdvancedPrivacyApplication.kt +3 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) Loading @@ -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), Loading
app/src/main/java/foundation/e/advancedprivacy/KoinModule.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -138,6 +139,8 @@ val appModule = module { single { CityDataSource } single { ResourcesRepository(androidContext()) } single { WeeklyReportWorkerScheduler(androidContext()) } singleOf(::FakeLocationStateUseCase) single { Loading
app/src/main/java/foundation/e/advancedprivacy/data/repositories/WeeklyReportLocalRepository.kt +4 −0 Original line number Diff line number Diff line Loading @@ -64,4 +64,8 @@ class WeeklyReportLocalRepository( }.getOrNull() } ?: emptyList() } suspend fun clearAllReports() { return store.removeKey(weeklyReportsKey) } }
app/src/main/java/foundation/e/advancedprivacy/domain/entities/weeklyreport/WeeklyReport.kt +6 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package foundation.e.advancedprivacy.domain.entities.weeklyreport import foundation.e.advancedprivacy.core.utils.InstantSerializer import java.time.Instant import java.time.LocalDate import java.time.ZoneId import kotlinx.serialization.Serializable @Serializable Loading @@ -30,6 +32,10 @@ data class WeeklyReport( val primaryValue: String, val secondaryValues: List<String> ) { fun getDate(): LocalDate { return timestamp.atZone(ZoneId.systemDefault()).toLocalDate() } @Serializable enum class StatType { CALLS_PER_APP, Loading
app/src/main/java/foundation/e/advancedprivacy/domain/usecases/WeeklyReportUseCase.kt +15 −16 Original line number Diff line number Diff line Loading @@ -22,18 +22,19 @@ 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 import java.time.DayOfWeek import java.time.DayOfWeek.SUNDAY import java.time.Instant import java.time.LocalDate 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 import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch Loading @@ -43,44 +44,42 @@ 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 REPORT_DAY_OF_WEEK: DayOfWeek = SUNDAY const val REPORT_HOUR: Long = 11L } 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 if (now < endOfDisplay) { if (LocalDate.now() == weeklyReport.getDate()) { _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>>> = Loading