diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4acc31ae..19b02048 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,21 +29,24 @@ android { 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) implementation(projects.feature.alarmInteraction) implementation(projects.feature.fortune) implementation(projects.feature.mission) implementation(projects.feature.setting) - implementation(projects.feature.navigator) + implementation(projects.feature.webview) implementation(libs.firebase.analytics) implementation(libs.firebase.crashlytics) implementation(libs.play.services.ads) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 51ec3303..3b4a973a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,7 +30,7 @@ android:value="@string/admob_app_id" /> alarms.forEach { alarm -> - alarmHelper.scheduleAlarm(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 c982fe7e..e0db3f5c 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 @@ -18,14 +18,14 @@ import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.net.toUri import com.yapp.alarm.AlarmConstants -import com.yapp.alarm.AlarmHelper +import com.yapp.alarm.AndroidAlarmScheduler import com.yapp.alarm.pendingIntent.interaction.createAlarmAlertPendingIntent import com.yapp.alarm.pendingIntent.interaction.createAlarmDismissPendingIntent import com.yapp.alarm.pendingIntent.interaction.createAlarmSnoozePendingIntent import com.yapp.alarm.pendingIntent.interaction.createNavigateToMissionPendingIntent -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay +import com.yapp.domain.repository.FortuneRepository import com.yapp.domain.usecase.AlarmUseCase import com.yapp.media.sound.SoundPlayer import dagger.hilt.android.AndroidEntryPoint @@ -51,10 +51,10 @@ class AlarmService : Service() { private lateinit var vibrator: Vibrator @Inject - lateinit var alarmHelper: AlarmHelper + lateinit var androidAlarmScheduler: AndroidAlarmScheduler @Inject - lateinit var userPreferences: UserPreferences + lateinit var fortuneRepository: FortuneRepository private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) @@ -103,7 +103,7 @@ class AlarmService : Service() { // 반복 요일 알람 시, 다음 주 동일 요일 알람 예약 if (!isOneTimeAlarm) { intent.getStringExtra(AlarmConstants.EXTRA_ALARM_DAY)?.let { - alarmHelper.scheduleWeeklyAlarm(alarm, AlarmDay.valueOf(it)) + androidAlarmScheduler.scheduleWeeklyAlarm(alarm, AlarmDay.valueOf(it)) } } @@ -126,7 +126,7 @@ class AlarmService : Service() { } private suspend fun shouldNavigateToMission(): Boolean { - val fortuneDate = userPreferences.fortuneDateFlow.firstOrNull() + val fortuneDate = fortuneRepository.fortuneDateFlow.firstOrNull() val todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) return fortuneDate != todayDate } diff --git a/core/common/src/main/java/com/yapp/common/security/CryptoManager.kt b/core/common/src/main/java/com/yapp/common/security/CryptoManager.kt deleted file mode 100644 index d3995727..00000000 --- a/core/common/src/main/java/com/yapp/common/security/CryptoManager.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.yapp.common.security - -interface CryptoManager { - fun encryptData(keyAlias: String, text: String): Pair - - fun decryptData(keyAlias: String, encryptedData: ByteArray, iv: ByteArray): ByteArray -} diff --git a/core/datastore/src/main/java/com/yapp/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/yapp/datastore/di/DataStoreModule.kt index 41447bb8..895621d9 100644 --- a/core/datastore/src/main/java/com/yapp/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/java/com/yapp/datastore/di/DataStoreModule.kt @@ -2,12 +2,9 @@ package com.yapp.datastore.di import android.content.Context import androidx.datastore.core.DataStore -import androidx.datastore.core.DataStoreFactory import androidx.datastore.dataStoreFile import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences -import com.yapp.datastore.token.AuthToken -import com.yapp.datastore.token.TokenDataSerializer import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -18,18 +15,6 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object DataStoreModule { - @Provides - @Singleton - fun providesTokenDataStore( - @ApplicationContext context: Context, - tokenDataSerializer: TokenDataSerializer, - ): DataStore = - DataStoreFactory.create( - serializer = tokenDataSerializer, - ) { - context.dataStoreFile("token.json") - } - @Provides @Singleton fun providesPreferencesDataStore( diff --git a/core/datastore/src/main/java/com/yapp/datastore/token/AuthToken.kt b/core/datastore/src/main/java/com/yapp/datastore/token/AuthToken.kt deleted file mode 100644 index ec40212a..00000000 --- a/core/datastore/src/main/java/com/yapp/datastore/token/AuthToken.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.yapp.datastore.token - -import kotlinx.serialization.Serializable - -@Serializable -data class AuthToken( - val accessToken: String = "", - val refreshToken: String = "", - val isSigned: Boolean = false, -) diff --git a/core/datastore/src/main/java/com/yapp/datastore/token/TokenDataSerializer.kt b/core/datastore/src/main/java/com/yapp/datastore/token/TokenDataSerializer.kt deleted file mode 100644 index ebe7d713..00000000 --- a/core/datastore/src/main/java/com/yapp/datastore/token/TokenDataSerializer.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.yapp.datastore.token - -import androidx.datastore.core.Serializer -import com.yapp.common.security.CryptoManager -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlinx.serialization.json.Json -import java.io.InputStream -import java.io.OutputStream -import javax.inject.Inject - -class TokenDataSerializer @Inject constructor( - private val cryptoManager: CryptoManager, -) : Serializer { - - private val securityKeyAlias = "data-store" - - override val defaultValue: AuthToken - get() = AuthToken() - - override suspend fun readFrom(input: InputStream): AuthToken { - val encryptedDataWithIv = input.readBytes() - - if (encryptedDataWithIv.size < 12) return defaultValue - - val (iv, encryptedData) = encryptedDataWithIv.splitIvAndData() - return runCatching { - val decryptedBytes = cryptoManager.decryptData(securityKeyAlias, encryptedData, iv) - Json.decodeFromString(AuthToken.serializer(), decryptedBytes.decodeToString()) - }.getOrElse { - it.printStackTrace() - defaultValue // 복호화 실패 시 defaultValue - } - } - - override suspend fun writeTo(t: AuthToken, output: OutputStream) { - val encryptedResult = cryptoManager.encryptData( - securityKeyAlias, - Json.encodeToString(AuthToken.serializer(), t), - ) - withContext(Dispatchers.IO) { - output.write(encryptedResult.toCombinedByteArray()) - } - } - - private fun ByteArray.splitIvAndData(): Pair { - val iv = this.copyOfRange(0, 12) - val encryptedData = this.copyOfRange(12, this.size) - return iv to encryptedData - } - - private fun Pair.toCombinedByteArray(): ByteArray { - return second + first - } -} diff --git a/core/datastore/src/main/java/com/yapp/datastore/token/TokenDataStore.kt b/core/datastore/src/main/java/com/yapp/datastore/token/TokenDataStore.kt deleted file mode 100644 index 75d9ee5d..00000000 --- a/core/datastore/src/main/java/com/yapp/datastore/token/TokenDataStore.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.yapp.datastore.token - -import android.util.Log -import androidx.datastore.core.DataStore -import java.io.IOException -import javax.inject.Inject - -class TokenDataStore @Inject constructor( - private val tokenPreferences: DataStore, -) { - - val token = tokenPreferences.data - - suspend fun setAuthToken(authToken: AuthToken) { - updateDataSafely { copy(authToken.accessToken, authToken.refreshToken, authToken.isSigned) } - } - - suspend fun setAutoLogin(isSigned: Boolean) { - updateDataSafely { copy(isSigned = isSigned) } - } - - suspend fun setAccessToken(accessToken: String) { - updateDataSafely { copy(accessToken = accessToken) } - } - - suspend fun setRefreshToken(refreshToken: String) { - updateDataSafely { copy(refreshToken = refreshToken) } - } - - private suspend fun updateDataSafely(transform: AuthToken.() -> AuthToken) { - runCatching { - tokenPreferences.updateData { it.transform() } - }.onFailure { exception -> - if (exception is IOException) { - Log.e("TokenDataStore", "데이터 업데이트 실패: ${exception.message}") - } - } - } -} diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index b15a325a..beabc390 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -11,7 +11,6 @@ android { } dependencies { - implementation(projects.core.datastore) implementation(projects.core.common) implementation(platform(libs.okhttp.bom)) implementation(libs.okhttp.logging) diff --git a/core/network/src/main/java/com/yapp/network/TokenRefreshService.kt b/core/network/src/main/java/com/yapp/network/TokenRefreshService.kt deleted file mode 100644 index db8def42..00000000 --- a/core/network/src/main/java/com/yapp/network/TokenRefreshService.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.yapp.network - -import com.yapp.network.model.BaseResponse -import com.yapp.network.model.ResponseAuthRefreshDto -import retrofit2.http.Header -import retrofit2.http.POST - -interface TokenRefreshService { - @POST("/$API/$VERSION/$AUTH/$REISSUE") - suspend fun postAuthRefresh( - @Header("refreshToken") refreshToken: String, - ): BaseResponse - - companion object { - const val API = "api" - const val VERSION = "v1" - const val AUTH = "auth" - const val REISSUE = "reissue" - } -} diff --git a/core/network/src/main/java/com/yapp/network/authenticator/AuthenticationIntercept.kt b/core/network/src/main/java/com/yapp/network/authenticator/AuthenticationIntercept.kt deleted file mode 100644 index 6051ebd9..00000000 --- a/core/network/src/main/java/com/yapp/network/authenticator/AuthenticationIntercept.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.yapp.network.authenticator - -import com.yapp.datastore.token.TokenDataStore -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response -import javax.inject.Inject - -class AuthenticationIntercept @Inject constructor( - private val datastore: TokenDataStore, -) : Interceptor { - - override fun intercept(chain: Interceptor.Chain): Response { - val originalRequest = chain.request() - val authRequest = originalRequest.addAuthorizationHeader() - return chain.proceed(authRequest) - } - - private fun Request.addAuthorizationHeader(): Request { - val accessToken = runBlocking { datastore.token.first().accessToken } - return this.newBuilder() - .addHeader("Authorization", "Bearer $accessToken") - .build() - } -} diff --git a/core/network/src/main/java/com/yapp/network/authenticator/OrbitAuthenticator.kt b/core/network/src/main/java/com/yapp/network/authenticator/OrbitAuthenticator.kt deleted file mode 100644 index 9183a058..00000000 --- a/core/network/src/main/java/com/yapp/network/authenticator/OrbitAuthenticator.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.yapp.network.authenticator - -import android.content.Context -import com.jakewharton.processphoenix.ProcessPhoenix -import com.yapp.datastore.token.TokenDataStore -import com.yapp.network.TokenRefreshService -import com.yapp.network.model.ResponseAuthRefreshDto -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.runBlocking -import okhttp3.Authenticator -import okhttp3.Request -import okhttp3.Response -import okhttp3.Route -import javax.inject.Inject - -class OrbitAuthenticator @Inject constructor( - private val dataStore: TokenDataStore, - private val tokenRefreshService: TokenRefreshService, - @ApplicationContext private val context: Context, -) : Authenticator { - - override fun authenticate(route: Route?, response: Response): Request? { - if (response.code == CODE_TOKEN_EXPIRED) { - return handleTokenExpiration(response) - } - return null - } - - private fun handleTokenExpiration(response: Response): Request? { - val newTokens = refreshTokens() - return newTokens?.let { - response.request.newBuilder() - .header("Authorization", "Bearer ${it.accessToken}") - .build() - } - } - - private fun refreshTokens(): ResponseAuthRefreshDto? { - return runCatching { - runBlocking { - val refreshToken = dataStore.token.first().refreshToken - tokenRefreshService.postAuthRefresh(refreshToken).data - } - }.onSuccess { newToken -> - runBlocking { - newToken?.let { - dataStore.setAccessToken(it.accessToken) - } - } - }.onFailure { - handleTokenRefreshFailure() - }.getOrNull() - } - - private fun handleTokenRefreshFailure() { - runBlocking { dataStore.setAutoLogin(false) } - ProcessPhoenix.triggerRebirth(context) - } - - companion object { - const val CODE_TOKEN_EXPIRED = 401 - } -} diff --git a/core/network/src/main/java/com/yapp/network/di/NetworkModule.kt b/core/network/src/main/java/com/yapp/network/di/NetworkModule.kt index 3435cb09..10276dfb 100644 --- a/core/network/src/main/java/com/yapp/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/yapp/network/di/NetworkModule.kt @@ -1,9 +1,6 @@ package com.yapp.network.di import com.yapp.common.buildconfig.BuildConfigFieldProvider -import com.yapp.network.TokenRefreshService -import com.yapp.network.authenticator.AuthenticationIntercept -import com.yapp.network.authenticator.OrbitAuthenticator import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -22,11 +19,6 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) object NetworkModule { - @Provides - @Singleton - fun provideTokenRefreshService(@NoneAuth retrofit: Retrofit) = - retrofit.create(TokenRefreshService::class.java) - @Provides @Singleton fun provideLoggingInterceptor( @@ -51,13 +43,11 @@ object NetworkModule { fun provideAuthOkHttpClient( loggingInterceptor: HttpLoggingInterceptor, authInterceptor: Interceptor, - authenticator: OrbitAuthenticator, ): OkHttpClient = OkHttpClient.Builder() .retryOnConnectionFailure(true) .addInterceptor(loggingInterceptor) .addInterceptor(authInterceptor) - .authenticator(authenticator) .build() @Provides @@ -106,8 +96,4 @@ object NetworkModule { .baseUrl(buildConfigFieldProvider.get().baseUrl) .client(okHttpClient) .build() - - @Provides - @Singleton - fun provideAuthInterceptor(interceptor: AuthenticationIntercept): Interceptor = interceptor } diff --git a/core/network/src/main/java/com/yapp/network/model/ResponseAuthRefreshDto.kt b/core/network/src/main/java/com/yapp/network/model/ResponseAuthRefreshDto.kt deleted file mode 100644 index d45c731a..00000000 --- a/core/network/src/main/java/com/yapp/network/model/ResponseAuthRefreshDto.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.yapp.network.model - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class ResponseAuthRefreshDto( - @SerialName("accessToken") - val accessToken: String, -) diff --git a/core/security/.gitignore b/core/security/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/core/security/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/core/security/build.gradle.kts b/core/security/build.gradle.kts deleted file mode 100644 index f727009c..00000000 --- a/core/security/build.gradle.kts +++ /dev/null @@ -1,14 +0,0 @@ -import com.yapp.convention.setNamespace - -plugins { - id("orbit.android.library") - id("orbit.android.hilt") -} - -android { - setNamespace("core.security") -} - -dependencies { - implementation(projects.core.common) -} diff --git a/core/security/consumer-rules.pro b/core/security/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/core/security/proguard-rules.pro b/core/security/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/core/security/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/security/src/main/AndroidManifest.xml b/core/security/src/main/AndroidManifest.xml deleted file mode 100644 index 8bdb7e14..00000000 --- a/core/security/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/core/security/src/main/java/com/yapp/security/CryptoManagerImpl.kt b/core/security/src/main/java/com/yapp/security/CryptoManagerImpl.kt deleted file mode 100644 index c21d2810..00000000 --- a/core/security/src/main/java/com/yapp/security/CryptoManagerImpl.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.yapp.security - -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties.BLOCK_MODE_GCM -import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE -import android.security.keystore.KeyProperties.KEY_ALGORITHM_AES -import android.security.keystore.KeyProperties.PURPOSE_DECRYPT -import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT -import com.yapp.common.security.CryptoManager -import java.security.KeyStore -import javax.crypto.Cipher -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey -import javax.crypto.spec.GCMParameterSpec -import javax.inject.Inject - -class CryptoManagerImpl @Inject constructor() : CryptoManager { - - private val provider = "AndroidKeyStore" - private val charset = Charsets.UTF_8 - - private val cipher: Cipher by lazy { Cipher.getInstance("AES/GCM/NoPadding") } - private val keyStore: KeyStore by lazy { KeyStore.getInstance(provider).apply { load(null) } } - private val keyGenerator: KeyGenerator by lazy { KeyGenerator.getInstance(KEY_ALGORITHM_AES, provider) } - - /** - * 데이터 암호화. - * @param keyAlias 키 별칭 - * @param text 암호화할 텍스트 - * @return 암호화된 데이터 + 초기화 벡터(IV) - */ - override fun encryptData(keyAlias: String, text: String): Pair { - val secretKey = getOrCreateSecretKey(keyAlias) - cipher.init(Cipher.ENCRYPT_MODE, secretKey) - return cipher.doFinal(text.toByteArray(charset)) to cipher.iv - } - - /** - * 데이터를 복호화. - * @param keyAlias 키 별칭 - * @param encryptedData 암호화된 데이터 - * @param iv 초기화 벡터(IV) - * @return 복호화된 데이터 - */ - override fun decryptData(keyAlias: String, encryptedData: ByteArray, iv: ByteArray): ByteArray { - val secretKey = getSecretKey(keyAlias) - cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv)) - return cipher.doFinal(encryptedData) - } - - /** - * 키가 없으면 생성하고, 이미 존재하면 가져옴. - * @param keyAlias 키 별칭 - * @return SecretKey - */ - private fun getOrCreateSecretKey(keyAlias: String): SecretKey = - keyStore.getSecretKeyOrNull(keyAlias) ?: generateSecretKey(keyAlias) - - /** - * 새로운 SecretKey를 생성. - * @param keyAlias 키 별칭 - * @return SecretKey - */ - private fun generateSecretKey(keyAlias: String): SecretKey { - val parameterSpec = KeyGenParameterSpec.Builder(keyAlias, PURPOSE_ENCRYPT or PURPOSE_DECRYPT) - .apply { - setBlockModes(BLOCK_MODE_GCM) - setEncryptionPaddings(ENCRYPTION_PADDING_NONE) - }.build() - keyGenerator.init(parameterSpec) - return keyGenerator.generateKey() - } - - /** - * KeyStore에서 SecretKey 가져오기. - * @param keyAlias 키 별칭 - * @return SecretKey - */ - private fun getSecretKey(keyAlias: String): SecretKey = - keyStore.getSecretKeyOrNull(keyAlias) - ?: throw IllegalStateException("SecretKey for alias $keyAlias does not exist") - - /** - * 키 존재하지 않으면 null 반환. - */ - private fun KeyStore.getSecretKeyOrNull(keyAlias: String): SecretKey? = - (getEntry(keyAlias, null) as? KeyStore.SecretKeyEntry)?.secretKey -} diff --git a/core/security/src/main/java/com/yapp/security/di/SecurityModule.kt b/core/security/src/main/java/com/yapp/security/di/SecurityModule.kt deleted file mode 100644 index 3e8a6c99..00000000 --- a/core/security/src/main/java/com/yapp/security/di/SecurityModule.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.yapp.security.di - -import com.yapp.common.security.CryptoManager -import com.yapp.security.CryptoManagerImpl -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -abstract class SecurityModule { - @Singleton - @Binds - abstract fun bindsCryptoManager(cryptoManagerImpl: CryptoManagerImpl): CryptoManager -} diff --git a/data/src/main/java/com/yapp/data/remote/di/RepositoryModule.kt b/data/src/main/java/com/yapp/data/di/RepositoryModule.kt similarity index 59% rename from data/src/main/java/com/yapp/data/remote/di/RepositoryModule.kt rename to data/src/main/java/com/yapp/data/di/RepositoryModule.kt index ed92d2db..e50b2ef6 100644 --- a/data/src/main/java/com/yapp/data/remote/di/RepositoryModule.kt +++ b/data/src/main/java/com/yapp/data/di/RepositoryModule.kt @@ -1,12 +1,14 @@ -package com.yapp.data.remote.di +package com.yapp.data.di -import com.yapp.data.remote.repositoryimpl.DummyRepositoryImpl -import com.yapp.data.remote.repositoryimpl.FortuneRepositoryImpl -import com.yapp.data.remote.repositoryimpl.RemoteConfigRepositoryImpl -import com.yapp.data.remote.repositoryimpl.SignUpRepositoryImpl -import com.yapp.data.remote.repositoryimpl.UserInfoRepositoryImpl -import com.yapp.domain.repository.DummyRepository +import com.yapp.data.repositoryimpl.AlarmRepositoryImpl +import com.yapp.data.repositoryimpl.FortuneRepositoryImpl +import com.yapp.data.repositoryimpl.ImageRepositoryImpl +import com.yapp.data.repositoryimpl.RemoteConfigRepositoryImpl +import com.yapp.data.repositoryimpl.SignUpRepositoryImpl +import com.yapp.data.repositoryimpl.UserInfoRepositoryImpl +import com.yapp.domain.repository.AlarmRepository import com.yapp.domain.repository.FortuneRepository +import com.yapp.domain.repository.ImageRepository import com.yapp.domain.repository.RemoteConfigRepository import com.yapp.domain.repository.SignUpRepository import com.yapp.domain.repository.UserInfoRepository @@ -21,15 +23,21 @@ import javax.inject.Singleton abstract class RepositoryModule { @Binds @Singleton - abstract fun bindsDummyRepository( - dummyRepository: DummyRepositoryImpl, - ): DummyRepository + abstract fun bindsAlarmRepository( + alarmRepository: AlarmRepositoryImpl, + ): AlarmRepository @Binds @Singleton - abstract fun bindsSignUpRepository( - signUpRepository: SignUpRepositoryImpl, - ): SignUpRepository + abstract fun bindsFortuneRepository( + fortuneRepository: FortuneRepositoryImpl, + ): FortuneRepository + + @Binds + @Singleton + abstract fun bindsImageRepository( + imageRepository: ImageRepositoryImpl, + ): ImageRepository @Binds @Singleton @@ -39,9 +47,9 @@ abstract class RepositoryModule { @Binds @Singleton - abstract fun bindsFortuneRepository( - fortuneRepository: FortuneRepositoryImpl, - ): FortuneRepository + abstract fun bindsSignUpRepository( + signUpRepository: SignUpRepositoryImpl, + ): SignUpRepository @Binds @Singleton diff --git a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt index f41b0caa..0579eab5 100644 --- a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt +++ b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt @@ -5,6 +5,8 @@ import com.yapp.domain.model.Alarm import kotlinx.coroutines.flow.Flow interface AlarmLocalDataSource { + val firstDismissedAlarmIdFlow: Flow + fun getAllAlarms(): Flow> fun getPagedAlarms(limit: Int, offset: Int): Flow> fun getAlarmsByTime(hour: Int, minute: Int, isAm: Boolean): Flow> @@ -14,4 +16,6 @@ interface AlarmLocalDataSource { suspend fun updateAlarmActive(id: Long, active: Boolean): Int suspend fun getAlarm(id: Long): Alarm? suspend fun deleteAlarm(id: Long): Int + suspend fun saveFirstDismissedAlarmId(alarmId: Long) + suspend fun clearDismissedAlarmId() } 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 d84acfa8..18a86ce5 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 @@ -3,6 +3,7 @@ package com.yapp.data.local.datasource import com.yapp.data.local.AlarmDao import com.yapp.data.local.AlarmEntity import com.yapp.data.local.toDomain +import com.yapp.datastore.UserPreferences import com.yapp.domain.model.Alarm import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -10,7 +11,10 @@ import javax.inject.Inject class AlarmLocalDataSourceImpl @Inject constructor( private val alarmDao: AlarmDao, + private val userPreferences: UserPreferences, ) : AlarmLocalDataSource { + override val firstDismissedAlarmIdFlow: Flow = userPreferences.firstDismissedAlarmIdFlow + override fun getAllAlarms(): Flow> { return alarmDao.getAllAlarms() .map { alarmEntities -> alarmEntities.map { it.toDomain() } } @@ -53,4 +57,12 @@ class AlarmLocalDataSourceImpl @Inject constructor( override suspend fun deleteAlarm(id: Long): Int { return alarmDao.deleteAlarm(id) } + + override suspend fun saveFirstDismissedAlarmId(alarmId: Long) { + userPreferences.saveFirstDismissedAlarmId(alarmId) + } + + override suspend fun clearDismissedAlarmId() { + userPreferences.clearDismissedAlarmId() + } } diff --git a/data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSource.kt b/data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSource.kt new file mode 100644 index 00000000..149234f7 --- /dev/null +++ b/data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSource.kt @@ -0,0 +1,20 @@ +package com.yapp.data.local.datasource + +import kotlinx.coroutines.flow.Flow + +interface FortuneLocalDataSource { + val fortuneIdFlow: Flow + val fortuneDateFlow: Flow + val fortuneImageIdFlow: Flow + val fortuneScoreFlow: Flow + val hasNewFortuneFlow: Flow + val firstDismissedAlarmIdFlow: Flow + + suspend fun saveFortuneId(fortuneId: Long) + suspend fun markFortuneAsChecked() + suspend fun saveFortuneImageId(imageResId: Int) + suspend fun saveFortuneScore(score: Int) + suspend fun saveFirstDismissedAlarmId(alarmId: Long) + suspend fun clearDismissedAlarmId() + suspend fun clearFortuneId() +} diff --git a/data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt b/data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt new file mode 100644 index 00000000..6dbe10f9 --- /dev/null +++ b/data/src/main/java/com/yapp/data/local/datasource/FortuneLocalDataSourceImpl.kt @@ -0,0 +1,44 @@ +package com.yapp.data.local.datasource + +import com.yapp.datastore.UserPreferences +import javax.inject.Inject + +class FortuneLocalDataSourceImpl @Inject constructor( + private val userPreferences: UserPreferences, +) : FortuneLocalDataSource { + + override val fortuneIdFlow = userPreferences.fortuneIdFlow + override val fortuneDateFlow = userPreferences.fortuneDateFlow + override val fortuneImageIdFlow = userPreferences.fortuneImageIdFlow + override val fortuneScoreFlow = userPreferences.fortuneScoreFlow + override val hasNewFortuneFlow = userPreferences.hasNewFortuneFlow + override val firstDismissedAlarmIdFlow = userPreferences.firstDismissedAlarmIdFlow + + override suspend fun saveFortuneId(fortuneId: Long) { + userPreferences.saveFortuneId(fortuneId) + } + + override suspend fun markFortuneAsChecked() { + userPreferences.markFortuneAsChecked() + } + + override suspend fun saveFortuneImageId(imageResId: Int) { + userPreferences.saveFortuneImageId(imageResId) + } + + override suspend fun saveFortuneScore(score: Int) { + userPreferences.saveFortuneScore(score) + } + + override suspend fun saveFirstDismissedAlarmId(alarmId: Long) { + userPreferences.saveFirstDismissedAlarmId(alarmId) + } + + override suspend fun clearDismissedAlarmId() { + userPreferences.clearDismissedAlarmId() + } + + override suspend fun clearFortuneId() { + userPreferences.clearFortuneId() + } +} diff --git a/data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSource.kt b/data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSource.kt new file mode 100644 index 00000000..37b4fc5a --- /dev/null +++ b/data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSource.kt @@ -0,0 +1,14 @@ +package com.yapp.data.local.datasource + +import kotlinx.coroutines.flow.Flow + +interface UserLocalDataSource { + val userIdFlow: Flow + val userNameFlow: Flow + val onboardingCompletedFlow: Flow + + suspend fun saveUserId(userId: Long) + suspend fun saveUserName(userName: String) + suspend fun setOnboardingCompleted() + suspend fun clearUserData() +} diff --git a/data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSourceImpl.kt b/data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSourceImpl.kt new file mode 100644 index 00000000..7e7d4324 --- /dev/null +++ b/data/src/main/java/com/yapp/data/local/datasource/UserLocalDataSourceImpl.kt @@ -0,0 +1,30 @@ +package com.yapp.data.local.datasource + +import com.yapp.datastore.UserPreferences +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class UserLocalDataSourceImpl @Inject constructor( + private val userPreferences: UserPreferences, +) : UserLocalDataSource { + + override val userIdFlow: Flow = userPreferences.userIdFlow + override val userNameFlow: Flow = userPreferences.userNameFlow + override val onboardingCompletedFlow: Flow = userPreferences.onboardingCompletedFlow + + override suspend fun saveUserId(userId: Long) { + userPreferences.saveUserId(userId) + } + + override suspend fun saveUserName(userName: String) { + userPreferences.saveUserName(userName) + } + + override suspend fun setOnboardingCompleted() { + userPreferences.setOnboardingCompleted() + } + + override suspend fun clearUserData() { + userPreferences.clearUserData() + } +} diff --git a/data/src/main/java/com/yapp/data/local/di/DataSourceModule.kt b/data/src/main/java/com/yapp/data/local/di/DataSourceModule.kt index eb567b3e..4a1ad9b8 100644 --- a/data/src/main/java/com/yapp/data/local/di/DataSourceModule.kt +++ b/data/src/main/java/com/yapp/data/local/di/DataSourceModule.kt @@ -2,6 +2,10 @@ package com.yapp.data.local.di import com.yapp.data.local.datasource.AlarmLocalDataSource import com.yapp.data.local.datasource.AlarmLocalDataSourceImpl +import com.yapp.data.local.datasource.FortuneLocalDataSource +import com.yapp.data.local.datasource.FortuneLocalDataSourceImpl +import com.yapp.data.local.datasource.UserLocalDataSource +import com.yapp.data.local.datasource.UserLocalDataSourceImpl import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -16,4 +20,16 @@ abstract class DataSourceModule { abstract fun bindsAlarmDataSource( alarmLocalDataSource: AlarmLocalDataSourceImpl, ): AlarmLocalDataSource + + @Binds + @Singleton + abstract fun bindsFortuneDataSource( + fortuneLocalDataSource: FortuneLocalDataSourceImpl, + ): FortuneLocalDataSource + + @Binds + @Singleton + abstract fun bindsUserDataSource( + userLocalDataSource: UserLocalDataSourceImpl, + ): UserLocalDataSource } diff --git a/data/src/main/java/com/yapp/data/local/di/RepositoryModule.kt b/data/src/main/java/com/yapp/data/local/di/RepositoryModule.kt deleted file mode 100644 index 3d7235c6..00000000 --- a/data/src/main/java/com/yapp/data/local/di/RepositoryModule.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.yapp.data.local.di - -import com.yapp.data.local.repositoryimpl.AlarmRepositoryImpl -import com.yapp.data.local.repositoryimpl.ImageRepositoryImpl -import com.yapp.domain.repository.AlarmRepository -import com.yapp.domain.repository.ImageRepository -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -abstract class RepositoryModule { - @Binds - @Singleton - abstract fun bindsAlarmRepository( - alarmRepository: AlarmRepositoryImpl, - ): AlarmRepository - - @Binds - @Singleton - abstract fun bindsImageRepository( - imageRepository: ImageRepositoryImpl, - ): ImageRepository -} diff --git a/data/src/main/java/com/yapp/data/remote/datasource/DummyDataSource.kt b/data/src/main/java/com/yapp/data/remote/datasource/DummyDataSource.kt deleted file mode 100644 index 2d12449b..00000000 --- a/data/src/main/java/com/yapp/data/remote/datasource/DummyDataSource.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.yapp.data.remote.datasource - -import com.yapp.data.remote.dto.request.RequestDummyDto -import com.yapp.data.remote.dto.response.ResponseDummyDto -import com.yapp.network.model.BaseResponse - -interface DummyDataSource { - suspend fun fetchDummy(): BaseResponse - suspend fun saveDummy(requestDummyDto: RequestDummyDto): BaseResponse -} diff --git a/data/src/main/java/com/yapp/data/remote/datasource/DummyDataSourceImpl.kt b/data/src/main/java/com/yapp/data/remote/datasource/DummyDataSourceImpl.kt deleted file mode 100644 index 102bbdeb..00000000 --- a/data/src/main/java/com/yapp/data/remote/datasource/DummyDataSourceImpl.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.yapp.data.remote.datasource - -import com.yapp.data.remote.dto.request.RequestDummyDto -import com.yapp.data.remote.dto.response.ResponseDummyDto -import com.yapp.data.remote.service.DummyService -import com.yapp.network.model.BaseResponse -import javax.inject.Inject - -class DummyDataSourceImpl @Inject constructor( - private val dummyService: DummyService, -) : DummyDataSource { - override suspend fun fetchDummy(): BaseResponse = dummyService.fetchDummy() - override suspend fun saveDummy(requestDummyDto: RequestDummyDto): BaseResponse = dummyService.saveDummy(requestDummyDto) -} diff --git a/data/src/main/java/com/yapp/data/remote/di/DataSourceModule.kt b/data/src/main/java/com/yapp/data/remote/di/DataSourceModule.kt index e7f06d23..f30ecb5d 100644 --- a/data/src/main/java/com/yapp/data/remote/di/DataSourceModule.kt +++ b/data/src/main/java/com/yapp/data/remote/di/DataSourceModule.kt @@ -1,7 +1,5 @@ package com.yapp.data.remote.di -import com.yapp.data.remote.datasource.DummyDataSource -import com.yapp.data.remote.datasource.DummyDataSourceImpl import com.yapp.data.remote.datasource.FortuneDataSource import com.yapp.data.remote.datasource.FortuneDataSourceImpl import com.yapp.data.remote.datasource.SignUpDataSource @@ -17,11 +15,6 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) abstract class DataSourceModule { - @Binds - @Singleton - abstract fun bindsDummyDataSource( - dummyDataSource: DummyDataSourceImpl, - ): DummyDataSource @Binds @Singleton diff --git a/data/src/main/java/com/yapp/data/remote/di/ServiceModule.kt b/data/src/main/java/com/yapp/data/remote/di/ServiceModule.kt index 8e5f451a..458db14b 100644 --- a/data/src/main/java/com/yapp/data/remote/di/ServiceModule.kt +++ b/data/src/main/java/com/yapp/data/remote/di/ServiceModule.kt @@ -1,7 +1,6 @@ package com.yapp.data.remote.di import com.yapp.data.remote.service.ApiService -import com.yapp.data.remote.service.DummyService import com.yapp.network.di.NoneAuth import dagger.Module import dagger.Provides @@ -15,11 +14,6 @@ import javax.inject.Singleton object ServiceModule { @Provides @Singleton - fun providesDummyService(@NoneAuth retrofit: Retrofit): DummyService = - retrofit.create(DummyService::class.java) - - @Provides - @Singleton - fun providesSignUpService(@NoneAuth retrofit: Retrofit): ApiService = + fun providesApiService(@NoneAuth retrofit: Retrofit): ApiService = retrofit.create(ApiService::class.java) } diff --git a/data/src/main/java/com/yapp/data/remote/dto/request/RequestDummyDto.kt b/data/src/main/java/com/yapp/data/remote/dto/request/RequestDummyDto.kt deleted file mode 100644 index ff249f68..00000000 --- a/data/src/main/java/com/yapp/data/remote/dto/request/RequestDummyDto.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.yapp.data.remote.dto.request - -import com.yapp.domain.model.Dummy -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class RequestDummyDto( - @SerialName("id") val id: Int, - @SerialName("name") val name: String, -) -fun Dummy.toData() = RequestDummyDto( - id = id, - name = name, -) diff --git a/data/src/main/java/com/yapp/data/remote/dto/response/FortuneResponse.kt b/data/src/main/java/com/yapp/data/remote/dto/response/FortuneResponse.kt index 925cc1c3..49709723 100644 --- a/data/src/main/java/com/yapp/data/remote/dto/response/FortuneResponse.kt +++ b/data/src/main/java/com/yapp/data/remote/dto/response/FortuneResponse.kt @@ -1,7 +1,7 @@ package com.yapp.data.remote.dto.response -import com.yapp.domain.model.fortune.Fortune -import com.yapp.domain.model.fortune.FortuneDetailModel +import com.yapp.domain.model.Fortune +import com.yapp.domain.model.FortuneDetailModel import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/data/src/main/java/com/yapp/data/remote/dto/response/ResponseDummyDto.kt b/data/src/main/java/com/yapp/data/remote/dto/response/ResponseDummyDto.kt deleted file mode 100644 index 9fcfc078..00000000 --- a/data/src/main/java/com/yapp/data/remote/dto/response/ResponseDummyDto.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.yapp.data.remote.dto.response - -import com.yapp.domain.model.Dummy -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class ResponseDummyDto( - @SerialName("id") val id: Int, - @SerialName("name") val name: String, -) -fun ResponseDummyDto.toDomain() = Dummy( - id = id, - name = name, -) diff --git a/data/src/main/java/com/yapp/data/remote/repositoryimpl/DummyRepositoryImpl.kt b/data/src/main/java/com/yapp/data/remote/repositoryimpl/DummyRepositoryImpl.kt deleted file mode 100644 index c581348a..00000000 --- a/data/src/main/java/com/yapp/data/remote/repositoryimpl/DummyRepositoryImpl.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.yapp.data.remote.repositoryimpl - -import com.yapp.data.remote.datasource.DummyDataSource -import com.yapp.data.remote.dto.request.toData -import com.yapp.data.remote.dto.response.toDomain -import com.yapp.data.remote.utils.ApiError -import com.yapp.data.remote.utils.safeApiCall -import com.yapp.domain.model.Dummy -import com.yapp.domain.repository.DummyRepository -import javax.inject.Inject - -class DummyRepositoryImpl @Inject constructor( - private val dummyDataSource: DummyDataSource, -) : DummyRepository { - - override suspend fun fetchDummy(): Result = safeApiCall { - dummyDataSource.fetchDummy().data?.toDomain() - ?: return Result.failure(ApiError("No data found")) - } - - override suspend fun saveDummy(dummy: Dummy): Result = safeApiCall { - dummyDataSource.saveDummy(dummy.toData()).data - ?: return Result.failure(ApiError("Save operation failed")) - } -} diff --git a/data/src/main/java/com/yapp/data/remote/repositoryimpl/FortuneRepositoryImpl.kt b/data/src/main/java/com/yapp/data/remote/repositoryimpl/FortuneRepositoryImpl.kt deleted file mode 100644 index b41e225a..00000000 --- a/data/src/main/java/com/yapp/data/remote/repositoryimpl/FortuneRepositoryImpl.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.yapp.data.remote.repositoryimpl - -import com.yapp.data.remote.datasource.FortuneDataSource -import com.yapp.data.remote.dto.response.toDomain -import com.yapp.domain.model.fortune.Fortune -import com.yapp.domain.repository.FortuneRepository -import javax.inject.Inject - -class FortuneRepositoryImpl @Inject constructor( - private val fortuneDataSource: FortuneDataSource, -) : FortuneRepository { - override suspend fun postFortune(userId: Long): Result { - return fortuneDataSource.postFortune(userId) - .mapCatching { fortuneResponse -> - fortuneResponse.toDomain() - } - } - override suspend fun getFortune(fortuneId: Long): Result { - return fortuneDataSource.getFortune(fortuneId) - .mapCatching { fortuneResponse -> - fortuneResponse.toDomain() - } - } -} diff --git a/data/src/main/java/com/yapp/data/remote/service/DummyService.kt b/data/src/main/java/com/yapp/data/remote/service/DummyService.kt deleted file mode 100644 index 066c49f3..00000000 --- a/data/src/main/java/com/yapp/data/remote/service/DummyService.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.yapp.data.remote.service - -import com.yapp.data.remote.dto.request.RequestDummyDto -import com.yapp.data.remote.dto.response.ResponseDummyDto -import com.yapp.network.model.BaseResponse - -interface DummyService { - suspend fun fetchDummy(): BaseResponse - suspend fun saveDummy(requestDummyDto: RequestDummyDto): BaseResponse -} diff --git a/data/src/main/java/com/yapp/data/local/repositoryimpl/AlarmRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt similarity index 89% rename from data/src/main/java/com/yapp/data/local/repositoryimpl/AlarmRepositoryImpl.kt rename to data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt index 6f2a3109..07efaa22 100644 --- a/data/src/main/java/com/yapp/data/local/repositoryimpl/AlarmRepositoryImpl.kt +++ b/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt @@ -1,4 +1,4 @@ -package com.yapp.data.local.repositoryimpl +package com.yapp.data.repositoryimpl import android.net.Uri import com.yapp.data.local.datasource.AlarmLocalDataSource @@ -16,6 +16,8 @@ class AlarmRepositoryImpl @Inject constructor( private val ringtoneManagerHelper: RingtoneManagerHelper, private val soundPlayer: SoundPlayer, ) : AlarmRepository { + override val firstDismissedAlarmIdFlow: Flow = alarmLocalDataSource.firstDismissedAlarmIdFlow + override suspend fun getAlarmSounds(): Result> = runCatching { ringtoneManagerHelper.getAlarmSounds().map { (title, uri) -> AlarmSound(title, uri) @@ -97,4 +99,12 @@ class AlarmRepositoryImpl @Inject constructor( throw Exception("No rows deleted") } } + + override suspend fun saveFirstDismissedAlarmId(alarmId: Long) { + alarmLocalDataSource.saveFirstDismissedAlarmId(alarmId) + } + + override suspend fun clearDismissedAlarmId() { + alarmLocalDataSource.clearDismissedAlarmId() + } } diff --git a/data/src/main/java/com/yapp/data/repositoryimpl/FortuneRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/FortuneRepositoryImpl.kt new file mode 100644 index 00000000..31521351 --- /dev/null +++ b/data/src/main/java/com/yapp/data/repositoryimpl/FortuneRepositoryImpl.kt @@ -0,0 +1,42 @@ +package com.yapp.data.repositoryimpl + +import com.yapp.data.local.datasource.FortuneLocalDataSource +import com.yapp.data.remote.datasource.FortuneDataSource +import com.yapp.data.remote.dto.response.toDomain +import com.yapp.domain.model.Fortune +import com.yapp.domain.repository.FortuneRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +class FortuneRepositoryImpl @Inject constructor( + private val fortuneLocalDataSource: FortuneLocalDataSource, + private val fortuneRemoteDataSource: FortuneDataSource, +) : FortuneRepository { + override val fortuneIdFlow: Flow = fortuneLocalDataSource.fortuneIdFlow + override val fortuneDateFlow: Flow = fortuneLocalDataSource.fortuneDateFlow + override val fortuneImageIdFlow: Flow = fortuneLocalDataSource.fortuneImageIdFlow + override val fortuneScoreFlow: Flow = fortuneLocalDataSource.fortuneScoreFlow + override val hasNewFortuneFlow: Flow = fortuneLocalDataSource.hasNewFortuneFlow + override val firstDismissedAlarmIdFlow: Flow = fortuneLocalDataSource.firstDismissedAlarmIdFlow + + override suspend fun saveFortuneId(fortuneId: Long) = fortuneLocalDataSource.saveFortuneId(fortuneId) + override suspend fun markFortuneAsChecked() = fortuneLocalDataSource.markFortuneAsChecked() + override suspend fun saveFortuneImageId(imageResId: Int) = fortuneLocalDataSource.saveFortuneImageId(imageResId) + override suspend fun saveFortuneScore(score: Int) = fortuneLocalDataSource.saveFortuneScore(score) + override suspend fun saveFirstDismissedAlarmId(alarmId: Long) = fortuneLocalDataSource.saveFirstDismissedAlarmId(alarmId) + override suspend fun clearDismissedAlarmId() = fortuneLocalDataSource.clearDismissedAlarmId() + override suspend fun clearFortuneId() = fortuneLocalDataSource.clearFortuneId() + + override suspend fun postFortune(userId: Long): Result { + return fortuneRemoteDataSource.postFortune(userId) + .mapCatching { fortuneResponse -> + fortuneResponse.toDomain() + } + } + override suspend fun getFortune(fortuneId: Long): Result { + return fortuneRemoteDataSource.getFortune(fortuneId) + .mapCatching { fortuneResponse -> + fortuneResponse.toDomain() + } + } +} diff --git a/data/src/main/java/com/yapp/data/local/repositoryimpl/ImageRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/ImageRepositoryImpl.kt similarity index 90% rename from data/src/main/java/com/yapp/data/local/repositoryimpl/ImageRepositoryImpl.kt rename to data/src/main/java/com/yapp/data/repositoryimpl/ImageRepositoryImpl.kt index 86cba2cc..09658171 100644 --- a/data/src/main/java/com/yapp/data/local/repositoryimpl/ImageRepositoryImpl.kt +++ b/data/src/main/java/com/yapp/data/repositoryimpl/ImageRepositoryImpl.kt @@ -1,4 +1,4 @@ -package com.yapp.data.local.repositoryimpl +package com.yapp.data.repositoryimpl import com.yapp.data.local.datasource.ImageLocalDataSource import com.yapp.domain.repository.ImageRepository diff --git a/data/src/main/java/com/yapp/data/remote/repositoryimpl/RemoteConfigRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/RemoteConfigRepositoryImpl.kt similarity index 92% rename from data/src/main/java/com/yapp/data/remote/repositoryimpl/RemoteConfigRepositoryImpl.kt rename to data/src/main/java/com/yapp/data/repositoryimpl/RemoteConfigRepositoryImpl.kt index e45ae5a1..46a14431 100644 --- a/data/src/main/java/com/yapp/data/remote/repositoryimpl/RemoteConfigRepositoryImpl.kt +++ b/data/src/main/java/com/yapp/data/repositoryimpl/RemoteConfigRepositoryImpl.kt @@ -1,4 +1,4 @@ -package com.yapp.data.remote.repositoryimpl +package com.yapp.data.repositoryimpl import com.yapp.domain.model.MissionType import com.yapp.domain.repository.RemoteConfigRepository diff --git a/data/src/main/java/com/yapp/data/remote/repositoryimpl/SignUpRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/SignUpRepositoryImpl.kt similarity index 94% rename from data/src/main/java/com/yapp/data/remote/repositoryimpl/SignUpRepositoryImpl.kt rename to data/src/main/java/com/yapp/data/repositoryimpl/SignUpRepositoryImpl.kt index 5977c5ae..2c593a9c 100644 --- a/data/src/main/java/com/yapp/data/remote/repositoryimpl/SignUpRepositoryImpl.kt +++ b/data/src/main/java/com/yapp/data/repositoryimpl/SignUpRepositoryImpl.kt @@ -1,4 +1,4 @@ -package com.yapp.data.remote.repositoryimpl +package com.yapp.data.repositoryimpl import android.util.Log import com.yapp.data.remote.datasource.SignUpDataSource diff --git a/data/src/main/java/com/yapp/data/remote/repositoryimpl/UserInfoRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/UserInfoRepositoryImpl.kt similarity index 57% rename from data/src/main/java/com/yapp/data/remote/repositoryimpl/UserInfoRepositoryImpl.kt rename to data/src/main/java/com/yapp/data/repositoryimpl/UserInfoRepositoryImpl.kt index c4720bb6..d96ca6be 100644 --- a/data/src/main/java/com/yapp/data/remote/repositoryimpl/UserInfoRepositoryImpl.kt +++ b/data/src/main/java/com/yapp/data/repositoryimpl/UserInfoRepositoryImpl.kt @@ -1,16 +1,28 @@ -package com.yapp.data.remote.repositoryimpl +package com.yapp.data.repositoryimpl +import com.yapp.data.local.datasource.UserLocalDataSource import com.yapp.data.remote.datasource.UserInfoDataSource import com.yapp.data.remote.dto.request.UpdateUserInfoRequest.Companion.toUpdateRequest import com.yapp.data.remote.dto.response.toDomain import com.yapp.domain.model.EditUser import com.yapp.domain.model.User import com.yapp.domain.repository.UserInfoRepository +import kotlinx.coroutines.flow.Flow import javax.inject.Inject class UserInfoRepositoryImpl @Inject constructor( + private val userLocalDataSource: UserLocalDataSource, private val userInfoDataSource: UserInfoDataSource, ) : UserInfoRepository { + override val userIdFlow: Flow = userLocalDataSource.userIdFlow + override val userNameFlow: Flow = userLocalDataSource.userNameFlow + override val onboardingCompletedFlow: Flow = userLocalDataSource.onboardingCompletedFlow + + override suspend fun saveUserId(userId: Long) = userLocalDataSource.saveUserId(userId) + override suspend fun saveUserName(userName: String) = userLocalDataSource.saveUserName(userName) + override suspend fun setOnboardingCompleted() = userLocalDataSource.setOnboardingCompleted() + override suspend fun clearUserData() = userLocalDataSource.clearUserData() + override suspend fun getUserInfo(userId: Long): Result { return userInfoDataSource.getUserInfo(userId) .mapCatching { userResponse -> diff --git a/domain/src/main/java/com/yapp/domain/model/Dummy.kt b/domain/src/main/java/com/yapp/domain/model/Dummy.kt deleted file mode 100644 index 9a450d6f..00000000 --- a/domain/src/main/java/com/yapp/domain/model/Dummy.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.yapp.domain.model - -data class Dummy( - val id: Int, - val name: String, -) diff --git a/domain/src/main/java/com/yapp/domain/model/fortune/Fortune.kt b/domain/src/main/java/com/yapp/domain/model/Fortune.kt similarity index 94% rename from domain/src/main/java/com/yapp/domain/model/fortune/Fortune.kt rename to domain/src/main/java/com/yapp/domain/model/Fortune.kt index 0a15638f..0af841cc 100644 --- a/domain/src/main/java/com/yapp/domain/model/fortune/Fortune.kt +++ b/domain/src/main/java/com/yapp/domain/model/Fortune.kt @@ -1,4 +1,4 @@ -package com.yapp.domain.model.fortune +package com.yapp.domain.model data class Fortune( val id: Long, 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 d3a7ae83..142c1b83 100644 --- a/domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt +++ b/domain/src/main/java/com/yapp/domain/repository/AlarmRepository.kt @@ -6,6 +6,8 @@ import com.yapp.domain.model.AlarmSound import kotlinx.coroutines.flow.Flow interface AlarmRepository { + val firstDismissedAlarmIdFlow: Flow + suspend fun getAlarmSounds(): Result> fun initializeSoundPlayer(uri: Uri) fun playAlarmSound(volume: Int) @@ -21,4 +23,6 @@ interface AlarmRepository { suspend fun updateAlarmActive(id: Long, active: Boolean): Result suspend fun getAlarm(id: Long): Result suspend fun deleteAlarm(id: Long): Result + suspend fun saveFirstDismissedAlarmId(alarmId: Long) + suspend fun clearDismissedAlarmId() } diff --git a/domain/src/main/java/com/yapp/domain/repository/DummyRepository.kt b/domain/src/main/java/com/yapp/domain/repository/DummyRepository.kt deleted file mode 100644 index c64fe8b0..00000000 --- a/domain/src/main/java/com/yapp/domain/repository/DummyRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.yapp.domain.repository - -import com.yapp.domain.model.Dummy - -interface DummyRepository { - suspend fun fetchDummy(): Result - suspend fun saveDummy(dummy: Dummy): Result -} diff --git a/domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt b/domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt index efbabfc1..23598e3c 100644 --- a/domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt +++ b/domain/src/main/java/com/yapp/domain/repository/FortuneRepository.kt @@ -1,8 +1,23 @@ package com.yapp.domain.repository -import com.yapp.domain.model.fortune.Fortune +import com.yapp.domain.model.Fortune +import kotlinx.coroutines.flow.Flow interface FortuneRepository { + val fortuneIdFlow: Flow + val fortuneDateFlow: Flow + val fortuneImageIdFlow: Flow + val fortuneScoreFlow: Flow + val hasNewFortuneFlow: Flow + val firstDismissedAlarmIdFlow: Flow + + suspend fun saveFortuneId(fortuneId: Long) + suspend fun markFortuneAsChecked() + suspend fun saveFortuneImageId(imageResId: Int) + suspend fun saveFortuneScore(score: Int) + suspend fun saveFirstDismissedAlarmId(alarmId: Long) + suspend fun clearDismissedAlarmId() + suspend fun clearFortuneId() suspend fun postFortune(userId: Long): Result suspend fun getFortune(fortuneId: Long): Result } diff --git a/domain/src/main/java/com/yapp/domain/repository/UserInfoRepository.kt b/domain/src/main/java/com/yapp/domain/repository/UserInfoRepository.kt index 34a6580a..bda28291 100644 --- a/domain/src/main/java/com/yapp/domain/repository/UserInfoRepository.kt +++ b/domain/src/main/java/com/yapp/domain/repository/UserInfoRepository.kt @@ -2,8 +2,18 @@ package com.yapp.domain.repository import com.yapp.domain.model.EditUser import com.yapp.domain.model.User +import kotlinx.coroutines.flow.Flow interface UserInfoRepository { + val userIdFlow: Flow + val userNameFlow: Flow + val onboardingCompletedFlow: Flow + + suspend fun saveUserId(userId: Long) + suspend fun saveUserName(userName: String) + suspend fun setOnboardingCompleted() + suspend fun clearUserData() + suspend fun getUserInfo(userId: Long): Result suspend fun updateUserInfo(userId: Long, editUser: EditUser): Result } diff --git a/domain/src/main/java/com/yapp/domain/scheduler/AlarmScheduler.kt b/domain/src/main/java/com/yapp/domain/scheduler/AlarmScheduler.kt new file mode 100644 index 00000000..1656ac30 --- /dev/null +++ b/domain/src/main/java/com/yapp/domain/scheduler/AlarmScheduler.kt @@ -0,0 +1,8 @@ +package com.yapp.domain.scheduler + +import com.yapp.domain.model.Alarm + +interface AlarmScheduler { + fun scheduleAlarm(alarm: Alarm) + fun unScheduleAlarm(alarm: Alarm) +} diff --git a/domain/src/main/java/com/yapp/domain/usecase/DummyUseCase.kt b/domain/src/main/java/com/yapp/domain/usecase/DummyUseCase.kt deleted file mode 100644 index a3584da6..00000000 --- a/domain/src/main/java/com/yapp/domain/usecase/DummyUseCase.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.yapp.domain.usecase - -import com.yapp.domain.model.Dummy -import com.yapp.domain.repository.DummyRepository -import javax.inject.Inject - -class DummyUseCase @Inject constructor( - private val dummyRepository: DummyRepository, -) { - suspend fun fetch(): Result = dummyRepository.fetchDummy() - suspend fun save(dummy: Dummy): Result = dummyRepository.saveDummy(dummy) -} diff --git a/feature/alarm-interaction/build.gradle.kts b/feature/alarm-interaction/build.gradle.kts index efc6eeec..22e53709 100644 --- a/feature/alarm-interaction/build.gradle.kts +++ b/feature/alarm-interaction/build.gradle.kts @@ -15,7 +15,6 @@ dependencies { implementation(projects.core.alarm) implementation(projects.core.media) implementation(projects.domain) - implementation(projects.core.datastore) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) diff --git a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionViewModel.kt b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionViewModel.kt index 57e6cd8c..d1d4c011 100644 --- a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionViewModel.kt +++ b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/action/AlarmActionViewModel.kt @@ -5,8 +5,8 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.yapp.alarm.pendingIntent.interaction.createAlarmDismissIntent import com.yapp.alarm.pendingIntent.interaction.createAlarmSnoozeIntent -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.Alarm +import com.yapp.domain.repository.FortuneRepository import com.yapp.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay @@ -23,7 +23,7 @@ import javax.inject.Inject @HiltViewModel class AlarmActionViewModel @Inject constructor( private val app: Application, - private val userPreferences: UserPreferences, + private val fortuneRepository: FortuneRepository, savedStateHandle: SavedStateHandle, ) : BaseViewModel( AlarmActionContract.State(), @@ -46,7 +46,7 @@ class AlarmActionViewModel @Inject constructor( private fun fetchIsFirstMission() { viewModelScope.launch { - val fortuneDate = userPreferences.fortuneDateFlow.firstOrNull() + val fortuneDate = fortuneRepository.fortuneDateFlow.firstOrNull() val todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) val isFirstMission = fortuneDate != todayDate diff --git a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/snooze/AlarmSnoozeTimerViewModel.kt b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/snooze/AlarmSnoozeTimerViewModel.kt index 6076cf8c..e8bb4277 100644 --- a/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/snooze/AlarmSnoozeTimerViewModel.kt +++ b/feature/alarm-interaction/src/main/java/com/yapp/alarm/interaction/snooze/AlarmSnoozeTimerViewModel.kt @@ -4,8 +4,8 @@ import android.app.Application import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.yapp.alarm.pendingIntent.interaction.createAlarmDismissIntent -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.Alarm +import com.yapp.domain.repository.FortuneRepository import com.yapp.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay @@ -23,7 +23,7 @@ import kotlin.math.max class AlarmSnoozeTimerViewModel @Inject constructor( private val app: Application, savedStateHandle: SavedStateHandle, - private val userPreferences: UserPreferences, + private val fortuneRepository: FortuneRepository, ) : BaseViewModel( AlarmSnoozeTimerContract.State(), ) { @@ -37,7 +37,7 @@ class AlarmSnoozeTimerViewModel @Inject constructor( private fun fetchIsFirstMission() { viewModelScope.launch { - val fortuneDate = userPreferences.fortuneDateFlow.firstOrNull() + val fortuneDate = fortuneRepository.fortuneDateFlow.firstOrNull() val todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) val isFirstMission = fortuneDate != todayDate diff --git a/feature/fortune/build.gradle.kts b/feature/fortune/build.gradle.kts index ac291d36..ae450155 100644 --- a/feature/fortune/build.gradle.kts +++ b/feature/fortune/build.gradle.kts @@ -12,7 +12,6 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.common) implementation(projects.core.analytics) - implementation(projects.core.datastore) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) diff --git a/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt b/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt index b4890bfb..b0774c04 100644 --- a/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt +++ b/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import android.util.Log import androidx.annotation.DrawableRes import androidx.lifecycle.viewModelScope -import com.yapp.datastore.UserPreferences import com.yapp.domain.repository.FortuneRepository import com.yapp.domain.repository.ImageRepository import com.yapp.fortune.page.toFortunePages @@ -25,16 +24,15 @@ class FortuneViewModel @Inject constructor( private val application: Application, private val fortuneRepository: FortuneRepository, private val imageRepository: ImageRepository, - private val userPreferences: UserPreferences, ) : BaseViewModel( FortuneContract.State(), ) { init { viewModelScope.launch { - val fortuneId = userPreferences.fortuneIdFlow.firstOrNull() - val firstDismissedAlarmId = userPreferences.firstDismissedAlarmIdFlow.firstOrNull() - val fortuneDate = userPreferences.fortuneDateFlow.firstOrNull() + val fortuneId = fortuneRepository.fortuneIdFlow.firstOrNull() + val firstDismissedAlarmId = fortuneRepository.firstDismissedAlarmIdFlow.firstOrNull() + val fortuneDate = fortuneRepository.fortuneDateFlow.firstOrNull() fortuneId?.let { getFortune(it, firstDismissedAlarmId, fortuneDate) } } } @@ -42,7 +40,7 @@ class FortuneViewModel @Inject constructor( updateState { copy(isLoading = true) } fortuneRepository.getFortune(fortuneId).onSuccess { fortune -> - val savedImageId = userPreferences.fortuneImageIdFlow.firstOrNull() + val savedImageId = fortuneRepository.fortuneImageIdFlow.firstOrNull() val imageId = savedImageId ?: getRandomImage() val formattedTitle = fortune.dailyFortuneTitle.replace(",", ",\n").trim() @@ -66,9 +64,9 @@ class FortuneViewModel @Inject constructor( } fun saveFortuneImageIdIfNeeded(imageId: Int) = viewModelScope.launch { - val savedImageId = userPreferences.fortuneImageIdFlow.firstOrNull() + val savedImageId = fortuneRepository.fortuneImageIdFlow.firstOrNull() if (savedImageId == null || savedImageId != imageId) { - userPreferences.saveFortuneImageId(imageId) + fortuneRepository.saveFortuneImageId(imageId) } } diff --git a/feature/fortune/src/main/java/com/yapp/fortune/page/FortunePageData.kt b/feature/fortune/src/main/java/com/yapp/fortune/page/FortunePageData.kt index e7070276..4ae64ca9 100644 --- a/feature/fortune/src/main/java/com/yapp/fortune/page/FortunePageData.kt +++ b/feature/fortune/src/main/java/com/yapp/fortune/page/FortunePageData.kt @@ -1,6 +1,6 @@ package com.yapp.fortune.page -import com.yapp.domain.model.fortune.Fortune +import com.yapp.domain.model.Fortune data class FortunePageData( val title: String, diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index b8a4b12d..9ef0f667 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -12,9 +12,7 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.common) implementation(projects.core.analytics) - implementation(projects.core.alarm) implementation(projects.core.media) - implementation(projects.core.datastore) implementation(projects.domain) implementation(libs.orbit.core) implementation(libs.orbit.compose) diff --git a/feature/home/src/main/java/com/yapp/alarm/addedit/AlarmAddEditViewModel.kt b/feature/home/src/main/java/com/yapp/alarm/addedit/AlarmAddEditViewModel.kt index 08713ebd..3634ff21 100644 --- a/feature/home/src/main/java/com/yapp/alarm/addedit/AlarmAddEditViewModel.kt +++ b/feature/home/src/main/java/com/yapp/alarm/addedit/AlarmAddEditViewModel.kt @@ -4,7 +4,6 @@ import android.util.Log import androidx.compose.ui.unit.dp import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope -import com.yapp.alarm.AlarmHelper import com.yapp.analytics.AnalyticsEvent import com.yapp.analytics.AnalyticsHelper import com.yapp.common.util.ResourceProvider @@ -15,6 +14,7 @@ import com.yapp.domain.model.copyFrom import com.yapp.domain.model.toAlarmDayNames import com.yapp.domain.model.toAlarmDays import com.yapp.domain.model.toDayOfWeek +import com.yapp.domain.scheduler.AlarmScheduler import com.yapp.domain.usecase.AlarmUseCase import com.yapp.media.haptic.HapticFeedbackManager import com.yapp.media.haptic.HapticType @@ -32,7 +32,7 @@ class AlarmAddEditViewModel @Inject constructor( private val alarmUseCase: AlarmUseCase, private val resourceProvider: ResourceProvider, private val hapticFeedbackManager: HapticFeedbackManager, - private val alarmHelper: AlarmHelper, + private val alarmScheduler: AlarmScheduler, savedStateHandle: SavedStateHandle, ) : BaseViewModel( initialState = AlarmAddEditContract.State(), @@ -209,12 +209,12 @@ class AlarmAddEditViewModel @Inject constructor( val updatedAlarm = alarm.copy(id = alarmId) alarmUseCase.getAlarm(alarmId).onSuccess { oldAlarm -> - alarmHelper.unScheduleAlarm(oldAlarm) + alarmScheduler.unScheduleAlarm(oldAlarm) } alarmUseCase.updateAlarm(updatedAlarm) .onSuccess { - alarmHelper.scheduleAlarm(updatedAlarm) + alarmScheduler.scheduleAlarm(updatedAlarm) emitSideEffect(AlarmAddEditContract.SideEffect.UpdateAlarm(it.id)) } .onFailure { @@ -268,7 +268,7 @@ class AlarmAddEditViewModel @Inject constructor( ), ), ) - alarmHelper.scheduleAlarm(it) + alarmScheduler.scheduleAlarm(it) emitSideEffect(AlarmAddEditContract.SideEffect.SaveAlarm(it.id)) } .onFailure { 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 9c5dccc6..df6b4558 100644 --- a/feature/home/src/main/java/com/yapp/home/HomeViewModel.kt +++ b/feature/home/src/main/java/com/yapp/home/HomeViewModel.kt @@ -2,12 +2,13 @@ package com.yapp.home import android.util.Log import androidx.lifecycle.viewModelScope -import com.yapp.alarm.AlarmHelper import com.yapp.common.util.ResourceProvider -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.Alarm import com.yapp.domain.model.toAlarmDays import com.yapp.domain.model.toDayOfWeek +import com.yapp.domain.repository.FortuneRepository +import com.yapp.domain.repository.UserInfoRepository +import com.yapp.domain.scheduler.AlarmScheduler import com.yapp.domain.usecase.AlarmUseCase import com.yapp.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -25,8 +26,9 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( private val alarmUseCase: AlarmUseCase, private val resourceProvider: ResourceProvider, - private val alarmHelper: AlarmHelper, - private val userPreferences: UserPreferences, + private val alarmScheduler: AlarmScheduler, + private val fortuneRepository: FortuneRepository, + private val userInfoRepository: UserInfoRepository, ) : BaseViewModel( initialState = HomeContract.State(), ) { @@ -175,9 +177,9 @@ class HomeViewModel @Inject constructor( } if (updatedAlarm.isAlarmActive) { - alarmHelper.scheduleAlarm(updatedAlarm) + alarmScheduler.scheduleAlarm(updatedAlarm) } else { - alarmHelper.unScheduleAlarm(updatedAlarm) + alarmScheduler.unScheduleAlarm(updatedAlarm) } }.onFailure { error -> Log.e("HomeViewModel", "Failed to update alarm state", error) @@ -239,9 +241,9 @@ class HomeViewModel @Inject constructor( } if (updatedAlarm.isAlarmActive) { - alarmHelper.scheduleAlarm(updatedAlarm) + alarmScheduler.scheduleAlarm(updatedAlarm) } else { - alarmHelper.unScheduleAlarm(updatedAlarm) + alarmScheduler.unScheduleAlarm(updatedAlarm) } }.onFailure { error -> Log.e("HomeViewModel", "Failed to rollback alarm state", error) @@ -262,7 +264,7 @@ class HomeViewModel @Inject constructor( viewModelScope.launch { alarmsToDelete.forEach { alarm -> alarmUseCase.deleteAlarm(alarm.id) - alarmHelper.unScheduleAlarm(alarm) + alarmScheduler.unScheduleAlarm(alarm) } } @@ -287,7 +289,7 @@ class HomeViewModel @Inject constructor( viewModelScope.launch { alarmsWithIndex.forEach { alarm -> alarmUseCase.insertAlarm(alarm) - alarmHelper.scheduleAlarm(alarm) + alarmScheduler.scheduleAlarm(alarm) } } } @@ -392,7 +394,7 @@ class HomeViewModel @Inject constructor( private fun loadDailyFortune() { viewModelScope.launch { - val fortuneDate = userPreferences.fortuneDateFlow.firstOrNull() + val fortuneDate = fortuneRepository.fortuneDateFlow.firstOrNull() val todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) Log.d("HomeViewModel", "fortuneDate: $fortuneDate, todayDate: $todayDate") @@ -400,7 +402,7 @@ class HomeViewModel @Inject constructor( if (fortuneDate != todayDate) { processAction(HomeContract.Action.ShowNoDailyFortuneDialog) } else { - userPreferences.markFortuneAsChecked() + fortuneRepository.markFortuneAsChecked() emitSideEffect(HomeContract.SideEffect.NavigateToFortune) } } @@ -411,9 +413,9 @@ class HomeViewModel @Inject constructor( val todayDate = LocalDate.now().format(DateTimeFormatter.ISO_DATE) combine( - userPreferences.fortuneDateFlow, - userPreferences.fortuneScoreFlow, - userPreferences.hasNewFortuneFlow, + fortuneRepository.fortuneDateFlow, + fortuneRepository.fortuneScoreFlow, + fortuneRepository.hasNewFortuneFlow, ) { fortuneDate, fortuneScore, hasNewFortune -> val isTodayFortuneAvailable = fortuneDate == todayDate val finalFortuneScore = if (isTodayFortuneAvailable) fortuneScore ?: -1 else -1 @@ -433,7 +435,7 @@ class HomeViewModel @Inject constructor( private fun loadUserName() { viewModelScope.launch { - userPreferences.userNameFlow.collect { userName -> + userInfoRepository.userNameFlow.collect { userName -> updateState { copy(name = userName ?: "") } } } diff --git a/feature/mission/build.gradle.kts b/feature/mission/build.gradle.kts index 95ffd72c..eda23f95 100644 --- a/feature/mission/build.gradle.kts +++ b/feature/mission/build.gradle.kts @@ -15,7 +15,6 @@ dependencies { implementation(projects.core.media) implementation(projects.core.alarm) implementation(projects.domain) - implementation(projects.core.datastore) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) 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 38512436..c99b8f79 100644 --- a/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt +++ b/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt @@ -7,9 +7,9 @@ import androidx.lifecycle.viewModelScope import com.yapp.alarm.pendingIntent.interaction.createAlarmDismissIntent import com.yapp.analytics.AnalyticsEvent import com.yapp.analytics.AnalyticsHelper -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.MissionType import com.yapp.domain.repository.FortuneRepository +import com.yapp.domain.repository.UserInfoRepository import com.yapp.domain.usecase.GetMissionTypeUseCase import com.yapp.media.haptic.HapticFeedbackManager import com.yapp.media.haptic.HapticType @@ -27,7 +27,7 @@ class MissionViewModel @Inject constructor( private val analyticsHelper: AnalyticsHelper, private val hapticFeedbackManager: HapticFeedbackManager, private val fortuneRepository: FortuneRepository, - private val userPreferences: UserPreferences, + private val userInfoRepository: UserInfoRepository, private val getMissionTypeUseCase: GetMissionTypeUseCase, private val app: Application, savedStateHandle: SavedStateHandle, @@ -116,7 +116,7 @@ class MissionViewModel @Inject constructor( private fun postFortune() { viewModelScope.launch { - val userId = userPreferences.userIdFlow.firstOrNull() ?: return@launch + val userId = userInfoRepository.userIdFlow.firstOrNull() ?: return@launch val result = runCatching { withContext(Dispatchers.IO) { fortuneRepository.postFortune(userId) @@ -125,8 +125,8 @@ class MissionViewModel @Inject constructor( result.onSuccess { val data = it.getOrThrow() - userPreferences.saveFortuneId(data.id) - userPreferences.saveFortuneScore(data.avgFortuneScore) + fortuneRepository.saveFortuneId(data.id) + fortuneRepository.saveFortuneScore(data.avgFortuneScore) emitSideEffect(MissionContract.SideEffect.NavigateToFortune) }.onFailure { error -> @@ -138,7 +138,7 @@ class MissionViewModel @Inject constructor( private fun retryPostFortune() { viewModelScope.launch { - val userId = userPreferences.userIdFlow.firstOrNull() ?: return@launch + val userId = userInfoRepository.userIdFlow.firstOrNull() ?: return@launch val result = runCatching { withContext(Dispatchers.IO) { fortuneRepository.postFortune(userId) @@ -147,8 +147,8 @@ class MissionViewModel @Inject constructor( result.onSuccess { val data = it.getOrThrow() - userPreferences.saveFortuneId(data.id) - userPreferences.saveFortuneScore(data.avgFortuneScore) + fortuneRepository.saveFortuneId(data.id) + fortuneRepository.saveFortuneScore(data.avgFortuneScore) emitSideEffect(MissionContract.SideEffect.NavigateToFortune) }.onFailure { diff --git a/feature/navigator/.gitignore b/feature/navigator/.gitignore deleted file mode 100644 index 42afabfd..00000000 --- a/feature/navigator/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/navigator/build.gradle.kts b/feature/navigator/build.gradle.kts deleted file mode 100644 index a8db6273..00000000 --- a/feature/navigator/build.gradle.kts +++ /dev/null @@ -1,26 +0,0 @@ -import com.yapp.convention.setNamespace - -plugins { - id("orbit.android.feature") -} - -android { - setNamespace("feature.navigator") -} - -dependencies { - implementation(projects.core.common) - implementation(projects.core.analytics) - implementation(libs.orbit.core) - implementation(libs.orbit.compose) - implementation(libs.orbit.viewmodel) - implementation(libs.kotlin.reflect) - implementation(projects.feature.home) - implementation(projects.feature.alarmInteraction) - implementation(projects.feature.onboarding) - implementation(projects.feature.mission) - implementation(projects.feature.fortune) - implementation(projects.feature.setting) - implementation(projects.feature.splash) - implementation(projects.feature.webview) -} diff --git a/feature/navigator/consumer-rules.pro b/feature/navigator/consumer-rules.pro deleted file mode 100644 index e69de29b..00000000 diff --git a/feature/navigator/proguard-rules.pro b/feature/navigator/proguard-rules.pro deleted file mode 100644 index 481bb434..00000000 --- a/feature/navigator/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/navigator/src/main/AndroidManifest.xml b/feature/navigator/src/main/AndroidManifest.xml deleted file mode 100644 index 76073216..00000000 --- a/feature/navigator/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/feature/onboarding/build.gradle.kts b/feature/onboarding/build.gradle.kts index 6c1d22e0..8a44d192 100644 --- a/feature/onboarding/build.gradle.kts +++ b/feature/onboarding/build.gradle.kts @@ -14,7 +14,6 @@ dependencies { implementation(projects.core.analytics) implementation(projects.core.media) implementation(projects.domain) - implementation(projects.core.datastore) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) 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 ec145349..d92bec81 100644 --- a/feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt +++ b/feature/onboarding/src/main/java/com/yapp/onboarding/OnboardingViewModel.kt @@ -6,11 +6,11 @@ import androidx.lifecycle.viewModelScope import com.yapp.analytics.AnalyticsEvent import com.yapp.analytics.AnalyticsHelper import com.yapp.common.navigation.route.OnboardingDestination -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.Alarm import com.yapp.domain.model.AlarmDay import com.yapp.domain.model.toRepeatDays 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 @@ -24,7 +24,7 @@ import kotlin.reflect.KClass class OnboardingViewModel @Inject constructor( private val analyticsHelper: AnalyticsHelper, private val signUpRepository: SignUpRepository, - private val userPreferences: UserPreferences, + private val userInfoRepository: UserInfoRepository, private val alarmUseCase: AlarmUseCase, private val hapticFeedbackManager: HapticFeedbackManager, private val savedStateHandle: SavedStateHandle, @@ -72,8 +72,8 @@ class OnboardingViewModel @Inject constructor( if (result.isSuccess) { val userId = result.getOrNull() ?: return@launch val userName = state.userName - userPreferences.saveUserId(userId) - userPreferences.saveUserName(userName) + userInfoRepository.saveUserId(userId) + userInfoRepository.saveUserName(userName) analyticsHelper.setUserId(userId) analyticsHelper.logEvent( @@ -240,7 +240,7 @@ class OnboardingViewModel @Inject constructor( private fun completeOnboarding() { viewModelScope.launch { - userPreferences.setOnboardingCompleted() + userInfoRepository.setOnboardingCompleted() emitSideEffect(OnboardingContract.SideEffect.OnboardingCompleted) } } diff --git a/feature/setting/build.gradle.kts b/feature/setting/build.gradle.kts index 96e74f94..5ea1d850 100644 --- a/feature/setting/build.gradle.kts +++ b/feature/setting/build.gradle.kts @@ -13,7 +13,6 @@ dependencies { implementation(projects.core.common) implementation(projects.core.analytics) implementation(projects.domain) - implementation(projects.core.datastore) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) diff --git a/feature/setting/src/main/java/com/yapp/setting/EditProfileViewModel.kt b/feature/setting/src/main/java/com/yapp/setting/EditProfileViewModel.kt index 166388b3..5427db4f 100644 --- a/feature/setting/src/main/java/com/yapp/setting/EditProfileViewModel.kt +++ b/feature/setting/src/main/java/com/yapp/setting/EditProfileViewModel.kt @@ -2,7 +2,6 @@ package com.yapp.setting import android.util.Log import androidx.lifecycle.viewModelScope -import com.yapp.datastore.UserPreferences import com.yapp.domain.model.EditUser import com.yapp.domain.repository.UserInfoRepository import com.yapp.ui.base.BaseViewModel @@ -15,7 +14,6 @@ import javax.inject.Inject @HiltViewModel class EditProfileViewModel @Inject constructor( private val userInfoRepository: UserInfoRepository, - private val userPreferences: UserPreferences, ) : BaseViewModel( SettingContract.State(), ) { @@ -134,7 +132,7 @@ class EditProfileViewModel @Inject constructor( } private fun submitUserInfo() = viewModelScope.launch { - val userId = userPreferences.userIdFlow.firstOrNull() ?: return@launch + val userId = userInfoRepository.userIdFlow.firstOrNull() ?: return@launch val state = container.stateFlow.value val updatedUser = EditUser( @@ -148,7 +146,7 @@ class EditProfileViewModel @Inject constructor( val result = userInfoRepository.updateUserInfo(userId, updatedUser) if (result.isSuccess) { - userPreferences.saveUserName(state.name) + userInfoRepository.saveUserName(state.name) emitSideEffect(SettingContract.SideEffect.NavigateToSettingRoute) } else { Log.e("EditProfileViewModel", "사용자 정보 수정 실패") @@ -166,7 +164,7 @@ class EditProfileViewModel @Inject constructor( private fun refreshUserInfo() { viewModelScope.launch { - val userId = userPreferences.userIdFlow.firstOrNull() + val userId = userInfoRepository.userIdFlow.firstOrNull() if (userId != null) { fetchUserInfo(userId) } diff --git a/feature/setting/src/main/java/com/yapp/setting/SettingViewModel.kt b/feature/setting/src/main/java/com/yapp/setting/SettingViewModel.kt index 2e0773c0..132f2047 100644 --- a/feature/setting/src/main/java/com/yapp/setting/SettingViewModel.kt +++ b/feature/setting/src/main/java/com/yapp/setting/SettingViewModel.kt @@ -2,7 +2,6 @@ package com.yapp.setting import android.util.Log import androidx.lifecycle.viewModelScope -import com.yapp.datastore.UserPreferences import com.yapp.domain.repository.UserInfoRepository import com.yapp.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -14,7 +13,6 @@ import javax.inject.Inject @HiltViewModel class SettingViewModel @Inject constructor( private val userInfoRepository: UserInfoRepository, - private val userPreferences: UserPreferences, ) : BaseViewModel( SettingContract.State(), ) { @@ -58,7 +56,7 @@ class SettingViewModel @Inject constructor( private fun refreshUserInfo() { viewModelScope.launch { - val userId = userPreferences.userIdFlow.firstOrNull() + val userId = userInfoRepository.userIdFlow.firstOrNull() if (userId != null) { fetchUserInfo(userId) } diff --git a/feature/splash/build.gradle.kts b/feature/splash/build.gradle.kts index 0377f847..a0212a52 100644 --- a/feature/splash/build.gradle.kts +++ b/feature/splash/build.gradle.kts @@ -12,7 +12,7 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.common) implementation(projects.core.analytics) - implementation(projects.core.datastore) + implementation(projects.domain) implementation(libs.orbit.core) implementation(libs.orbit.compose) implementation(libs.orbit.viewmodel) diff --git a/feature/splash/src/main/java/com/yapp/splash/SplashViewModel.kt b/feature/splash/src/main/java/com/yapp/splash/SplashViewModel.kt index db697f4b..49e4ccdb 100644 --- a/feature/splash/src/main/java/com/yapp/splash/SplashViewModel.kt +++ b/feature/splash/src/main/java/com/yapp/splash/SplashViewModel.kt @@ -1,7 +1,7 @@ package com.yapp.splash import androidx.lifecycle.viewModelScope -import com.yapp.datastore.UserPreferences +import com.yapp.domain.repository.UserInfoRepository import com.yapp.ui.base.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay @@ -11,7 +11,7 @@ import javax.inject.Inject @HiltViewModel class SplashViewModel @Inject constructor( - private val userPreferences: UserPreferences, + private val userInfoRepository: UserInfoRepository, ) : BaseViewModel( initialState = SplashContract.State(), ) { @@ -33,8 +33,8 @@ class SplashViewModel @Inject constructor( private fun checkUserState() { viewModelScope.launch { combine( - userPreferences.userIdFlow, - userPreferences.onboardingCompletedFlow, + userInfoRepository.userIdFlow, + userInfoRepository.onboardingCompletedFlow, ) { userId, onboardingCompleted -> Pair(userId, onboardingCompleted) }.collect { (userId, onboardingCompleted) -> diff --git a/gradle/dependencyGraph.gradle b/gradle/dependencyGraph.gradle index 904cf4cd..86641c1e 100644 --- a/gradle/dependencyGraph.gradle +++ b/gradle/dependencyGraph.gradle @@ -1,5 +1,3 @@ -// from: https://github.com/DroidKaigi/conference-app-2021/blob/main/gradle/dependencyGraph.gradle -// from: https://github.com/JakeWharton/SdkSearch/blob/3351cad9bfacb0a364858e843774147143f58c7a/gradle/projectDependencyGraph.gradle tasks.register('projectDependencyGraph') { doLast { def dotFileName = 'project.dot' diff --git a/project.dot.png b/project.dot.png index 51f22f9d..11f78f97 100644 Binary files a/project.dot.png and b/project.dot.png differ diff --git a/settings.gradle.kts b/settings.gradle.kts index c2db5f10..e86bd657 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,10 +33,8 @@ include(":core:buildconfig") include(":data") include(":domain") include(":feature") -include(":core:security") include(":core:ui") include(":feature:home") -include(":feature:navigator") include(":feature:onboarding") include(":feature:mission") include(":feature:fortune")