diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b14587e9..65170af4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,20 +32,19 @@ android { isShrinkResources = true } } + + lint { + disable.add("NullSafeMutableLiveData") + } } dependencies { implementation(projects.core.common) implementation(projects.core.analytics) implementation(projects.core.buildconfig) - implementation(projects.core.network) implementation(projects.core.designsystem) - implementation(projects.core.datastore) - implementation(projects.core.alarm) - implementation(projects.core.media) implementation(projects.core.ui) implementation(projects.data) - implementation(projects.domain) implementation(projects.feature.splash) implementation(projects.feature.onboarding) implementation(projects.feature.home) diff --git a/build-logic/src/main/java/orbit.kotlin.library.gradle.kts b/build-logic/src/main/java/orbit.kotlin.library.gradle.kts index 663168a5..31a66fda 100644 --- a/build-logic/src/main/java/orbit.kotlin.library.gradle.kts +++ b/build-logic/src/main/java/orbit.kotlin.library.gradle.kts @@ -1,7 +1,9 @@ import com.yapp.convention.configureKotlin +import com.yapp.convention.configureTestKotlin plugins { kotlin("jvm") } configureKotlin() +configureTestKotlin() diff --git a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmAlertPendingIntent.kt b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmAlertPendingIntent.kt index cdb80e2c..50854350 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmAlertPendingIntent.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmAlertPendingIntent.kt @@ -6,6 +6,7 @@ import android.content.Context import android.content.Intent import com.yapp.alarm.AlarmConstants import com.yapp.domain.model.Alarm +import com.yapp.domain.model.toJson fun createAlarmAlertPendingIntent( context: Context, @@ -31,7 +32,7 @@ private fun createAlarmAlertIntent( ): Intent { return Intent("com.yapp.alarm.interaction.ACTION_ALARM_INTERACTION").apply { putExtra(AlarmConstants.EXTRA_NOTIFICATION_ID, notificationId) - putExtra(AlarmConstants.EXTRA_ALARM, alarm) + putExtra(AlarmConstants.EXTRA_ALARM, alarm.toJson()) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) setPackage(context.packageName) } diff --git a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmSnoozePendingIntent.kt b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmSnoozePendingIntent.kt index 05fd38b0..2939a693 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmSnoozePendingIntent.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/interaction/AlarmSnoozePendingIntent.kt @@ -7,6 +7,7 @@ import android.content.Intent import com.yapp.alarm.AlarmConstants import com.yapp.alarm.receivers.AlarmReceiver import com.yapp.domain.model.Alarm +import com.yapp.domain.model.toJson fun createAlarmSnoozePendingIntent( context: Context, @@ -27,6 +28,6 @@ fun createAlarmSnoozeIntent( ): Intent { return Intent(context, AlarmReceiver::class.java).apply { action = AlarmConstants.ACTION_ALARM_SNOOZED - putExtra(AlarmConstants.EXTRA_ALARM, alarm) + putExtra(AlarmConstants.EXTRA_ALARM, alarm.toJson()) } } diff --git a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/ScheduleAlarmPendingIntent.kt b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/ScheduleAlarmPendingIntent.kt index 517eedbd..0b8b5430 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/ScheduleAlarmPendingIntent.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/ScheduleAlarmPendingIntent.kt @@ -7,6 +7,7 @@ import com.yapp.alarm.AlarmConstants import com.yapp.alarm.receivers.AlarmReceiver import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay +import com.yapp.domain.model.toJson fun createAlarmReceiverPendingIntentForSchedule( app: Application, @@ -29,7 +30,7 @@ private fun createAlarmReceiverIntent( ): Intent { return Intent(AlarmConstants.ACTION_ALARM_TRIGGERED).apply { setClass(app, AlarmReceiver::class.java) - putExtra(AlarmConstants.EXTRA_ALARM, alarm) + putExtra(AlarmConstants.EXTRA_ALARM, alarm.toJson()) day?.let { putExtra(AlarmConstants.EXTRA_ALARM_DAY, it.name) } } } diff --git a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/UnScheduleAlarmPendingIntent.kt b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/UnScheduleAlarmPendingIntent.kt index 650c9fac..f080395b 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/UnScheduleAlarmPendingIntent.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/pendingIntent/schedule/UnScheduleAlarmPendingIntent.kt @@ -7,6 +7,7 @@ import com.yapp.alarm.AlarmConstants import com.yapp.alarm.receivers.AlarmReceiver import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay +import com.yapp.domain.model.toJson fun createAlarmReceiverPendingIntentForUnSchedule( app: Application, @@ -25,6 +26,6 @@ fun createAlarmReceiverPendingIntentForUnSchedule( private fun createAlarmReceiverIntent(app: Application, alarm: Alarm): Intent { return Intent(AlarmConstants.ACTION_ALARM_TRIGGERED).apply { setClass(app, AlarmReceiver::class.java) - putExtra(AlarmConstants.EXTRA_ALARM, alarm) + putExtra(AlarmConstants.EXTRA_ALARM, alarm.toJson()) } } diff --git a/core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt b/core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt index 011dadef..9ef2dd52 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/receivers/AlarmReceiver.kt @@ -3,7 +3,6 @@ package com.yapp.alarm.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.os.Build import android.util.Log import android.widget.Toast import com.yapp.alarm.AlarmConstants @@ -14,8 +13,8 @@ import com.yapp.analytics.AnalyticsHelper import com.yapp.domain.model.Alarm import com.yapp.domain.model.toAlarmDay import com.yapp.domain.model.toTimeString +import com.yapp.domain.repository.AlarmRepository import com.yapp.domain.repository.FortuneRepository -import com.yapp.domain.usecase.AlarmUseCase import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -38,7 +37,7 @@ class AlarmReceiver : BroadcastReceiver() { lateinit var fortuneRepository: FortuneRepository @Inject - lateinit var alarmUseCase: AlarmUseCase + lateinit var alarmRepository: AlarmRepository override fun onReceive(context: Context?, intent: Intent?) { context ?: return @@ -47,15 +46,7 @@ class AlarmReceiver : BroadcastReceiver() { val alarmServiceIntent = createAlarmServiceIntent(context, intent) when (intent.action) { AlarmConstants.ACTION_ALARM_TRIGGERED -> { - val alarm: Alarm? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - alarmServiceIntent.getParcelableExtra( - AlarmConstants.EXTRA_ALARM, - Alarm::class.java, - ) - } else { - @Suppress("DEPRECATION") - alarmServiceIntent.getParcelableExtra(AlarmConstants.EXTRA_ALARM) - } + val alarm: Alarm? = alarmServiceIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) analyticsHelper.logEvent( AnalyticsEvent( type = "alarm_ring", @@ -70,12 +61,7 @@ class AlarmReceiver : BroadcastReceiver() { } AlarmConstants.ACTION_ALARM_SNOOZED -> { - val alarm: Alarm? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra(AlarmConstants.EXTRA_ALARM, Alarm::class.java) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(AlarmConstants.EXTRA_ALARM) - } + val alarm: Alarm? = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) analyticsHelper.logEvent( AnalyticsEvent( type = "alarm_snooze", @@ -107,7 +93,7 @@ class AlarmReceiver : BroadcastReceiver() { context.stopService(alarmServiceIntent) CoroutineScope(Dispatchers.IO).launch { - val alarms = alarmUseCase.getAllAlarms().first() + val alarms = alarmRepository.getAllAlarms().first() val isSnoozeId = notificationId >= AlarmConstants.SNOOZE_ID_OFFSET diff --git a/core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt b/core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt index bdd47fe8..07e6af6a 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/receivers/RescheduleAlarmReceiver.kt @@ -4,7 +4,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.yapp.alarm.AndroidAlarmScheduler -import com.yapp.domain.usecase.AlarmUseCase +import com.yapp.domain.repository.AlarmRepository import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -17,7 +17,7 @@ import javax.inject.Inject class RescheduleAlarmReceiver : BroadcastReceiver() { @Inject - lateinit var alarmUseCase: AlarmUseCase + lateinit var alarmRepository: AlarmRepository @Inject lateinit var androidAlarmScheduler: AndroidAlarmScheduler @@ -35,7 +35,7 @@ class RescheduleAlarmReceiver : BroadcastReceiver() { private fun rescheduleAlarm(pendingResult: PendingResult) { CoroutineScope(Dispatchers.IO + SupervisorJob()).launch { try { - val alarms = alarmUseCase.getAllAlarms().first() + val alarms = alarmRepository.getAllAlarms().first() alarms .filter { it.isAlarmActive } .forEach { alarm -> androidAlarmScheduler.scheduleAlarm(alarm) } diff --git a/core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt b/core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt index 56141679..abdc8692 100644 --- a/core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt +++ b/core/alarm/src/main/java/com/yapp/alarm/services/AlarmService.kt @@ -27,7 +27,7 @@ import com.yapp.alarm.scheduler.PostFortuneTaskScheduler import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay import com.yapp.domain.model.MissionType -import com.yapp.domain.usecase.AlarmUseCase +import com.yapp.domain.repository.AlarmRepository import com.yapp.media.sound.SoundPlayer import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope @@ -41,7 +41,7 @@ import javax.inject.Inject class AlarmService : Service() { @Inject - lateinit var alarmUseCase: AlarmUseCase + lateinit var alarmRepository: AlarmRepository @Inject lateinit var soundPlayer: SoundPlayer @@ -80,12 +80,7 @@ class AlarmService : Service() { } private fun handleIntent(intent: Intent) { - val alarm: Alarm? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra(AlarmConstants.EXTRA_ALARM, Alarm::class.java) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(AlarmConstants.EXTRA_ALARM) - } + val alarm: Alarm? = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) if (alarm == null) { Log.e("AlarmService", "Failed to retrieve Alarm object from intent") @@ -198,7 +193,7 @@ class AlarmService : Service() { private fun turnOffAlarm(alarmId: Long) { CoroutineScope(Dispatchers.IO).launch { - alarmUseCase.updateAlarmActive( + alarmRepository.updateAlarmActive( id = alarmId, active = false, ) diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index a01d2fa2..65cd3261 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -3,7 +3,7 @@ import com.yapp.convention.setNamespace plugins { id("orbit.android.library") id("orbit.android.hilt") - id("orbit.android.compose") + id("org.jetbrains.kotlin.plugin.serialization") } android { @@ -13,7 +13,5 @@ android { dependencies { implementation(projects.core.designsystem) implementation(projects.domain) - implementation(libs.compose.navigation) - implementation(libs.hilt.navigation.compose) implementation(libs.kotlinx.serialization.json) } diff --git a/core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt b/core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt index 111c7788..df063d5d 100644 --- a/core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt +++ b/core/common/src/main/java/com/yapp/common/navigation/route/MissionRoute.kt @@ -1,6 +1,6 @@ package com.yapp.common.navigation.route -import com.yapp.domain.MissionMode +import com.yapp.domain.model.MissionMode import kotlinx.serialization.Serializable @Serializable diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 5796214b..caff92bd 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -16,8 +16,6 @@ room { } dependencies { - implementation(projects.domain) - ksp(libs.androidx.room.compiler) implementation(libs.androidx.room.ktx) implementation(libs.androidx.room.runtime) diff --git a/core/database/src/main/java/com/yapp/database/AlarmDatabase.kt b/core/database/src/main/java/com/yapp/database/AlarmDatabase.kt index 988912e2..433d2880 100644 --- a/core/database/src/main/java/com/yapp/database/AlarmDatabase.kt +++ b/core/database/src/main/java/com/yapp/database/AlarmDatabase.kt @@ -2,10 +2,8 @@ package com.yapp.database import androidx.room.Database import androidx.room.RoomDatabase -import androidx.room.TypeConverters @Database(entities = [AlarmEntity::class], version = 2, exportSchema = true) -@TypeConverters(MissionTypeConverter::class) abstract class AlarmDatabase : RoomDatabase() { abstract fun alarmDao(): AlarmDao diff --git a/core/database/src/main/java/com/yapp/database/AlarmEntity.kt b/core/database/src/main/java/com/yapp/database/AlarmEntity.kt index 72320fbd..c35262a8 100644 --- a/core/database/src/main/java/com/yapp/database/AlarmEntity.kt +++ b/core/database/src/main/java/com/yapp/database/AlarmEntity.kt @@ -3,8 +3,6 @@ package com.yapp.database import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -import com.yapp.domain.model.Alarm -import com.yapp.domain.model.MissionType @Entity(tableName = AlarmDatabase.DATABASE_NAME) data class AlarmEntity( @@ -33,45 +31,9 @@ data class AlarmEntity( val isAlarmActive: Boolean = true, @ColumnInfo(defaultValue = "1") - val missionType: MissionType = MissionType.TAP, + val missionType: Int = DEFAULT_MISSION_TYPE_VALUE, @ColumnInfo(defaultValue = "10") val missionCount: Int = 10, ) -fun AlarmEntity.toDomain() = Alarm( - id = id, - hour = hour, - minute = minute, - second = second, - repeatDays = repeatDays, - isHolidayAlarmOff = isHolidayAlarmOff, - isSnoozeEnabled = isSnoozeEnabled, - snoozeInterval = snoozeInterval, - snoozeCount = snoozeCount, - isVibrationEnabled = isVibrationEnabled, - isSoundEnabled = isSoundEnabled, - soundUri = soundUri, - soundVolume = soundVolume, - isAlarmActive = isAlarmActive, - missionType = missionType, - missionCount = missionCount, -) - -fun Alarm.toEntity() = AlarmEntity( - id = id, - hour = hour, - minute = minute, - second = second, - repeatDays = repeatDays, - isHolidayAlarmOff = isHolidayAlarmOff, - isSnoozeEnabled = isSnoozeEnabled, - snoozeInterval = snoozeInterval, - snoozeCount = snoozeCount, - isVibrationEnabled = isVibrationEnabled, - isSoundEnabled = isSoundEnabled, - soundUri = soundUri, - soundVolume = soundVolume, - isAlarmActive = isAlarmActive, - missionType = missionType, - missionCount = missionCount, -) +private const val DEFAULT_MISSION_TYPE_VALUE = 1 diff --git a/core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt b/core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt deleted file mode 100644 index aeb59503..00000000 --- a/core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.yapp.database - -import androidx.room.TypeConverter -import com.yapp.domain.model.MissionType - -class MissionTypeConverter { - - @TypeConverter - fun fromInt(value: Int): MissionType { - return MissionType.fromInt(value) - } - - @TypeConverter - fun toInt(missionType: MissionType): Int { - return missionType.value - } -} diff --git a/core/media/build.gradle.kts b/core/media/build.gradle.kts index f481d5fd..cf6ead67 100644 --- a/core/media/build.gradle.kts +++ b/core/media/build.gradle.kts @@ -9,3 +9,7 @@ plugins { android { setNamespace("core.media") } + +dependencies { + implementation(projects.domain) +} diff --git a/core/media/src/main/java/com/yapp/media/di/AlarmSoundModule.kt b/core/media/src/main/java/com/yapp/media/di/AlarmSoundModule.kt new file mode 100644 index 00000000..2dce4201 --- /dev/null +++ b/core/media/src/main/java/com/yapp/media/di/AlarmSoundModule.kt @@ -0,0 +1,19 @@ +package com.yapp.media.di + +import com.yapp.domain.media.AlarmSoundManager +import com.yapp.media.sound.AlarmSoundManagerImpl +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +interface AlarmSoundModule { + @Binds + @Singleton + fun bindAlarmSoundManager( + impl: AlarmSoundManagerImpl, + ): AlarmSoundManager +} diff --git a/core/media/src/main/java/com/yapp/media/sound/AlarmSoundManagerImpl.kt b/core/media/src/main/java/com/yapp/media/sound/AlarmSoundManagerImpl.kt new file mode 100644 index 00000000..e7db3a3e --- /dev/null +++ b/core/media/src/main/java/com/yapp/media/sound/AlarmSoundManagerImpl.kt @@ -0,0 +1,41 @@ +package com.yapp.media.sound + +import android.net.Uri +import com.yapp.domain.media.AlarmSoundManager +import com.yapp.domain.model.AlarmSound +import com.yapp.media.ringtone.RingtoneManagerHelper +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class AlarmSoundManagerImpl @Inject constructor( + private val ringtoneManagerHelper: RingtoneManagerHelper, + private val soundPlayer: SoundPlayer, +) : AlarmSoundManager { + + override fun getAlarmSounds(): List = + ringtoneManagerHelper.getAlarmSounds().map { (title, uri) -> + AlarmSound(title = title, uri = uri.toString()) + } + + override fun initializeSoundPlayer(uri: String) { + if (uri.isBlank()) return + soundPlayer.initialize(Uri.parse(uri)) + } + + override fun playAlarmSound(volume: Int) { + soundPlayer.playSound(volume) + } + + override fun stopAlarmSound() { + soundPlayer.stopSound() + } + + override fun updateAlarmVolume(volume: Int) { + soundPlayer.updateVolume(volume) + } + + override fun releaseSoundPlayer() { + soundPlayer.release() + } +} diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index b4250dc6..871f4d8e 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -3,6 +3,7 @@ import com.yapp.convention.setNamespace plugins { id("orbit.android.library") id("orbit.android.compose") + id("org.jetbrains.kotlin.plugin.serialization") } android { @@ -10,9 +11,13 @@ android { } dependencies { + implementation(projects.core.common) implementation(projects.core.designsystem) implementation(projects.core.media) + implementation(projects.domain) implementation(libs.compose.material) + implementation(libs.compose.navigation) + implementation(libs.hilt.navigation.compose) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) diff --git a/core/common/src/main/java/com/yapp/common/navigation/OrbitNavigator.kt b/core/ui/src/main/java/com/yapp/common/navigation/OrbitNavigator.kt similarity index 100% rename from core/common/src/main/java/com/yapp/common/navigation/OrbitNavigator.kt rename to core/ui/src/main/java/com/yapp/common/navigation/OrbitNavigator.kt diff --git a/core/common/src/main/java/com/yapp/common/navigation/extensions/SharedViewModelExtensions.kt b/core/ui/src/main/java/com/yapp/common/navigation/extensions/SharedViewModelExtensions.kt similarity index 100% rename from core/common/src/main/java/com/yapp/common/navigation/extensions/SharedViewModelExtensions.kt rename to core/ui/src/main/java/com/yapp/common/navigation/extensions/SharedViewModelExtensions.kt diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 354e0002..13e36ec8 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -15,7 +15,6 @@ dependencies { implementation(projects.core.datastore) implementation(projects.domain) - implementation(projects.core.media) implementation(projects.core.remoteconfig) implementation(libs.kotlinx.serialization.json) diff --git a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt index 03fecd55..7e585dc3 100644 --- a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt +++ b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt @@ -1,8 +1,8 @@ package com.yapp.data.local.datasource +import com.yapp.data.local.mapper.toDomain import com.yapp.database.AlarmDao import com.yapp.database.AlarmEntity -import com.yapp.database.toDomain import com.yapp.domain.model.Alarm import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/data/src/main/java/com/yapp/data/local/mapper/AlarmMapper.kt b/data/src/main/java/com/yapp/data/local/mapper/AlarmMapper.kt new file mode 100644 index 00000000..9fea398c --- /dev/null +++ b/data/src/main/java/com/yapp/data/local/mapper/AlarmMapper.kt @@ -0,0 +1,43 @@ +package com.yapp.data.local.mapper + +import com.yapp.database.AlarmEntity +import com.yapp.domain.model.Alarm +import com.yapp.domain.model.MissionType + +fun AlarmEntity.toDomain(): Alarm = Alarm( + id = id, + hour = hour, + minute = minute, + second = second, + repeatDays = repeatDays, + isHolidayAlarmOff = isHolidayAlarmOff, + isSnoozeEnabled = isSnoozeEnabled, + snoozeInterval = snoozeInterval, + snoozeCount = snoozeCount, + isVibrationEnabled = isVibrationEnabled, + isSoundEnabled = isSoundEnabled, + soundUri = soundUri, + soundVolume = soundVolume, + isAlarmActive = isAlarmActive, + missionType = MissionType.fromInt(missionType), + missionCount = missionCount, +) + +fun Alarm.toEntity(): AlarmEntity = AlarmEntity( + id = id, + hour = hour, + minute = minute, + second = second, + repeatDays = repeatDays, + isHolidayAlarmOff = isHolidayAlarmOff, + isSnoozeEnabled = isSnoozeEnabled, + snoozeInterval = snoozeInterval, + snoozeCount = snoozeCount, + isVibrationEnabled = isVibrationEnabled, + isSoundEnabled = isSoundEnabled, + soundUri = soundUri, + soundVolume = soundVolume, + isAlarmActive = isAlarmActive, + missionType = missionType.value, + missionCount = missionCount, +) diff --git a/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt index 50385b4f..ae53195b 100644 --- a/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt +++ b/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt @@ -1,45 +1,40 @@ package com.yapp.data.repositoryimpl -import android.net.Uri import com.yapp.data.local.datasource.AlarmLocalDataSource -import com.yapp.database.toEntity +import com.yapp.data.local.mapper.toEntity +import com.yapp.domain.media.AlarmSoundManager import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmSound import com.yapp.domain.repository.AlarmRepository -import com.yapp.media.ringtone.RingtoneManagerHelper -import com.yapp.media.sound.SoundPlayer import kotlinx.coroutines.flow.Flow import javax.inject.Inject class AlarmRepositoryImpl @Inject constructor( private val alarmLocalDataSource: AlarmLocalDataSource, - private val ringtoneManagerHelper: RingtoneManagerHelper, - private val soundPlayer: SoundPlayer, + private val alarmSoundManager: AlarmSoundManager, ) : AlarmRepository { override suspend fun getAlarmSounds(): Result> = runCatching { - ringtoneManagerHelper.getAlarmSounds().map { (title, uri) -> - AlarmSound(title, uri) - } + alarmSoundManager.getAlarmSounds() } - override fun initializeSoundPlayer(uri: Uri) { - soundPlayer.initialize(uri) + override fun initializeSoundPlayer(uri: String) { + alarmSoundManager.initializeSoundPlayer(uri) } override fun playAlarmSound(volume: Int) { - soundPlayer.playSound(volume) + alarmSoundManager.playAlarmSound(volume) } override fun stopAlarmSound() { - soundPlayer.stopSound() + alarmSoundManager.stopAlarmSound() } override fun updateAlarmVolume(volume: Int) { - soundPlayer.updateVolume(volume) + alarmSoundManager.updateAlarmVolume(volume) } override fun releaseSoundPlayer() { - soundPlayer.release() + alarmSoundManager.releaseSoundPlayer() } override fun getAllAlarms(): Flow> = diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index e5567cb0..48a1487c 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -1,14 +1,13 @@ -import com.yapp.convention.setNamespace - plugins { - id("orbit.android.library") - id("kotlin-parcelize") + id("orbit.kotlin.library") + id("org.jetbrains.kotlin.plugin.serialization") } -android { - setNamespace("domain") +kotlin { + jvmToolchain(17) } dependencies { implementation(libs.kotlinx.serialization.json) + implementation(libs.kotlinx.coroutines.core) } diff --git a/domain/src/main/java/com/yapp/domain/MissionMode.kt b/domain/src/main/java/com/yapp/domain/MissionMode.kt deleted file mode 100644 index b047c7c8..00000000 --- a/domain/src/main/java/com/yapp/domain/MissionMode.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.yapp.domain - -enum class MissionMode { - REAL, - PREVIEW, - ; - - companion object { - fun fromRaw(raw: String?): MissionMode { - return raw?.let { entries.find { it.name == raw } } ?: REAL - } - } -} diff --git a/domain/src/main/java/com/yapp/domain/media/AlarmSoundManager.kt b/domain/src/main/java/com/yapp/domain/media/AlarmSoundManager.kt new file mode 100644 index 00000000..69e27d7c --- /dev/null +++ b/domain/src/main/java/com/yapp/domain/media/AlarmSoundManager.kt @@ -0,0 +1,12 @@ +package com.yapp.domain.media + +import com.yapp.domain.model.AlarmSound + +interface AlarmSoundManager { + fun getAlarmSounds(): List + fun initializeSoundPlayer(uri: String) + fun playAlarmSound(volume: Int) + fun stopAlarmSound() + fun updateAlarmVolume(volume: Int) + fun releaseSoundPlayer() +} diff --git a/domain/src/main/java/com/yapp/domain/model/Alarm.kt b/domain/src/main/java/com/yapp/domain/model/Alarm.kt index 14079633..295c83fb 100644 --- a/domain/src/main/java/com/yapp/domain/model/Alarm.kt +++ b/domain/src/main/java/com/yapp/domain/model/Alarm.kt @@ -1,70 +1,50 @@ package com.yapp.domain.model -import android.net.Uri -import android.os.Parcelable -import kotlinx.parcelize.Parcelize import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -@Parcelize @Serializable data class Alarm( val id: Long = 0, - val hour: Int = 6, val minute: Int = 0, val second: Int = 0, - - // 반복 요일 (bitmask 를 통해 설정) val repeatDays: Int = 0, - val isHolidayAlarmOff: Boolean = false, val isSnoozeEnabled: Boolean = false, - val snoozeInterval: Int = 5, - // -1 이면 무제한 val snoozeCount: Int = 1, - val isVibrationEnabled: Boolean = true, val isSoundEnabled: Boolean = true, - val soundUri: String = "", val soundVolume: Int = 70, - val isAlarmActive: Boolean = true, - val missionType: MissionType = MissionType.TAP, val missionCount: Int = 10, -) : Parcelable { - +) { companion object { - fun fromJson(json: String): Alarm { - return Json.decodeFromString(json) - } + fun fromJson(json: String): Alarm = Json.decodeFromString(json) } - - override fun toString(): String = Uri.encode(Json.encodeToString(this)) } -fun Alarm.copyFrom(source: Alarm): Alarm { - return this.copy( - repeatDays = source.repeatDays, - isHolidayAlarmOff = source.isHolidayAlarmOff, - isSnoozeEnabled = source.isSnoozeEnabled, - snoozeInterval = source.snoozeInterval, - snoozeCount = source.snoozeCount, - isVibrationEnabled = source.isVibrationEnabled, - isSoundEnabled = source.isSoundEnabled, - soundUri = source.soundUri, - soundVolume = source.soundVolume, - isAlarmActive = source.isAlarmActive, - ) -} +fun Alarm.copyFrom(source: Alarm): Alarm = copy( + repeatDays = source.repeatDays, + isHolidayAlarmOff = source.isHolidayAlarmOff, + isSnoozeEnabled = source.isSnoozeEnabled, + snoozeInterval = source.snoozeInterval, + snoozeCount = source.snoozeCount, + isVibrationEnabled = source.isVibrationEnabled, + isSoundEnabled = source.isSoundEnabled, + soundUri = source.soundUri, + soundVolume = source.soundVolume, + isAlarmActive = source.isAlarmActive, +) fun Alarm.toTimeString(): String { val formattedHour = hour.toString().padStart(2, '0') val formattedMinute = minute.toString().padStart(2, '0') - return "$formattedHour:$formattedMinute" } + +fun Alarm.toJson(): String = Json.encodeToString(this) diff --git a/domain/src/main/java/com/yapp/domain/model/AlarmSound.kt b/domain/src/main/java/com/yapp/domain/model/AlarmSound.kt index f5327fe8..9cb8026e 100644 --- a/domain/src/main/java/com/yapp/domain/model/AlarmSound.kt +++ b/domain/src/main/java/com/yapp/domain/model/AlarmSound.kt @@ -1,8 +1,6 @@ package com.yapp.domain.model -import android.net.Uri - data class AlarmSound( val title: String, - val uri: Uri, + val uri: String, ) diff --git a/domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt b/domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt index 60123473..2cfdca3c 100644 --- a/domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt +++ b/domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt @@ -1,13 +1,12 @@ package com.yapp.domain.repository -import android.net.Uri import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmSound import kotlinx.coroutines.flow.Flow interface AlarmRepository { suspend fun getAlarmSounds(): Result> - fun initializeSoundPlayer(uri: Uri) + fun initializeSoundPlayer(uri: String) fun playAlarmSound(volume: Int) fun stopAlarmSound() fun updateAlarmVolume(volume: Int) diff --git a/domain/src/main/java/com/yapp/domain/usecase/AlarmUseCase.kt b/domain/src/main/java/com/yapp/domain/usecase/AlarmUseCase.kt deleted file mode 100644 index 86720460..00000000 --- a/domain/src/main/java/com/yapp/domain/usecase/AlarmUseCase.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.yapp.domain.usecase - -import android.net.Uri -import com.yapp.domain.model.Alarm -import com.yapp.domain.model.AlarmSound -import com.yapp.domain.repository.AlarmRepository -import com.yapp.domain.scheduler.AlarmScheduler -import kotlinx.coroutines.flow.Flow -import javax.inject.Inject - -class AlarmUseCase @Inject constructor( - private val alarmRepository: AlarmRepository, - private val alarmScheduler: AlarmScheduler, -) { - suspend fun getAlarmSounds(): Result> = alarmRepository.getAlarmSounds() - fun initializeSoundPlayer(uri: Uri) = alarmRepository.initializeSoundPlayer(uri) - fun playAlarmSound(volume: Int) = alarmRepository.playAlarmSound(volume) - fun stopAlarmSound() = alarmRepository.stopAlarmSound() - fun updateAlarmVolume(volume: Int) = alarmRepository.updateAlarmVolume(volume) - fun releaseSoundPlayer() = alarmRepository.releaseSoundPlayer() - fun getAllAlarms(): Flow> = alarmRepository.getAllAlarms() - fun getAlarmsByTime(hour: Int, minute: Int): Flow> = alarmRepository.getAlarmsByTime(hour, minute) - suspend fun insertAlarm(alarm: Alarm): Result = alarmRepository.insertAlarm(alarm) - suspend fun updateAlarm(alarm: Alarm): Result = alarmRepository.updateAlarm(alarm) - suspend fun updateAlarmActive(id: Long, active: Boolean): Result = alarmRepository.updateAlarmActive(id, active) - suspend fun getAlarm(id: Long): Result = alarmRepository.getAlarm(id) - suspend fun deleteAlarm(id: Long): Result = alarmRepository.deleteAlarm(id) - - fun scheduleAlarm(alarm: Alarm) = alarmScheduler.scheduleAlarm(alarm) - fun unScheduleAlarm(alarm: Alarm) = alarmScheduler.unScheduleAlarm(alarm) -} diff --git a/domain/src/main/java/com/yapp/domain/usecase/GetMissionTypeUseCase.kt b/domain/src/main/java/com/yapp/domain/usecase/GetMissionTypeUseCase.kt deleted file mode 100644 index 9df0611f..00000000 --- a/domain/src/main/java/com/yapp/domain/usecase/GetMissionTypeUseCase.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.yapp.domain.usecase - -import com.yapp.domain.model.MissionType -import com.yapp.domain.repository.RemoteConfigRepository -import javax.inject.Inject - -class GetMissionTypeUseCase @Inject constructor( - private val repository: RemoteConfigRepository, -) { - suspend fun execute(): MissionType { - repository.fetchAndActivate() - return repository.getMissionType() - } -} diff --git a/feature/alarm-interaction/build.gradle.kts b/feature/alarm-interaction/build.gradle.kts index 22e53709..b113eaa2 100644 --- a/feature/alarm-interaction/build.gradle.kts +++ b/feature/alarm-interaction/build.gradle.kts @@ -2,6 +2,7 @@ import com.yapp.convention.setNamespace plugins { id("orbit.android.feature") + id("org.jetbrains.kotlin.plugin.serialization") } android { diff --git a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt index e95a93b2..7e0af944 100644 --- a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt +++ b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionActivity.kt @@ -5,7 +5,6 @@ import android.app.KeyguardManager import android.content.Intent import android.content.IntentFilter import android.content.pm.ActivityInfo -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -34,12 +33,7 @@ class AlarmInteractionActivity : ComponentActivity() { super.onCreate(savedInstanceState) requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - val alarm: Alarm? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra(AlarmConstants.EXTRA_ALARM, Alarm::class.java) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(AlarmConstants.EXTRA_ALARM) - } + val alarm = intent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) unlockScreen() @@ -66,12 +60,7 @@ class AlarmInteractionActivity : ComponentActivity() { DisposableEffect(this, navigator.navController) { val onNewIntentConsumer = Consumer { newIntent -> - val newAlarm: Alarm? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - newIntent.getParcelableExtra(AlarmConstants.EXTRA_ALARM, Alarm::class.java) - } else { - @Suppress("DEPRECATION") - newIntent.getParcelableExtra(AlarmConstants.EXTRA_ALARM) - } + val newAlarm = newIntent.getStringExtra(AlarmConstants.EXTRA_ALARM)?.let(Alarm::fromJson) newAlarm?.let { alarm -> navigator.navigateToAlarmAction(alarm = alarm) } diff --git a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt index eb025a86..2734c488 100644 --- a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt +++ b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/AlarmInteractionNavGraph.kt @@ -1,5 +1,6 @@ package com.yapp.alarm.interaction +import android.net.Uri import android.os.Bundle import androidx.compose.runtime.LaunchedEffect import androidx.navigation.NavGraphBuilder @@ -13,7 +14,7 @@ import com.yapp.common.navigation.OrbitNavigator import com.yapp.common.navigation.route.AlarmInteractionBaseRoute import com.yapp.common.navigation.route.AlarmInteractionDestination import com.yapp.domain.model.Alarm -import kotlinx.serialization.json.Json +import com.yapp.domain.model.toJson import kotlin.reflect.typeOf val AlarmArgType = object : NavType(isNullableAllowed = false) { @@ -26,7 +27,11 @@ val AlarmArgType = object : NavType(isNullableAllowed = false) { } override fun put(bundle: Bundle, key: String, value: Alarm) { - bundle.putString(key, Json.encodeToString(Alarm.serializer(), value)) + bundle.putString(key, value.toJson()) + } + + override fun serializeAsValue(value: Alarm): String { + return Uri.encode(value.toJson()) } } diff --git a/feature/home/src/main/java/com/yapp/home/HomeViewModel.kt b/feature/home/src/main/java/com/yapp/home/HomeViewModel.kt index 57b74428..9cc510fa 100644 --- a/feature/home/src/main/java/com/yapp/home/HomeViewModel.kt +++ b/feature/home/src/main/java/com/yapp/home/HomeViewModel.kt @@ -7,9 +7,10 @@ import android.util.Log import androidx.lifecycle.ViewModel import com.yapp.common.util.ResourceProvider import com.yapp.domain.model.Alarm +import com.yapp.domain.repository.AlarmRepository import com.yapp.domain.repository.FortuneRepository import com.yapp.domain.repository.UserInfoRepository -import com.yapp.domain.usecase.AlarmUseCase +import com.yapp.domain.scheduler.AlarmScheduler import com.yapp.home.util.AlarmDateTimeFormatter import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -30,7 +31,8 @@ import javax.inject.Named @HiltViewModel class HomeViewModel @Inject constructor( - private val alarmUseCase: AlarmUseCase, + private val alarmRepository: AlarmRepository, + private val alarmScheduler: AlarmScheduler, private val resourceProvider: ResourceProvider, private val alarmDateTimeFormatter: AlarmDateTimeFormatter, private val fortuneRepository: FortuneRepository, @@ -178,7 +180,7 @@ class HomeViewModel @Inject constructor( val previousState = currentAlarm.isAlarmActive // 기존 상태 저장 val updatedAlarm = currentAlarm.copy(isAlarmActive = !currentAlarm.isAlarmActive) - alarmUseCase.updateAlarmActive(alarmId, updatedAlarm.isAlarmActive).onSuccess { + alarmRepository.updateAlarmActive(alarmId, updatedAlarm.isAlarmActive).onSuccess { val updatedAlarms = state.alarms.toMutableList() updatedAlarms[currentIndex] = updatedAlarm @@ -192,9 +194,9 @@ class HomeViewModel @Inject constructor( } if (updatedAlarm.isAlarmActive) { - alarmUseCase.scheduleAlarm(updatedAlarm) + alarmScheduler.scheduleAlarm(updatedAlarm) } else { - alarmUseCase.unScheduleAlarm(updatedAlarm) + alarmScheduler.unScheduleAlarm(updatedAlarm) } }.onFailure { error -> Log.e("HomeViewModel", "Failed to update alarm state", error) @@ -242,7 +244,7 @@ class HomeViewModel @Inject constructor( val currentAlarm = state.alarms[currentIndex] val restoredAlarm = currentAlarm.copy(isAlarmActive = previousState) - alarmUseCase.updateAlarm(restoredAlarm).onSuccess { updatedAlarm -> + alarmRepository.updateAlarm(restoredAlarm).onSuccess { updatedAlarm -> val updatedAlarms = state.alarms.toMutableList() updatedAlarms[currentIndex] = updatedAlarm reduce { @@ -254,9 +256,9 @@ class HomeViewModel @Inject constructor( } if (updatedAlarm.isAlarmActive) { - alarmUseCase.scheduleAlarm(updatedAlarm) + alarmScheduler.scheduleAlarm(updatedAlarm) } else { - alarmUseCase.unScheduleAlarm(updatedAlarm) + alarmScheduler.unScheduleAlarm(updatedAlarm) } }.onFailure { error -> Log.e("HomeViewModel", "Failed to rollback alarm state", error) @@ -274,8 +276,8 @@ class HomeViewModel @Inject constructor( .filter { it.id in alarmIds } alarmsToDelete.forEach { alarm -> - alarmUseCase.deleteAlarm(alarm.id) - alarmUseCase.unScheduleAlarm(alarm) + alarmRepository.deleteAlarm(alarm.id) + alarmScheduler.unScheduleAlarm(alarm) } if (state.activeItemMenu != null) { @@ -297,8 +299,8 @@ class HomeViewModel @Inject constructor( private fun restoreDeletedAlarms(alarmsWithIndex: List) = intent { alarmsWithIndex.forEach { alarm -> - alarmUseCase.insertAlarm(alarm) - alarmUseCase.scheduleAlarm(alarm) + alarmRepository.insertAlarm(alarm) + alarmScheduler.scheduleAlarm(alarm) } } @@ -309,7 +311,7 @@ class HomeViewModel @Inject constructor( private fun loadAllAlarms() = intent { reduce { state.copy(initialLoading = true) } - alarmUseCase.getAllAlarms().collect { alarms -> + alarmRepository.getAllAlarms().collect { alarms -> reduce { state.copy( alarms = alarms, diff --git a/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditContract.kt b/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditContract.kt index 4f303b92..7d163bbf 100644 --- a/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditContract.kt +++ b/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditContract.kt @@ -154,7 +154,7 @@ internal fun AlarmAddEditContract.State.toAlarm(id: Long = 0): Alarm { snoozeCount = snoozeState.snoozeCount, isVibrationEnabled = soundState.isVibrationEnabled, isSoundEnabled = soundState.isSoundEnabled, - soundUri = soundState.sounds.getOrNull(soundState.soundIndex)?.uri.toString(), + soundUri = soundState.sounds.getOrNull(soundState.soundIndex)?.uri.orEmpty(), soundVolume = soundState.soundVolume, isAlarmActive = true, ) diff --git a/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt b/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt index 9d726ae3..9e12561f 100644 --- a/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt +++ b/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditScreen.kt @@ -1,6 +1,5 @@ package com.yapp.home.alarm.addedit -import android.net.Uri import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -546,16 +545,13 @@ private fun AlarmAddEditSettingsSection( state.soundState.isSoundEnabled && state.soundState.isVibrationEnabled -> { "${stringResource(id = R.string.alarm_add_edit_vibration)}, ${ state.soundState.sounds.getOrElse(state.soundState.soundIndex) { - AlarmSound("", Uri.EMPTY) + AlarmSound("", "") }.title }" } state.soundState.isSoundEnabled -> state.soundState.sounds.getOrElse(state.soundState.soundIndex) { - AlarmSound( - "", - Uri.EMPTY, - ) + AlarmSound("", "") }.title state.soundState.isVibrationEnabled -> stringResource(id = R.string.alarm_add_edit_vibration) diff --git a/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditViewModel.kt b/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditViewModel.kt index b0f8dfb5..0322b905 100644 --- a/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditViewModel.kt +++ b/feature/home/src/main/java/com/yapp/home/alarm/addedit/AlarmAddEditViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel import com.yapp.analytics.AnalyticsEvent import com.yapp.analytics.AnalyticsHelper import com.yapp.common.util.ResourceProvider +import com.yapp.domain.media.AlarmSoundManager import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay import com.yapp.domain.model.AlarmSound @@ -15,7 +16,8 @@ import com.yapp.domain.model.copyFrom import com.yapp.domain.model.toAlarmDayNames import com.yapp.domain.model.toAlarmDays import com.yapp.domain.model.toRepeatDays -import com.yapp.domain.usecase.AlarmUseCase +import com.yapp.domain.repository.AlarmRepository +import com.yapp.domain.scheduler.AlarmScheduler import com.yapp.home.util.AlarmDateTimeFormatter import com.yapp.media.haptic.HapticFeedbackManager import com.yapp.media.haptic.HapticType @@ -35,7 +37,9 @@ import javax.inject.Inject @HiltViewModel class AlarmAddEditViewModel @Inject constructor( private val analyticsHelper: AnalyticsHelper, - private val alarmUseCase: AlarmUseCase, + private val alarmRepository: AlarmRepository, + private val alarmScheduler: AlarmScheduler, + private val alarmSoundManager: AlarmSoundManager, private val resourceProvider: ResourceProvider, private val alarmDateTimeFormatter: AlarmDateTimeFormatter, private val hapticFeedbackManager: HapticFeedbackManager, @@ -52,7 +56,7 @@ class AlarmAddEditViewModel @Inject constructor( private val alarmId: Long = savedStateHandle.get("alarmId") ?: -1 private fun initializeAlarmScreen() = intent { - alarmUseCase.getAlarmSounds().onSuccess { sounds -> + alarmRepository.getAlarmSounds().onSuccess { sounds -> if (alarmId == -1L) { setupNewAlarmScreen(sounds) } else { @@ -67,7 +71,7 @@ class AlarmAddEditViewModel @Inject constructor( val defaultSoundIndex = sounds.indexOfFirst { it.title == "Homecoming" }.takeIf { it >= 0 } ?: 0 val defaultSound = sounds[defaultSoundIndex] - alarmUseCase.initializeSoundPlayer(defaultSound.uri) + alarmSoundManager.initializeSoundPlayer(defaultSound.uri) val now = LocalTime.now() @@ -85,12 +89,12 @@ class AlarmAddEditViewModel @Inject constructor( } private fun loadExistingAlarm(sounds: List) = intent { - alarmUseCase.getAlarm(alarmId).onSuccess { alarm -> + alarmRepository.getAlarm(alarmId).onSuccess { alarm -> val repeatDays = alarm.repeatDays.toAlarmDays() - val selectedSoundIndex = sounds.indexOfFirst { it.uri.toString() == alarm.soundUri } + val selectedSoundIndex = sounds.indexOfFirst { it.uri == alarm.soundUri } val selectedSound = sounds.getOrNull(selectedSoundIndex) ?: sounds.first() - alarmUseCase.initializeSoundPlayer(selectedSound.uri) + alarmSoundManager.initializeSoundPlayer(selectedSound.uri) reduce { state.copy( @@ -156,7 +160,7 @@ class AlarmAddEditViewModel @Inject constructor( override fun onCleared() { super.onCleared() - alarmUseCase.releaseSoundPlayer() + alarmSoundManager.releaseSoundPlayer() } fun processAction(action: AlarmAddEditContract.Action) { @@ -201,7 +205,7 @@ class AlarmAddEditViewModel @Inject constructor( navigateBack() } else { val updatedAlarm = state.toAlarm() - alarmUseCase.getAlarm(alarmId).onSuccess { existingAlarm -> + alarmRepository.getAlarm(alarmId).onSuccess { existingAlarm -> if (updatedAlarm.copy(id = alarmId) != existingAlarm) { showUnsavedChangesDialog() } else { @@ -242,13 +246,13 @@ class AlarmAddEditViewModel @Inject constructor( private fun updateExistingAlarm(alarm: Alarm) = intent { val updatedAlarm = alarm.copy(id = alarmId) - alarmUseCase.getAlarm(alarmId).onSuccess { oldAlarm -> - alarmUseCase.unScheduleAlarm(oldAlarm) + alarmRepository.getAlarm(alarmId).onSuccess { oldAlarm -> + alarmScheduler.unScheduleAlarm(oldAlarm) } - alarmUseCase.updateAlarm(updatedAlarm) + alarmRepository.updateAlarm(updatedAlarm) .onSuccess { - alarmUseCase.scheduleAlarm(updatedAlarm) + alarmScheduler.scheduleAlarm(updatedAlarm) postSideEffect(AlarmAddEditContract.SideEffect.UpdateAlarm(it.id)) } .onFailure { @@ -257,7 +261,7 @@ class AlarmAddEditViewModel @Inject constructor( } private suspend fun checkAndCreateAlarm(newAlarm: Alarm) { - val timeMatchedAlarms = alarmUseCase.getAlarmsByTime(newAlarm.hour, newAlarm.minute) + val timeMatchedAlarms = alarmRepository.getAlarmsByTime(newAlarm.hour, newAlarm.minute) .first() when { @@ -290,7 +294,7 @@ class AlarmAddEditViewModel @Inject constructor( } private fun createNewAlarm(alarm: Alarm) = intent { - alarmUseCase.insertAlarm(alarm) + alarmRepository.insertAlarm(alarm) .onSuccess { analyticsHelper.logEvent( AnalyticsEvent( @@ -302,7 +306,7 @@ class AlarmAddEditViewModel @Inject constructor( ), ), ) - alarmUseCase.scheduleAlarm(it) + alarmScheduler.scheduleAlarm(it) postSideEffect(AlarmAddEditContract.SideEffect.SaveAlarm(it.id)) } .onFailure { @@ -508,18 +512,18 @@ class AlarmAddEditViewModel @Inject constructor( private fun toggleSoundEnabled(enabled: Boolean) = intent { if (!enabled) { - alarmUseCase.stopAlarmSound() + alarmSoundManager.stopAlarmSound() } } private fun setSoundVolume(volume: Int) = intent { - alarmUseCase.updateAlarmVolume(volume) + alarmSoundManager.updateAlarmVolume(volume) } private fun setSoundIndex(index: Int) = intent { val selectedSound = state.soundState.sounds[index] - alarmUseCase.initializeSoundPlayer(selectedSound.uri) - alarmUseCase.playAlarmSound(state.soundState.soundVolume) + alarmSoundManager.initializeSoundPlayer(selectedSound.uri) + alarmSoundManager.playAlarmSound(state.soundState.soundVolume) } private fun showBottomSheet(sheetType: AlarmAddEditContract.BottomSheetType) = intent { diff --git a/feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmSoundBottomSheet.kt b/feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmSoundBottomSheet.kt index 7b706435..66524be2 100644 --- a/feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmSoundBottomSheet.kt +++ b/feature/home/src/main/java/com/yapp/home/alarm/component/bottomsheet/AlarmSoundBottomSheet.kt @@ -1,6 +1,5 @@ package com.yapp.home.alarm.component.bottomsheet -import android.net.Uri import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -269,7 +268,7 @@ private fun AlarmSoundBottomSheetPreview() { OrbitTheme { AlarmSoundBottomSheet( soundState = AlarmAddEditContract.AlarmSoundState( - sounds = (1..20).map { AlarmSound("sound $it", Uri.EMPTY) }, + sounds = (1..20).map { AlarmSound("sound $it", "") }, ), onVibrationToggle = {}, onSoundToggle = {}, diff --git a/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt b/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt index 8e5c61d4..877187ed 100644 --- a/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt +++ b/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt @@ -1,6 +1,6 @@ package com.yapp.mission -import com.yapp.domain.MissionMode +import com.yapp.domain.model.MissionMode import com.yapp.domain.model.MissionType sealed class MissionContract { diff --git a/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt b/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt index 65249b8c..f989032e 100644 --- a/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt +++ b/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt @@ -49,7 +49,7 @@ import com.yapp.analytics.AnalyticsHelper import com.yapp.analytics.LocalAnalyticsHelper import com.yapp.common.navigation.OrbitNavigator import com.yapp.designsystem.theme.OrbitTheme -import com.yapp.domain.MissionMode +import com.yapp.domain.model.MissionMode import com.yapp.domain.model.MissionType import com.yapp.mission.component.FlipCard import com.yapp.mission.component.MissionProgressBar diff --git a/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt b/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt index f26f893c..f88d9321 100644 --- a/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt +++ b/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt @@ -6,8 +6,8 @@ import androidx.lifecycle.ViewModel import com.yapp.alarm.pendingIntent.interaction.createAlarmDismissIntent import com.yapp.analytics.AnalyticsEvent import com.yapp.analytics.AnalyticsHelper -import com.yapp.domain.MissionMode import com.yapp.domain.model.FortuneCreateStatus +import com.yapp.domain.model.MissionMode import com.yapp.domain.model.MissionType import com.yapp.domain.repository.FortuneRepository import com.yapp.media.haptic.HapticFeedbackManager diff --git a/feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt b/feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt index 13fc35eb..57e28ed9 100644 --- a/feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt @@ -9,9 +9,9 @@ import com.yapp.common.navigation.route.OnboardingDestination import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay import com.yapp.domain.model.toRepeatDays +import com.yapp.domain.repository.AlarmRepository import com.yapp.domain.repository.SignUpRepository import com.yapp.domain.repository.UserInfoRepository -import com.yapp.domain.usecase.AlarmUseCase import com.yapp.media.haptic.HapticFeedbackManager import com.yapp.media.haptic.HapticType import dagger.hilt.android.lifecycle.HiltViewModel @@ -30,7 +30,7 @@ class OnboardingViewModel @Inject constructor( private val analyticsHelper: AnalyticsHelper, private val signUpRepository: SignUpRepository, private val userInfoRepository: UserInfoRepository, - private val alarmUseCase: AlarmUseCase, + private val alarmRepository: AlarmRepository, private val hapticFeedbackManager: HapticFeedbackManager, private val savedStateHandle: SavedStateHandle, ) : ViewModel(), ContainerHost { @@ -131,9 +131,9 @@ class OnboardingViewModel @Inject constructor( } private fun createAlarm() = intent { - alarmUseCase.getAlarmSounds().onSuccess { sounds -> + alarmRepository.getAlarmSounds().onSuccess { sounds -> val defaultSoundIndex = sounds.indexOfFirst { it.title == "Homecoming" }.takeIf { it >= 0 } ?: 0 - val defaultSoundUri = sounds[defaultSoundIndex] + val defaultSound = sounds[defaultSoundIndex] val newAlarm = Alarm( hour = state.selectedTime.hour, @@ -142,10 +142,10 @@ class OnboardingViewModel @Inject constructor( isSnoozeEnabled = true, snoozeInterval = 5, snoozeCount = 5, - soundUri = "${defaultSoundUri.uri}", + soundUri = defaultSound.uri, ) - alarmUseCase.insertAlarm( + alarmRepository.insertAlarm( alarm = newAlarm, ).onFailure { Log.e("OnboardingViewModel", "Failed to create alarm", it) diff --git a/gradle/dependencyGraph.gradle b/gradle/dependencyGraph.gradle index 6f117039..a79768b1 100644 --- a/gradle/dependencyGraph.gradle +++ b/gradle/dependencyGraph.gradle @@ -1,156 +1,236 @@ -tasks.register('projectDependencyGraph') { - doLast { - def dotFileName = 'project.dot' - def dot = new File(rootProject.rootDir, dotFileName) - dot.parentFile.mkdirs() - dot.delete() - - dot << 'digraph {\n' - dot << " graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4];\n" - dot << ' node [style=filled, fillcolor="#bbbbbb"];\n' // 기본 노드 색상 - dot << ' rankdir=TB;\n' - - def rootProjects = [] - def queue = [rootProject] +import org.gradle.work.DisableCachingByDefault + +import javax.inject.Inject + +@DisableCachingByDefault(because = "Generates Graphviz assets from the current project graph") +abstract class ProjectDependencyGraphTask extends DefaultTask { + + @Internal + abstract RegularFileProperty getDotFile() + + @OutputFile + abstract RegularFileProperty getGraphImageFile() + + @Input + abstract Property getDotGraphContent() + + @Inject + protected abstract ExecOperations getExecOperations() + + ProjectDependencyGraphTask() { + group = "reporting" + description = "Generates a Graphviz dependency graph for all modules in the project." + dotFile.convention(project.layout.buildDirectory.file("reports/dependencyGraph/project.dot")) + graphImageFile.convention(project.layout.projectDirectory.file("project.dot.png")) + } + + @TaskAction + void generate() { + File dotOutput = dotFile.get().asFile + File pngOutput = graphImageFile.get().asFile + + dotOutput.parentFile.mkdirs() + pngOutput.parentFile.mkdirs() + + dotOutput.text = dotGraphContent.get() + + execOperations.exec { spec -> + spec.commandLine('dot', '-Tpng', dotOutput.absolutePath, '-o', pngOutput.absolutePath) + } + + dotOutput.delete() + logger.lifecycle("Project module dependency graph created at ${pngOutput.absolutePath}") + } +} + +class DependencyGraphBuilder { + + static String build(Project rootProject) { + def rootProjects = new LinkedHashSet() + def queue = new ArrayDeque() + queue.add(rootProject) while (!queue.isEmpty()) { - def project = queue.remove(0) - rootProjects.add(project) - queue.addAll(project.childProjects.values()) + def current = queue.removeFirst() + rootProjects.add(current) + current.childProjects.values().each { queue.add(it) } } def projects = new LinkedHashSet() - def dependencies = new LinkedHashMap, List>() - def multiplatformProjects = [] - def jsProjects = [] - def androidProjects = [] - def androidLibraryProjects = [] - def androidDynamicFeatureProjects = [] - def javaProjects = [] - - // --- 모듈 유형을 저장할 리스트 추가 --- - def featureModules = [] - def coreModules = [] - def dataModules = [] - def domainModules = [] // domain 모듈 리스트 추가 - - queue = [rootProject] + def dependencies = new LinkedHashMap>() + def featureModules = new LinkedHashSet() + def domainModules = new LinkedHashSet() + def coreModules = new LinkedHashSet() + def dataModules = new LinkedHashSet() + def multiplatformProjects = new LinkedHashSet() + def jsProjects = new LinkedHashSet() + def androidProjects = new LinkedHashSet() + def androidLibraryProjects = new LinkedHashSet() + def androidDynamicFeatureProjects = new LinkedHashSet() + def javaProjects = new LinkedHashSet() + + queue.clear() + queue.add(rootProject) while (!queue.isEmpty()) { - def project = queue.remove(0) - queue.addAll(project.childProjects.values()) - - // --- 모듈 경로/이름을 기반으로 모듈 유형 식별 --- - // 프로젝트의 모듈 명명 규칙에 맞게 조건을 수정하세요. - // 우선순위를 고려하여 배치 (더 구체적인 조건이 위로) - if (project.path.startsWith(':feature')) { - featureModules.add(project) - } else if (project.path.contains(':domain')) { // domain 모듈 식별 조건 (예: ':user:domain', ':product:domain') - domainModules.add(project) - } else if (project.path.contains(':core')) { - coreModules.add(project) - } else if (project.path.startsWith(':data')) { - dataModules.add(project) - } - // --- 기존 플러그인 기반 식별 로직 유지 --- - else if (project.plugins.hasPlugin('org.jetbrains.kotlin.multiplatform')) { - multiplatformProjects.add(project) - } else if (project.plugins.hasPlugin('kotlin2js')) { - jsProjects.add(project) - } else if (project.plugins.hasPlugin('com.android.application')) { - androidProjects.add(project) - } else if (project.plugins.hasPlugin('com.android.library')) { - androidLibraryProjects.add(project) - } else if (project.plugins.hasPlugin('com.android.dynamic-feature')) { - androidDynamicFeatureProjects.add(project) - } else if (project.plugins.hasPlugin('java-library') || project.plugins.hasPlugin('java')) { - javaProjects.add(project) - } + def current = queue.removeFirst() + current.childProjects.values().each { queue.add(it) } + + categorizeModule(current, featureModules, domainModules, coreModules, dataModules, + multiplatformProjects, jsProjects, androidProjects, androidLibraryProjects, + androidDynamicFeatureProjects, javaProjects) + + current.configurations.configureEach { config -> + def lowerName = config.name.toLowerCase(Locale.US) + if (lowerName.contains('test')) { + return + } - project.configurations.configureEach { config -> - if (config.name.toLowerCase().contains("test")) return config.dependencies - .withType(ProjectDependency) - .collect { it.dependencyProject } - .each { dependency -> - projects.add(project) - projects.add(dependency) - rootProjects.remove(dependency) - - def graphKey = new Tuple2(project, dependency) - def traits = dependencies.computeIfAbsent(graphKey) { new ArrayList() } - - if (config.name.toLowerCase().endsWith('implementation')) { - traits.add('style=dotted') + .withType(ProjectDependency) + .collect { it.dependencyProject } + .each { dependency -> + projects.add(current) + projects.add(dependency) + rootProjects.remove(dependency) + + def key = "${current.path}->${dependency.path}" + def traits = dependencies.computeIfAbsent(key) { new ArrayList() } + + if (lowerName.endsWith('implementation')) { + traits.add('style=dotted') + } } - } } } - projects = projects.sort { it.path } + def sortedProjects = projects.toList().sort { it.path } + def builder = new StringBuilder() + builder << 'digraph {\n' + builder << " graph [label=\"${rootProject.name}\\n \",labelloc=t,fontsize=30,ranksep=1.4];\n" + builder << ' node [style=filled, fillcolor="#bbbbbb"];\n' + builder << ' rankdir=TB;\n\n' - dot << '\n # Projects\n\n' - for (project in projects) { + builder << ' # Projects\n\n' + sortedProjects.each { proj -> def traits = [] - - if (rootProjects.contains(project)) { + if (rootProjects.contains(proj)) { traits.add('shape=box') } - // --- 특정 모듈 유형 색상 우선 지정 --- - if (featureModules.contains(project)) { - traits.add('fillcolor="#FFC0CB"') // 핑크 (Feature) - } else if (domainModules.contains(project)) { - traits.add('fillcolor="#DAF7A6"') // 예: 라이트 그린/옐로우 (Domain) - 색상 변경 가능 - } else if (coreModules.contains(project)) { - traits.add('fillcolor="#ADD8E6"') // 라이트 블루 (Core) - } else if (dataModules.contains(project)) { - traits.add('fillcolor="#90EE90"') // 라이트 그린 (Data) - } - // --- 기존 플러그인 기반 색상 지정 로직 --- - else if (multiplatformProjects.contains(project)) { - traits.add('fillcolor="#ffd2b3"') - } else if (jsProjects.contains(project)) { - traits.add('fillcolor="#ffffba"') - } else if (androidProjects.contains(project)) { - traits.add('fillcolor="#baffc9"') - } else if (androidLibraryProjects.contains(project)) { - traits.add('fillcolor="#81D4FA"') - } else if (androidDynamicFeatureProjects.contains(project)) { - traits.add('fillcolor="#c9baff"') - } else if (javaProjects.contains(project)) { - traits.add('fillcolor="#ffb3ba"') - } else { - traits.add('fillcolor="#eeeeee"') // 그 외 기본 색상 - } + applyColorTrait(proj, traits, featureModules, domainModules, coreModules, dataModules, + multiplatformProjects, jsProjects, androidProjects, androidLibraryProjects, + androidDynamicFeatureProjects, javaProjects) - dot << " \"${project.path}\" [${traits.join(", ")}];\n" + builder << " \"${proj.path}\" [${traits.join(', ')}];\n" } - dot << '\n {rank = same;' - for (project in projects) { - if (rootProjects.contains(project)) { - dot << " \"${project.path}\";" + builder << '\n {rank = same;' + sortedProjects.each { proj -> + if (rootProjects.contains(proj)) { + builder << " \"${proj.path}\";" } } - dot << '}\n' + builder << '}\n\n' - dot << '\n # Dependencies\n\n' + builder << ' # Dependencies\n\n' dependencies.forEach { key, traits -> - dot << " \"${key.first.path}\" -> \"${key.second.path}\"" + def parts = key.split('->', 2) + builder << " \"${parts[0]}\" -> \"${parts[1]}\"" if (!traits.isEmpty()) { - dot << " [${traits.join(", ")}]" + builder << " [${traits.join(', ')}]" } - dot << '\n' + builder << '\n' } + builder << '}\n' - dot << '}\n' + return builder.toString() + } - def p = "dot -Tpng -O ${dotFileName}".execute([], dot.parentFile) - p.waitFor() - if (p.exitValue() != 0) { - throw new RuntimeException(p.errorStream.text) + private static void categorizeModule( + Project current, + Set featureModules, + Set domainModules, + Set coreModules, + Set dataModules, + Set multiplatformProjects, + Set jsProjects, + Set androidProjects, + Set androidLibraryProjects, + Set androidDynamicFeatureProjects, + Set javaProjects + ) { + if (current.path.startsWith(':feature')) { + featureModules.add(current) + } else if (current.path.contains(':domain')) { + domainModules.add(current) + } else if (current.path.contains(':core')) { + coreModules.add(current) + } else if (current.path.startsWith(':data')) { + dataModules.add(current) + } else if (current.plugins.hasPlugin('org.jetbrains.kotlin.multiplatform')) { + multiplatformProjects.add(current) + } else if (current.plugins.hasPlugin('kotlin2js')) { + jsProjects.add(current) + } else if (current.plugins.hasPlugin('com.android.application')) { + androidProjects.add(current) + } else if (current.plugins.hasPlugin('com.android.library')) { + androidLibraryProjects.add(current) + } else if (current.plugins.hasPlugin('com.android.dynamic-feature')) { + androidDynamicFeatureProjects.add(current) + } else if (current.plugins.hasPlugin('java-library') || current.plugins.hasPlugin('java')) { + javaProjects.add(current) } - dot.delete() + } - println("Project module dependency graph created at ${dot.absolutePath}.png") + private static void applyColorTrait( + Project project, + List traits, + Set featureModules, + Set domainModules, + Set coreModules, + Set dataModules, + Set multiplatformProjects, + Set jsProjects, + Set androidProjects, + Set androidLibraryProjects, + Set androidDynamicFeatureProjects, + Set javaProjects + ) { + if (featureModules.contains(project)) { + traits.add('fillcolor="#FFC0CB"') + } else if (domainModules.contains(project)) { + traits.add('fillcolor="#DAF7A6"') + } else if (coreModules.contains(project)) { + traits.add('fillcolor="#ADD8E6"') + } else if (dataModules.contains(project)) { + traits.add('fillcolor="#90EE90"') + } else if (multiplatformProjects.contains(project)) { + traits.add('fillcolor="#ffd2b3"') + } else if (jsProjects.contains(project)) { + traits.add('fillcolor="#ffffba"') + } else if (androidProjects.contains(project)) { + traits.add('fillcolor="#baffc9"') + } else if (androidLibraryProjects.contains(project)) { + traits.add('fillcolor="#81D4FA"') + } else if (androidDynamicFeatureProjects.contains(project)) { + traits.add('fillcolor="#c9baff"') + } else if (javaProjects.contains(project)) { + traits.add('fillcolor="#ffb3ba"') + } else { + traits.add('fillcolor="#eeeeee"') + } } } + +def projectDependencyGraphTask = tasks.register('projectDependencyGraph', ProjectDependencyGraphTask) + +gradle.projectsEvaluated(new Action() { + @Override + void execute(Gradle gradle) { + projectDependencyGraphTask.configure(new Action() { + @Override + void execute(ProjectDependencyGraphTask task) { + task.dotGraphContent.set(DependencyGraphBuilder.build(gradle.rootProject)) + } + }) + } +}) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3aa13360..ea5df3af 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] # SDK Versions compileSdk = "35" +javaxInject = "1" minSdk = "28" targetSdk = "35" @@ -87,6 +88,7 @@ profileinstaller = "1.4.1" ## Android Libraries android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android-gradle-plugin" } +javax-inject = { module = "javax.inject:javax.inject", version.ref = "javaxInject" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } ksp-gradle-plugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } diff --git a/project.dot.png b/project.dot.png index 77a616a5..725757a3 100644 Binary files a/project.dot.png and b/project.dot.png differ