From 397c72b67e901cd344893102ea0d7a5fa8819c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=84=9D?= Date: Thu, 9 Feb 2023 09:12:03 +0900 Subject: [PATCH] =?UTF-8?q?Issues=20#287=20feat:=20data-preference=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/lighthouse/common/di/AuthModule.kt | 17 +++ data/data-preference/.gitignore | 1 + data/data-preference/build.gradle.kts | 24 ++++ .../data/preference/di/DataModule.kt | 19 +++ .../data/preference/di/PreferenceModule.kt | 29 ++++ .../exception/PrefNotFoundException.kt | 3 + .../data/preference/ext/KotlinExt.kt | 14 ++ .../data/preference/ext/PreferencesExt.kt | 49 +++++++ .../UserPreferenceRepositoryImpl.kt | 136 ++++++++++++++++++ .../repository/user/AuthRepository.kt | 13 ++ .../user/UserPreferenceRepository.kt | 42 ++++++ .../repository/user/UserRepositoryImpl.kt | 49 +++++++ .../domain/repository/user/UserRepository.kt | 21 +++ .../{db => common}/NotFoundException.kt | 2 +- .../beep/model/user}/SecurityOption.kt | 2 +- 15 files changed, 419 insertions(+), 2 deletions(-) create mode 100644 commons/common-auth/src/main/java/com/lighthouse/common/di/AuthModule.kt create mode 100644 data/data-preference/.gitignore create mode 100644 data/data-preference/build.gradle.kts create mode 100644 data/data-preference/src/main/java/com/lighthouse/data/preference/di/DataModule.kt create mode 100644 data/data-preference/src/main/java/com/lighthouse/data/preference/di/PreferenceModule.kt create mode 100644 data/data-preference/src/main/java/com/lighthouse/data/preference/exception/PrefNotFoundException.kt create mode 100644 data/data-preference/src/main/java/com/lighthouse/data/preference/ext/KotlinExt.kt create mode 100644 data/data-preference/src/main/java/com/lighthouse/data/preference/ext/PreferencesExt.kt create mode 100644 data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt create mode 100644 data/data/src/main/java/com/lighthouse/repository/user/AuthRepository.kt create mode 100644 data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt create mode 100644 data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt create mode 100644 domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt rename model/src/main/java/com/lighthouse/beep/model/exception/{db => common}/NotFoundException.kt (67%) rename {presentation/src/main/java/com/lighthouse/presentation/ui/setting => model/src/main/java/com/lighthouse/beep/model/user}/SecurityOption.kt (71%) diff --git a/commons/common-auth/src/main/java/com/lighthouse/common/di/AuthModule.kt b/commons/common-auth/src/main/java/com/lighthouse/common/di/AuthModule.kt new file mode 100644 index 000000000..0b3370fb9 --- /dev/null +++ b/commons/common-auth/src/main/java/com/lighthouse/common/di/AuthModule.kt @@ -0,0 +1,17 @@ +package com.lighthouse.common.di + +import com.google.firebase.auth.FirebaseAuth +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Suppress("unused") +@Module +@InstallIn(SingletonComponent::class) +internal object AuthModule { + @Provides + @Singleton + fun provideFirebaseAuth(): FirebaseAuth = FirebaseAuth.getInstance() +} diff --git a/data/data-preference/.gitignore b/data/data-preference/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/data/data-preference/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/data-preference/build.gradle.kts b/data/data-preference/build.gradle.kts new file mode 100644 index 000000000..3a27941bd --- /dev/null +++ b/data/data-preference/build.gradle.kts @@ -0,0 +1,24 @@ +@Suppress("DSL_SCOPE_VIOLATION") +plugins { + id("beep.android.library") + id("beep.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.lighthouse.data.preference" +} + +dependencies { + implementation(projects.core) + implementation(projects.coreAndroid) + implementation(projects.model) + implementation(projects.common) + implementation(projects.commonAndroid) + implementation(projects.commonLocation) + implementation(projects.commonRecognizer) + implementation(projects.data) + + implementation(libs.androidX.datastore.preferences) + implementation(libs.javax.inject) +} diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/di/DataModule.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/di/DataModule.kt new file mode 100644 index 000000000..862d903af --- /dev/null +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/di/DataModule.kt @@ -0,0 +1,19 @@ +package com.lighthouse.data.preference.di + +import com.lighthouse.data.preference.repository.UserPreferenceRepositoryImpl +import com.lighthouse.repository.user.UserPreferenceRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Suppress("unused") +@Module +@InstallIn(SingletonComponent::class) +internal abstract class DataModule { + + @Binds + abstract fun bindsUserPreferenceRepository( + repository: UserPreferenceRepositoryImpl + ): UserPreferenceRepository +} diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/di/PreferenceModule.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/di/PreferenceModule.kt new file mode 100644 index 000000000..b94764c58 --- /dev/null +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/di/PreferenceModule.kt @@ -0,0 +1,29 @@ +package com.lighthouse.data.preference.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStoreFile +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Suppress("unused") +@Module +@InstallIn(SingletonComponent::class) +internal object PreferenceModule { + + private const val USER_PREFERENCES = "user_preferences" + + @Singleton + @Provides + fun providePreferencesDataStore(@ApplicationContext context: Context): DataStore { + return PreferenceDataStoreFactory.create( + produceFile = { context.preferencesDataStoreFile(USER_PREFERENCES) } + ) + } +} diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/exception/PrefNotFoundException.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/exception/PrefNotFoundException.kt new file mode 100644 index 000000000..f95aefcb7 --- /dev/null +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/exception/PrefNotFoundException.kt @@ -0,0 +1,3 @@ +package com.lighthouse.data.preference.exception + +internal class PrefNotFoundException(message: String = "데이터를 찾을 수 없습니다.") : Exception(message) diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/ext/KotlinExt.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/ext/KotlinExt.kt new file mode 100644 index 000000000..4e9000271 --- /dev/null +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/ext/KotlinExt.kt @@ -0,0 +1,14 @@ +package com.lighthouse.data.preference.ext + +import com.lighthouse.beep.model.exception.common.NotFoundException +import com.lighthouse.data.preference.exception.PrefNotFoundException + +internal inline fun T.runCatchingPref(block: T.() -> R): Result { + return try { + Result.success(block()) + } catch (e: PrefNotFoundException) { + Result.failure(NotFoundException(e.message)) + } catch (e: Throwable) { + Result.failure(e) + } +} diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/ext/PreferencesExt.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/ext/PreferencesExt.kt new file mode 100644 index 000000000..616adf0f6 --- /dev/null +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/ext/PreferencesExt.kt @@ -0,0 +1,49 @@ +@file:Suppress("unused") + +package com.lighthouse.data.preference.ext + +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.byteArrayPreferencesKey +import androidx.datastore.preferences.core.doublePreferencesKey +import androidx.datastore.preferences.core.floatPreferencesKey +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.core.stringSetPreferencesKey + +private fun createKey(userId: String, key: String): String { + return "${userId}_$key" +} + +internal fun intKey(userId: String, key: String): Preferences.Key { + return intPreferencesKey(createKey(userId, key)) +} + +internal fun doubleKey(userId: String, key: String): Preferences.Key { + return doublePreferencesKey(createKey(userId, key)) +} + +internal fun stringKey(userId: String, key: String): Preferences.Key { + return stringPreferencesKey(createKey(userId, key)) +} + +internal fun booleanKey(userId: String, key: String): Preferences.Key { + return booleanPreferencesKey(createKey(userId, key)) +} + +internal fun floatKey(userId: String, key: String): Preferences.Key { + return floatPreferencesKey(createKey(userId, key)) +} + +internal fun longKey(userId: String, key: String): Preferences.Key { + return longPreferencesKey(createKey(userId, key)) +} + +internal fun stringSetKey(userId: String, key: String): Preferences.Key> { + return stringSetPreferencesKey(createKey(userId, key)) +} + +internal fun byteArrayKey(userId: String, key: String): Preferences.Key { + return byteArrayPreferencesKey(createKey(userId, key)) +} diff --git a/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt b/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt new file mode 100644 index 000000000..458df6df2 --- /dev/null +++ b/data/data-preference/src/main/java/com/lighthouse/data/preference/repository/UserPreferenceRepositoryImpl.kt @@ -0,0 +1,136 @@ +package com.lighthouse.data.preference.repository + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import com.lighthouse.beep.model.user.SecurityOption +import com.lighthouse.data.preference.exception.PrefNotFoundException +import com.lighthouse.data.preference.ext.booleanKey +import com.lighthouse.data.preference.ext.byteArrayKey +import com.lighthouse.data.preference.ext.runCatchingPref +import com.lighthouse.data.preference.ext.stringKey +import com.lighthouse.repository.user.UserPreferenceRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import javax.inject.Inject + +internal class UserPreferenceRepositoryImpl @Inject constructor( + private val dataStore: DataStore +) : UserPreferenceRepository { + + override suspend fun getIV(userId: String): Result = runCatchingPref { + val key = byteArrayKey(userId, KEY_NAME_IV) + dataStore.data.first()[key] + } + + override suspend fun setIV( + userId: String, + iv: ByteArray + ): Result = runCatchingPref { + val key = byteArrayKey(userId, KEY_NAME_IV) + dataStore.edit { pref -> + pref[key] = iv + } + } + + override suspend fun setPinPassword( + userId: String, + pinPassword: ByteArray + ): Result = runCatchingPref { + val key = byteArrayKey(userId, KEY_NAME_PIN_PASSWORD) + dataStore.edit { pref -> + pref[key] = pinPassword + } + } + + override suspend fun confirmPinPassword( + userId: String, + pinPassword: ByteArray + ): Result = runCatchingPref { + val key = byteArrayKey(userId, KEY_NAME_PIN_PASSWORD) + val savedPinPassword = dataStore.data.first()[key] + savedPinPassword.contentEquals(pinPassword) + } + + override suspend fun setSecurityOption( + userId: String, + securityOption: SecurityOption + ): Result = runCatchingPref { + val key = stringKey(userId, KEY_NAME_SECURITY_OPTION) + dataStore.edit { pref -> + pref[key] = securityOption.name + } + } + + override fun getSecurityOption( + userId: String + ): Result> = runCatchingPref { + val key = stringKey(userId, KEY_NAME_SECURITY_OPTION) + dataStore.data.map { pref -> + val value = pref[key] ?: throw PrefNotFoundException("Security 값이 없습니다.") + SecurityOption.valueOf(value) + } + } + + override suspend fun setNotificationEnable( + userId: String, + enable: Boolean + ): Result = runCatchingPref { + val key = booleanKey(userId, KEY_NAME_NOTIFICATION_ENABLE) + dataStore.edit { pref -> + pref[key] = enable + } + } + + override fun getNotificationEnable( + userId: String + ): Result> = runCatchingPref { + val key = booleanKey(userId, KEY_NAME_NOTIFICATION_ENABLE) + dataStore.data.map { pref -> + pref[key] ?: throw PrefNotFoundException("Notification Enable 값이 없습니다.") + } + } + + override suspend fun transferData( + oldUserId: String, + newUserId: String + ): Result = runCatchingPref { + val data = dataStore.data.first() + val iv = data[byteArrayKey(oldUserId, KEY_NAME_IV)] + val pin = data[byteArrayKey(oldUserId, KEY_NAME_PIN_PASSWORD)] + val securityOption = data[stringKey(oldUserId, KEY_NAME_SECURITY_OPTION)]?.let { + SecurityOption.valueOf(it) + } ?: throw PrefNotFoundException("Security 값이 없습니다.") + val notificationEnable = data[booleanKey(oldUserId, KEY_NAME_NOTIFICATION_ENABLE)] + + dataStore.edit { pref -> + if (iv != null) { + pref[byteArrayKey(newUserId, KEY_NAME_IV)] = iv + } + if (pin != null) { + pref[byteArrayKey(newUserId, KEY_NAME_PIN_PASSWORD)] = pin + } + pref[stringKey(newUserId, KEY_NAME_SECURITY_OPTION)] = securityOption.name + pref[booleanKey(newUserId, KEY_NAME_NOTIFICATION_ENABLE)] = notificationEnable ?: false + } + + clearData(oldUserId) + } + + override suspend fun clearData(userId: String): Result = runCatchingPref { + dataStore.edit { pref -> + pref.remove(byteArrayKey(userId, KEY_NAME_IV)) + pref.remove(byteArrayKey(userId, KEY_NAME_PIN_PASSWORD)) + pref.remove(byteArrayKey(userId, KEY_NAME_SECURITY_OPTION)) + pref.remove(byteArrayKey(userId, KEY_NAME_NOTIFICATION_ENABLE)) + } + } + + companion object { + private const val KEY_NAME_PIN_PASSWORD = "PinPassword" + private const val KEY_NAME_IV = "IV" + private const val KEY_NAME_SECURITY_OPTION = "SecurityOption" + private const val KEY_NAME_NOTIFICATION_ENABLE = "NotificationEnable" + } +} diff --git a/data/data/src/main/java/com/lighthouse/repository/user/AuthRepository.kt b/data/data/src/main/java/com/lighthouse/repository/user/AuthRepository.kt new file mode 100644 index 000000000..4986fb364 --- /dev/null +++ b/data/data/src/main/java/com/lighthouse/repository/user/AuthRepository.kt @@ -0,0 +1,13 @@ +package com.lighthouse.repository.user + +import kotlinx.coroutines.flow.Flow +import javax.crypto.spec.IvParameterSpec + +interface AuthRepository { + + fun isGuest(): Flow + + fun getCurrentUserId(): String + + fun encrypt(pin: String, iv: IvParameterSpec) +} diff --git a/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt b/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt new file mode 100644 index 000000000..60f851002 --- /dev/null +++ b/data/data/src/main/java/com/lighthouse/repository/user/UserPreferenceRepository.kt @@ -0,0 +1,42 @@ +package com.lighthouse.repository.user + +import com.lighthouse.beep.model.user.SecurityOption +import kotlinx.coroutines.flow.Flow + +interface UserPreferenceRepository { + + suspend fun getIV(userId: String): Result + + suspend fun setIV(userId: String, iv: ByteArray): Result + + suspend fun setPinPassword( + userId: String, + pinPassword: ByteArray + ): Result + + suspend fun confirmPinPassword( + userId: String, + pinPassword: ByteArray + ): Result + + suspend fun setSecurityOption( + userId: String, + securityOption: SecurityOption + ): Result + + fun getSecurityOption(userId: String): Result> + + suspend fun setNotificationEnable( + userId: String, + enable: Boolean + ): Result + + fun getNotificationEnable(userId: String): Result> + + suspend fun transferData( + oldUserId: String, + newUserId: String + ): Result + + suspend fun clearData(userId: String): Result +} diff --git a/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt b/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt new file mode 100644 index 000000000..9e740f99f --- /dev/null +++ b/data/data/src/main/java/com/lighthouse/repository/user/UserRepositoryImpl.kt @@ -0,0 +1,49 @@ +package com.lighthouse.repository.user + +import com.lighthouse.beep.model.user.SecurityOption +import com.lighthouse.domain.repository.user.UserRepository +import kotlinx.coroutines.flow.Flow +import javax.inject.Inject + +internal class UserRepositoryImpl @Inject constructor( + private val authRepository: AuthRepository, + private val userPreferenceRepository: UserPreferenceRepository +) : UserRepository { + + override fun isGuest(): Flow { + return authRepository.isGuest() + } + + override suspend fun setPinPassword(pinPassword: ByteArray): Result { + val userId = authRepository.getCurrentUserId() + return userPreferenceRepository.setPinPassword(userId, pinPassword) + } + + override suspend fun confirmPinPassword(pinPassword: String): Result { + TODO("Not yet implemented") + } + + override suspend fun setSecurityOption(securityOption: SecurityOption): Result { + TODO("Not yet implemented") + } + + override fun getSecurityOption(): Flow { + TODO("Not yet implemented") + } + + override suspend fun setNotificationEnable(enable: Boolean): Result { + TODO("Not yet implemented") + } + + override fun getNotificationEnable(): Flow { + TODO("Not yet implemented") + } + + override suspend fun transferData(newUserId: String): Result { + TODO("Not yet implemented") + } + + override suspend fun clearData(): Result { + TODO("Not yet implemented") + } +} diff --git a/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt b/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt new file mode 100644 index 000000000..a8c81ad65 --- /dev/null +++ b/domain/src/main/java/com/lighthouse/domain/repository/user/UserRepository.kt @@ -0,0 +1,21 @@ +package com.lighthouse.domain.repository.user + +import com.lighthouse.beep.model.user.SecurityOption +import kotlinx.coroutines.flow.Flow + +interface UserRepository { + + fun isGuest(): Flow + + suspend fun setPinPassword(pinPassword: ByteArray): Result + suspend fun confirmPinPassword(pinPassword: String): Result + + suspend fun setSecurityOption(securityOption: SecurityOption): Result + fun getSecurityOption(): Flow + + suspend fun setNotificationEnable(enable: Boolean): Result + fun getNotificationEnable(): Flow + + suspend fun transferData(newUserId: String): Result + suspend fun clearData(): Result +} diff --git a/model/src/main/java/com/lighthouse/beep/model/exception/db/NotFoundException.kt b/model/src/main/java/com/lighthouse/beep/model/exception/common/NotFoundException.kt similarity index 67% rename from model/src/main/java/com/lighthouse/beep/model/exception/db/NotFoundException.kt rename to model/src/main/java/com/lighthouse/beep/model/exception/common/NotFoundException.kt index c204a4072..3ef8aeea8 100644 --- a/model/src/main/java/com/lighthouse/beep/model/exception/db/NotFoundException.kt +++ b/model/src/main/java/com/lighthouse/beep/model/exception/common/NotFoundException.kt @@ -1,3 +1,3 @@ -package com.lighthouse.beep.model.exception.db +package com.lighthouse.beep.model.exception.common class NotFoundException(message: String? = "데이터를 찾을 수 없습니다.") : Exception(message) diff --git a/presentation/src/main/java/com/lighthouse/presentation/ui/setting/SecurityOption.kt b/model/src/main/java/com/lighthouse/beep/model/user/SecurityOption.kt similarity index 71% rename from presentation/src/main/java/com/lighthouse/presentation/ui/setting/SecurityOption.kt rename to model/src/main/java/com/lighthouse/beep/model/user/SecurityOption.kt index a3933f7f4..ec794a2fe 100644 --- a/presentation/src/main/java/com/lighthouse/presentation/ui/setting/SecurityOption.kt +++ b/model/src/main/java/com/lighthouse/beep/model/user/SecurityOption.kt @@ -1,4 +1,4 @@ -package com.lighthouse.presentation.ui.setting +package com.lighthouse.beep.model.user enum class SecurityOption(val text: String) { NONE("사용 안 함"),