Skip to content

Commit

Permalink
[AN] feat: kakao login (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
junjange authored Jul 20, 2024
1 parent 1e89c4c commit 06bf0c1
Show file tree
Hide file tree
Showing 20 changed files with 225 additions and 19 deletions.
4 changes: 4 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ val localProperties = Properties()
localProperties.load(FileInputStream(localPropertiesFile))

val googleClientId = localProperties.getProperty("GOOGLE_CLIENT_ID") ?: ""
val kakaoNativeAppKey = localProperties.getProperty("KAKAO_NATIVE_APP_KEY") ?: ""
val kakaoOauthHost = localProperties.getProperty("KAKAO_OAUTH_HOST") ?: ""
val naverClientId = localProperties.getProperty("NAVER_CLIEND_ID") ?: ""
val baseUrl = localProperties.getProperty("base_url") ?: ""

Expand All @@ -32,6 +34,8 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

buildConfigField("String", "GOOGLE_CLIENT_ID", googleClientId)
buildConfigField("String", "KAKAO_NATIVE_APP_KEY", kakaoNativeAppKey)
resValue("string", "KAKAO_OAUTH_HOST", kakaoOauthHost)
buildConfigField("String", "NAVER_CLIEND_ID", naverClientId)
buildConfigField("String", "base_url", baseUrl)
}
Expand Down
16 changes: 15 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
android:theme="@style/Theme.Friendogly"
android:usesCleartextTraffic="true"
tools:targetApi="31">

<activity
android:name=".presentation.ui.profilesetting.ProfileSettingActivity"
android:exported="false" />
Expand All @@ -39,6 +38,21 @@
android:name="com.canhub.cropper.CropImageActivity"
android:theme="@style/Theme.ImageCropper" />

<activity
android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data
android:host="oauth"
android:scheme="@string/KAKAO_OAUTH_HOST" />
</intent-filter>
</activity>

</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package com.woowacourse.friendogly.application

import android.app.Application
import com.kakao.sdk.common.KakaoSdk
import com.naver.maps.map.NaverMapSdk
import com.woowacourse.friendogly.BuildConfig
import com.woowacourse.friendogly.kakao.di.KakaoModule

