Pith - kiln
kiln/app/src/main/java/com/vgmlr/kiln/KilnAlertWorker.kt [4.4 kb]
Modified: 00:02:27 153 026 (19 Aug 026)
5 Days Ago
package com.vgmlr.kiln

import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import java.time.Duration
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.util.concurrent.TimeUnit

class KilnAlertWorker(context: Context, params: WorkerParameters) :
    CoroutineWorker(context, params) {

    override suspend fun doWork(): Result {
        val ctx = applicationContext
        if (!KilnNotifications.allowed(ctx)) return Result.success()

        val db = KilnDb.get(ctx)
        val prefs = KilnPrefs.get(ctx)
        val today = LocalDate.now()

        for (account in db.accountDao().getAll()) {
            if (!account.pmsAlert && !account.menstruationAlert) continue
            val records = db.dayRecordDao().getAll(account.id)
            val buttons = KilnButtonSet(db.buttonDao().getAll(account.id))
            val starts = KilnCyclePredictor.periodStarts(records, buttons)
            if (starts.isEmpty()) continue
            val cycle = KilnCyclePredictor.avgCycleLength(starts)

            if (account.pmsAlert) {
                val hueOf = KilnGradientPalette.lookup(records, buttons, starts, cycle)
                pms(ctx, prefs, account, today, cycle, hueOf)
            }
            if (account.menstruationAlert) {
                menstruation(ctx, prefs, account, today, starts, cycle)
            }
        }
        return Result.success()
    }
    
    private fun pms(
        ctx: Context,
        prefs: KilnPrefs,
        account: KilnAccount,
        today: LocalDate,
        cycle: Int,
        hueOf: (LocalDate) -> Float
    ) {
        if (KilnDayStats.preMenstrualIn(today, cycle, hueOf) != 0L) return
        val day = today.toEpochDay()
        if (prefs.alertDay(KilnPrefs.KEY_PMS_DAY, account.id) == day) return
        prefs.setAlertDay(KilnPrefs.KEY_PMS_DAY, account.id, day)
        KilnNotifications.pms(ctx, account)
    }

    private fun menstruation(
        ctx: Context,
        prefs: KilnPrefs,
        account: KilnAccount,
        today: LocalDate,
        starts: List<LocalDate>,
        cycle: Int
    ) {
        val id = account.id
        val day = today.toEpochDay()
        val anchor = prefs.alertDay(KilnPrefs.KEY_PERIOD_ANCHOR, id)
        val due = starts.none { it == today } &&
                KilnDayStats.daysToPeriod(today, starts, cycle) == 0L

        if (due && anchor != day) {
            prefs.setAlertDay(KilnPrefs.KEY_PERIOD_ANCHOR, id, day)
            prefs.clearAlertDay(KilnPrefs.KEY_PERIOD_DISMISSED, id)
        } else {
            if (!account.menstruationReminder || anchor == KilnPrefs.NO_DAY) return
            if (day !in (anchor + 1)..(anchor + RUN_DAYS - 1)) return
            if (prefs.alertDay(KilnPrefs.KEY_PERIOD_DISMISSED, id) == anchor) return
            if (starts.any { it.toEpochDay() >= anchor }) return 
        }
        if (prefs.alertDay(KilnPrefs.KEY_PERIOD_DAY, id) == day) return
        prefs.setAlertDay(KilnPrefs.KEY_PERIOD_DAY, id, day)
        KilnNotifications.menstruation(ctx, account)
    }

    companion object {
        private const val WORK_NAME = "alert_sync"
        private const val ONCE_NAME = "alert_sync_once"
        private const val RUN_DAYS = 5
        private val ALERT_TIME: LocalTime = LocalTime.of(9, 0)

        fun schedule(context: Context) {
            WorkManager.getInstance(context).enqueueUniquePeriodicWork(
                WORK_NAME,
                ExistingPeriodicWorkPolicy.KEEP,
                PeriodicWorkRequestBuilder<KilnAlertWorker>(1, TimeUnit.DAYS)
                    .setInitialDelay(minutesUntilAlert(), TimeUnit.MINUTES)
                    .build()
            )
        }

        fun runOnce(context: Context) {
            WorkManager.getInstance(context).enqueueUniqueWork(
                ONCE_NAME,
                ExistingWorkPolicy.REPLACE,
                OneTimeWorkRequestBuilder<KilnAlertWorker>().build()
            )
        }

        private fun minutesUntilAlert(): Long {
            val now = LocalDateTime.now()
            var next = now.toLocalDate().atTime(ALERT_TIME)
            if (!next.isAfter(now)) next = next.plusDays(1)
            return Duration.between(now, next).toMinutes()
        }
    }
}
Updates
Kerf - Android 157.026
Kiln - Android 157.026
Wedge - Android 156.026
Whittle - Linux 155.026
Grit - Firefox/Chrome 154.026

Menu
Calendar
Project Tin (024/029)
Miter
RSS Feed
User Avatar
@vgmlr
=SUM(parts)
0.00278
257,110 (+364)