package dev.privacyllc.period.di import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStoreFile import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import dev.privacyllc.period.core.data.CycleData import dev.privacyllc.period.core.data.CycleRepository import dev.privacyllc.period.core.datastore.UserPreferencesRepository import dev.privacyllc.period.domain.prediction.BaselinePredictionEngine import dev.privacyllc.period.domain.prediction.PredictionEngine import java.time.Clock import javax.inject.Singleton /** * Where the app learns what a file path is, and the only place it does. * * Note what is absent: no `PeriodDatabase`, no DAO, no Room import anywhere in * this module or anywhere above it. `CycleData.repository` hands back a * repository and keeps the storage to itself — see * docs/architecture/README.md. */ @Module @InstallIn(SingletonComponent::class) object DataModule { /** * Singleton because Room and DataStore both are: two instances over one * file is a corruption bug that only shows up under concurrency. */ @Provides @Singleton fun cycleRepository( @ApplicationContext context: Context, engine: PredictionEngine, clock: Clock, ): CycleRepository = CycleData.repository(context, engine, clock) @Provides @Singleton fun preferencesDataStore(@ApplicationContext context: Context): DataStore = PreferenceDataStoreFactory.create { context.preferencesDataStoreFile("user_preferences") } @Provides @Singleton fun userPreferencesRepository(store: DataStore) = UserPreferencesRepository(store) /** * Batch 02 replaces this binding, and only this binding. * * The rest of the app depends on [PredictionEngine], never on an * implementation — so swapping in the real engine is one line here, and the * §51 acceptance tests can run against both to show the replacement is * better rather than merely different. */ @Provides @Singleton fun predictionEngine(): PredictionEngine = BaselinePredictionEngine() /** Injected rather than read from the environment, so §50's date edge cases stay testable. */ @Provides @Singleton fun clock(): Clock = Clock.systemDefaultZone() }