class FriendoglyApplication : Application() {
override fun onCreate() {
super.onCreate()
initNaverMapSdk()
KakaoSdk.init(this, BuildConfig.KAKAO_NATIVE_APP_KEY)
}

private fun initNaverMapSdk() {
NaverMapSdk.getInstance(this).client =
NaverMapSdk.NaverCloudPlatformClient(BuildConfig.NAVER_CLIEND_ID)
}

companion object {
val kakaoModule: KakaoModule = KakaoModule()
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.woowacourse.friendogly.data.datasource

import android.content.Context
import com.woowacourse.friendogly.data.model.KakaoAccessTokenDto

interface KakaoLoginDataSource {
suspend fun login(context: Context): Result<KakaoAccessTokenDto>

companion object {
private var instance: KakaoLoginDataSource? = null

fun setInstance(dataSource: KakaoLoginDataSource) {
instance = dataSource
}

fun getInstance(): KakaoLoginDataSource = requireNotNull(instance)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.woowacourse.friendogly.data.mapper

import com.woowacourse.friendogly.data.model.KakaoAccessTokenDto
import com.woowacourse.friendogly.domain.model.KakaoAccessToken

fun KakaoAccessTokenDto.toDomain(): KakaoAccessToken {
return KakaoAccessToken(
idToken = this.idToken,
accessToken = this.accessToken,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.woowacourse.friendogly.data.model

data class KakaoAccessTokenDto(
val accessToken: String,
val idToken: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.woowacourse.friendogly.data.repository

import android.content.Context
import com.woowacourse.friendogly.data.datasource.KakaoLoginDataSource
import com.woowacourse.friendogly.data.mapper.toDomain
import com.woowacourse.friendogly.domain.model.KakaoAccessToken
import com.woowacourse.friendogly.domain.repository.KakaoLoginRepository

class KakaoLoginRepositoryImpl(
private val dataSource: KakaoLoginDataSource,
) : KakaoLoginRepository {
override suspend fun login(context: Context): Result<KakaoAccessToken> =
dataSource.login(context = context).mapCatching { it.toDomain() }
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.woowacourse.friendogly.domain.model

data class KakaoAccessToken(
val accessToken: String?,
val idToken: String?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.woowacourse.friendogly.domain.repository

import android.content.Context
import com.woowacourse.friendogly.domain.model.KakaoAccessToken

interface KakaoLoginRepository {
suspend fun login(context: Context): Result<KakaoAccessToken>

companion object {
private var instance: KakaoLoginRepository? = null

fun setInstance(repository: KakaoLoginRepository) {
instance = repository
}

fun getInstance(): KakaoLoginRepository = requireNotNull(instance)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.woowacourse.friendogly.domain.usecase

import android.content.Context
import com.woowacourse.friendogly.domain.model.KakaoAccessToken
import com.woowacourse.friendogly.domain.repository.KakaoLoginRepository

class KakaoLoginUseCase(
private val repository: KakaoLoginRepository,
) {
suspend operator fun invoke(context: Context): Result<KakaoAccessToken> = repository.login(context = context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.woowacourse.friendogly.kakao.datasource

import android.content.Context
import com.kakao.sdk.auth.model.OAuthToken
import com.kakao.sdk.user.UserApiClient
import com.woowacourse.friendogly.data.datasource.KakaoLoginDataSource
import com.woowacourse.friendogly.data.model.KakaoAccessTokenDto
import com.woowacourse.friendogly.kakao.mapper.toData
import com.woowacourse.friendogly.kakao.model.KakaoAccessTokenResponse
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException

class KakaoLoginDataSourceImpl : KakaoLoginDataSource {
/**
* @param context: Activity context
*/
override suspend fun login(context: Context): Result<KakaoAccessTokenDto> =
runCatching {
suspendCancellableCoroutine { continuation ->
val callback: (OAuthToken?, Throwable?) -> Unit = { token, throwable ->
when {
throwable != null -> continuation.resumeWithException(throwable)
token != null && continuation.isActive -> {
val idToken =
token.idToken
?: throw IllegalArgumentException("kakao login idToken이 없습니다.")
val accessToken =
KakaoAccessTokenResponse(
accessToken = token.accessToken,
idToken = idToken,
)
continuation.resume(accessToken.toData())
}
}
}

val userApiClient = UserApiClient.instance
if (userApiClient.isKakaoTalkLoginAvailable(context)) {
userApiClient.loginWithKakaoTalk(context, callback = callback)
} else {
userApiClient.loginWithKakaoAccount(context, callback = callback)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.woowacourse.friendogly.kakao.di

import com.woowacourse.friendogly.data.datasource.KakaoLoginDataSource
import com.woowacourse.friendogly.data.repository.KakaoLoginRepositoryImpl
import com.woowacourse.friendogly.domain.repository.KakaoLoginRepository
import com.woowacourse.friendogly.domain.usecase.KakaoLoginUseCase
import com.woowacourse.friendogly.kakao.datasource.KakaoLoginDataSourceImpl

class KakaoModule {
private val kakaoLoginDataSource: KakaoLoginDataSource = KakaoLoginDataSourceImpl()

private val kakaoLoginRepository: KakaoLoginRepository =
KakaoLoginRepositoryImpl(dataSource = kakaoLoginDataSource)

val kakaoLoginUseCase: KakaoLoginUseCase = KakaoLoginUseCase(repository = kakaoLoginRepository)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.woowacourse.friendogly.kakao.mapper

import com.woowacourse.friendogly.data.model.KakaoAccessTokenDto
import com.woowacourse.friendogly.kakao.model.KakaoAccessTokenResponse

fun KakaoAccessTokenResponse.toData(): KakaoAccessTokenDto {
return KakaoAccessTokenDto(
accessToken = this.accessToken,
idToken = this.idToken,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.woowacourse.friendogly.kakao.model

data class KakaoAccessTokenResponse(
val accessToken: String,
val idToken: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package com.woowacourse.friendogly.presentation.ui.register
import androidx.activity.viewModels
import com.google.android.gms.common.api.ApiException
import com.woowacourse.friendogly.R
import com.woowacourse.friendogly.application.FriendoglyApplication.Companion.kakaoModule
import com.woowacourse.friendogly.databinding.ActivityRegisterBinding
import com.woowacourse.friendogly.presentation.base.BaseActivity
import com.woowacourse.friendogly.presentation.base.observeEvent
import com.woowacourse.friendogly.presentation.ui.MainActivity
import com.woowacourse.friendogly.presentation.ui.profilesetting.ProfileSettingActivity

class RegisterActivity : BaseActivity<ActivityRegisterBinding>(R.layout.activity_register) {
private val viewModel: RegisterViewModel by viewModels()
private val viewModel: RegisterViewModel by viewModels {
RegisterViewModel.factory(kakaoLoginUseCase = kakaoModule.kakaoLoginUseCase)
}

private val googleSignInLauncher =
registerForActivityResult(GoogleSignInContract()) { task ->
Expand All @@ -32,16 +34,15 @@ class RegisterActivity : BaseActivity<ActivityRegisterBinding>(R.layout.activity
private fun initObserve() {
viewModel.navigateAction.observeEvent(this) { action ->
when (action) {
is RegisterNavigationAction.NavigateToKakaoLogin -> {
startActivity(MainActivity.getIntent(this))
finish()
}

is RegisterNavigationAction.NavigateToGoogleLogin ->
googleSignInLauncher.launch(SIGN_IN_REQUEST_CODE)
googleSignInLauncher.launch(
SIGN_IN_REQUEST_CODE,
)

is RegisterNavigationAction.NavigateToProfileSetting ->
startActivity(ProfileSettingActivity.getIntent(this))
startActivity(
ProfileSettingActivity.getIntent(this),
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.woowacourse.friendogly.presentation.ui.register

sealed interface RegisterNavigationAction {
data object NavigateToKakaoLogin : RegisterNavigationAction

data object NavigateToGoogleLogin : RegisterNavigationAction

data class NavigateToProfileSetting(val idToken: String) : RegisterNavigationAction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
package com.woowacourse.friendogly.presentation.ui.register

import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.woowacourse.friendogly.domain.usecase.KakaoLoginUseCase
import com.woowacourse.friendogly.presentation.base.BaseViewModel
import com.woowacourse.friendogly.presentation.base.BaseViewModelFactory
import com.woowacourse.friendogly.presentation.base.Event
import com.woowacourse.friendogly.presentation.base.emit
import kotlinx.coroutines.launch

class RegisterViewModel : BaseViewModel() {
class RegisterViewModel(
private val kakaoLoginUseCase: KakaoLoginUseCase,
) : BaseViewModel() {
private val _navigateAction: MutableLiveData<Event<RegisterNavigationAction>> =
MutableLiveData(null)
val navigateAction: LiveData<Event<RegisterNavigationAction>> get() = _navigateAction

fun executeKakaoLogin() {
_navigateAction.emit(RegisterNavigationAction.NavigateToKakaoLogin)
fun executeKakaoLogin(context: Context) {
viewModelScope.launch {
kakaoLoginUseCase(context = context).onSuccess {
_navigateAction.emit(RegisterNavigationAction.NavigateToGoogleLogin)
}.onFailure { }
}
}

fun executeGoogleLogin() {
Expand All @@ -22,4 +34,12 @@ class RegisterViewModel : BaseViewModel() {
fun handleGoogleLogin(idToken: String) {
_navigateAction.emit(RegisterNavigationAction.NavigateToProfileSetting(idToken = idToken))
}

companion object {
fun factory(kakaoLoginUseCase: KakaoLoginUseCase): ViewModelProvider.Factory {
return BaseViewModelFactory { _ ->
RegisterViewModel(kakaoLoginUseCase = kakaoLoginUseCase)
}
}
}
}
4 changes: 3 additions & 1 deletion android/app/src/main/res/layout/activity_register.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

<data>

<import type="android.view.View" />

<variable
name="vm"
type="com.woowacourse.friendogly.presentation.ui.register.RegisterViewModel" />
Expand Down Expand Up @@ -40,7 +42,7 @@

<ImageButton
android:id="@+id/ib_kakao_register"
onSingleClick="@{() -> vm.executeKakaoLogin()}"
onSingleClick="@{(view) -> vm.executeKakaoLogin(view.context)}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
Expand Down

0 comments on commit 06bf0c1

Please sign in to comment.