feat(di): Hilt modules for the database, repositories and clocks
The database and its DAOs, the four repository bindings, and the Android implementations of the two clocks — so nothing constructs a Room database or reads a system clock by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,8 +21,8 @@ import java.io.File
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clockula's storage bindings. M1 wires the preference half only; the Room
|
* The DataStore half of Clockula's storage. The Room database and its DAOs live
|
||||||
* database and its DAOs join this module in M2.
|
* next door in `DatabaseModule`.
|
||||||
*/
|
*/
|
||||||
@Module
|
@Module
|
||||||
@InstallIn(SingletonComponent::class)
|
@InstallIn(SingletonComponent::class)
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package de.jeanlucmakiola.clockula.data.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.Provides
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import de.jeanlucmakiola.clockula.data.alarms.AlarmDao
|
||||||
|
import de.jeanlucmakiola.clockula.data.db.ClockulaDatabase
|
||||||
|
import de.jeanlucmakiola.clockula.data.stopwatch.LapDao
|
||||||
|
import de.jeanlucmakiola.clockula.data.timers.TimerDao
|
||||||
|
import de.jeanlucmakiola.clockula.data.worldclocks.WorldClockDao
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The database and its four DAOs. No destructive-migration fallback: docs/PLAN.md
|
||||||
|
* §12 wants every schema change to arrive with a tested migration, and a fallback
|
||||||
|
* would quietly drop a user's alarms instead of failing loudly in review.
|
||||||
|
*/
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
object DatabaseModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideDatabase(@ApplicationContext context: Context): ClockulaDatabase =
|
||||||
|
Room.databaseBuilder(context, ClockulaDatabase::class.java, ClockulaDatabase.NAME).build()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideAlarmDao(database: ClockulaDatabase): AlarmDao = database.alarmDao()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideTimerDao(database: ClockulaDatabase): TimerDao = database.timerDao()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideWorldClockDao(database: ClockulaDatabase): WorldClockDao = database.worldClockDao()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun provideLapDao(database: ClockulaDatabase): LapDao = database.lapDao()
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package de.jeanlucmakiola.clockula.data.di
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import de.jeanlucmakiola.clockula.data.alarms.AlarmRepository
|
||||||
|
import de.jeanlucmakiola.clockula.data.alarms.AlarmRepositoryImpl
|
||||||
|
import de.jeanlucmakiola.clockula.data.stopwatch.StopwatchRepository
|
||||||
|
import de.jeanlucmakiola.clockula.data.stopwatch.StopwatchRepositoryImpl
|
||||||
|
import de.jeanlucmakiola.clockula.data.timers.TimerRepository
|
||||||
|
import de.jeanlucmakiola.clockula.data.timers.TimerRepositoryImpl
|
||||||
|
import de.jeanlucmakiola.clockula.data.worldclocks.WorldClockRepository
|
||||||
|
import de.jeanlucmakiola.clockula.data.worldclocks.WorldClockRepositoryImpl
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/** The four interfaces the rest of the app sees — and the only ones it may see. */
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
abstract class RepositoryModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun bindAlarmRepository(impl: AlarmRepositoryImpl): AlarmRepository
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun bindTimerRepository(impl: TimerRepositoryImpl): TimerRepository
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun bindWorldClockRepository(impl: WorldClockRepositoryImpl): WorldClockRepository
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun bindStopwatchRepository(impl: StopwatchRepositoryImpl): StopwatchRepository
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package de.jeanlucmakiola.clockula.data.di
|
||||||
|
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import dagger.hilt.InstallIn
|
||||||
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import de.jeanlucmakiola.clockula.data.time.SystemElapsedRealtimeClock
|
||||||
|
import de.jeanlucmakiola.clockula.data.time.SystemWallClock
|
||||||
|
import de.jeanlucmakiola.clockula.domain.time.ElapsedRealtimeClock
|
||||||
|
import de.jeanlucmakiola.clockula.domain.time.WallClock
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Both clocks are injected, never called statically, so the wall-clock /
|
||||||
|
* elapsed-realtime distinction can be tested from both sides.
|
||||||
|
*/
|
||||||
|
@Module
|
||||||
|
@InstallIn(SingletonComponent::class)
|
||||||
|
abstract class TimeModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindWallClock(impl: SystemWallClock): WallClock
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindElapsedRealtimeClock(impl: SystemElapsedRealtimeClock): ElapsedRealtimeClock
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package de.jeanlucmakiola.clockula.data.time
|
||||||
|
|
||||||
|
import android.os.SystemClock
|
||||||
|
import de.jeanlucmakiola.clockula.domain.time.ElapsedRealtimeClock
|
||||||
|
import de.jeanlucmakiola.clockula.domain.time.WallClock
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
import kotlin.time.Instant
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** The device's calendar time. Moves with the user, the network and DST. */
|
||||||
|
class SystemWallClock @Inject constructor() : WallClock {
|
||||||
|
override fun now(): Instant = Instant.fromEpochMilliseconds(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Milliseconds since boot, including deep sleep. Never decreases within a boot
|
||||||
|
* and no setting can move it — which is exactly why a running timer and the
|
||||||
|
* stopwatch anchor on it.
|
||||||
|
*/
|
||||||
|
class SystemElapsedRealtimeClock @Inject constructor() : ElapsedRealtimeClock {
|
||||||
|
override fun elapsedRealtime(): Duration = SystemClock.elapsedRealtime().milliseconds
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user