diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3355e6a6..0115b657 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -29,26 +29,26 @@ android { isMinifyEnabled = true isShrinkResources = true isDebuggable = false - android.buildFeatures.buildConfig = true + android.buildFeatures.buildConfig = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) - buildConfigField("String","KAKAO_APP_KEY" , getLocalProperty("KAKAO_APP_KEY")) - resValue ("string", "kakao_oauth_key", getLocalProperty("KAKAO_OAUTH_KEY")) + buildConfigField("String", "KAKAO_APP_KEY", getLocalProperty("KAKAO_APP_KEY")) + resValue("string", "kakao_oauth_key", getLocalProperty("KAKAO_OAUTH_KEY")) } debug { isMinifyEnabled = false isShrinkResources = false isDebuggable = true - android.buildFeatures.buildConfig = true + android.buildFeatures.buildConfig = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) applicationIdSuffix = ".debug" - buildConfigField("String","KAKAO_APP_KEY" , getLocalProperty("KAKAO_APP_KEY")) - resValue ("string", "kakao_oauth_key", getLocalProperty("KAKAO_OAUTH_KEY")) + buildConfigField("String", "KAKAO_APP_KEY", getLocalProperty("KAKAO_APP_KEY")) + resValue("string", "kakao_oauth_key", getLocalProperty("KAKAO_OAUTH_KEY")) } } @@ -125,7 +125,7 @@ dependencies { implementation(libs.bundles.logging) debugImplementation(libs.leakcanary) - implementation(libs.kakao.user) + implementation(libs.bundles.kakao) } fun getLocalProperty(propertyKey: String): String { diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 7a3ff14c..475ddd99 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -50,7 +50,7 @@ dependencies { implementation(libs.bundles.logging) debugImplementation(libs.okhttp3.logging.interceptor) - implementation(libs.kakao.user) + implementation(libs.bundles.kakao) } fun getLocalProperty(propertyKey: String): String { diff --git a/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/di/RepositoryModule.kt b/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/di/RepositoryModule.kt index b9aa5b11..1df414f2 100644 --- a/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/di/RepositoryModule.kt +++ b/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/di/RepositoryModule.kt @@ -5,6 +5,7 @@ import ac.dnd.bookkeeping.android.data.repository.authentication.MockAuthenticat import ac.dnd.bookkeeping.android.data.repository.authentication.sociallogin.KakaoLoginRepositoryImpl import ac.dnd.bookkeeping.android.data.repository.feature.group.MockGroupRepository import ac.dnd.bookkeeping.android.data.repository.feature.heart.MockHeartRepository +import ac.dnd.bookkeeping.android.data.repository.feature.relation.KakaoFriendRepositoryImpl import ac.dnd.bookkeeping.android.data.repository.feature.relation.MockRelationRepository import ac.dnd.bookkeeping.android.data.repository.feature.schedule.MockScheduleRepository import ac.dnd.bookkeeping.android.data.repository.feature.statistics.MockStatisticsRepository @@ -17,6 +18,7 @@ import ac.dnd.bookkeeping.android.domain.repository.GalleryImageRepository import ac.dnd.bookkeeping.android.domain.repository.GalleryRepository import ac.dnd.bookkeeping.android.domain.repository.GroupRepository import ac.dnd.bookkeeping.android.domain.repository.HeartRepository +import ac.dnd.bookkeeping.android.domain.repository.KakaoFriendRepository import ac.dnd.bookkeeping.android.domain.repository.KakaoLoginRepository import ac.dnd.bookkeeping.android.domain.repository.MemberRepository import ac.dnd.bookkeeping.android.domain.repository.RelationRepository @@ -80,6 +82,12 @@ internal abstract class RepositoryModule { kakaoLoginRepositoryImpl: KakaoLoginRepositoryImpl ): KakaoLoginRepository + @Binds + @Singleton + abstract fun bindsKakaoFriendRepository( + kakaoFriendRepositoryImpl: KakaoFriendRepositoryImpl + ): KakaoFriendRepository + @Binds @Singleton abstract fun bindsFileRepository( diff --git a/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/repository/feature/relation/KakaoFriendRepositoryImpl.kt b/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/repository/feature/relation/KakaoFriendRepositoryImpl.kt new file mode 100644 index 00000000..4c236dfd --- /dev/null +++ b/data/src/main/kotlin/ac/dnd/bookkeeping/android/data/repository/feature/relation/KakaoFriendRepositoryImpl.kt @@ -0,0 +1,70 @@ +package ac.dnd.bookkeeping.android.data.repository.feature.relation + +import ac.dnd.bookkeeping.android.domain.model.error.ServerException +import ac.dnd.bookkeeping.android.domain.model.feature.relation.KakaoFriendInfo +import ac.dnd.bookkeeping.android.domain.repository.KakaoFriendRepository +import android.content.Context +import com.kakao.sdk.friend.client.PickerClient +import com.kakao.sdk.friend.model.OpenPickerFriendRequestParams +import com.kakao.sdk.friend.model.PickerOrientation +import com.kakao.sdk.friend.model.ViewAppearance +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +class KakaoFriendRepositoryImpl @Inject constructor( + @ApplicationContext private val context: Context +) : KakaoFriendRepository { + + override suspend fun loadFriendInfo(): Result { + return runCatching { + PickerClient.findKakaoFriend() + } + .onSuccess { + Result.success(it) + } + .onFailure { error -> + throw ServerException("Error", error.message.toString()) + } + } + + + private suspend fun PickerClient.Companion.findKakaoFriend(): KakaoFriendInfo { + return suspendCoroutine { continuation -> + val openPickerFriendRequestParams = OpenPickerFriendRequestParams( + title = "팝업 싱글 친구 피커", + viewAppearance = ViewAppearance.AUTO, + orientation = PickerOrientation.AUTO, + enableSearch = true, + enableIndex = true, + showMyProfile = true, + showFavorite = true + ) + + instance.selectFriendPopup( + context = context, + params = openPickerFriendRequestParams + ) { selectedUsers, error -> + if (error != null) { + continuation.resumeWithException(error) + } else { + val user = selectedUsers?.users?.firstOrNull() + val name = user?.profileNickname ?: "" + val image = user?.profileThumbnailImage ?: "" + if (name.isEmpty() || image.isEmpty()) { + continuation.resumeWithException(RuntimeException("Can't find user")) + } else { + continuation.resume( + KakaoFriendInfo( + name = name, + profileImageUrl = image + ) + ) + } + } + } + } + } +} diff --git a/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/model/feature/relation/KakaoFriendInfo.kt b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/model/feature/relation/KakaoFriendInfo.kt new file mode 100644 index 00000000..466737a6 --- /dev/null +++ b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/model/feature/relation/KakaoFriendInfo.kt @@ -0,0 +1,6 @@ +package ac.dnd.bookkeeping.android.domain.model.feature.relation + +data class KakaoFriendInfo( + val name: String, + val profileImageUrl: String, +) diff --git a/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/repository/KakaoFriendRepository.kt b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/repository/KakaoFriendRepository.kt new file mode 100644 index 00000000..e89eed50 --- /dev/null +++ b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/repository/KakaoFriendRepository.kt @@ -0,0 +1,7 @@ +package ac.dnd.bookkeeping.android.domain.repository + +import ac.dnd.bookkeeping.android.domain.model.feature.relation.KakaoFriendInfo + +interface KakaoFriendRepository { + suspend fun loadFriendInfo(): Result +} diff --git a/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/feature/relation/GetKakaoFriendInfoUseCase.kt b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/feature/relation/GetKakaoFriendInfoUseCase.kt new file mode 100644 index 00000000..1f6b4925 --- /dev/null +++ b/domain/src/main/kotlin/ac/dnd/bookkeeping/android/domain/usecase/feature/relation/GetKakaoFriendInfoUseCase.kt @@ -0,0 +1,12 @@ +package ac.dnd.bookkeeping.android.domain.usecase.feature.relation + +import ac.dnd.bookkeeping.android.domain.model.feature.relation.KakaoFriendInfo +import ac.dnd.bookkeeping.android.domain.repository.KakaoFriendRepository + +class GetKakaoFriendInfoUseCase( + private val kakaoFriendRepository: KakaoFriendRepository +) { + suspend operator fun invoke(): Result { + return kakaoFriendRepository.loadFriendInfo() + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc905aae..e11c45bf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -76,6 +76,7 @@ androidx-paging-common = { module = "androidx.paging:paging-common-ktx", version google-system-contoller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "google-system-contoller" } # Kakao kakao-user = { module = "com.kakao.sdk:v2-user", version.ref = "kakao" } +kakao-friend = { module = "com.kakao.sdk:v2-friend", version.ref = "kakao" } # Network ktor-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } @@ -117,3 +118,4 @@ androidx-presentation = ["androidx-core", "androidx-appcompat", "androidx-compos network = ["ktor-core", "ktor-okhttp", "ktor-resources", "ktor-content-negotiation", "ktor-kotlinx-serialization", "ktor-auth", "kotlinx-serialization"] logging = ["timber", "sentry"] +kakao = ["kakao-user", "kakao-friend"]