diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 75afb311..79fceb1f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,13 +2,14 @@ - - - + + + - + - + android:value="${KAKAO_NATIVE_KEY}" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/PawKeyApplication.kt b/app/src/main/java/com/paw/key/PawKeyApplication.kt index 1b02e4c7..eb67c810 100644 --- a/app/src/main/java/com/paw/key/PawKeyApplication.kt +++ b/app/src/main/java/com/paw/key/PawKeyApplication.kt @@ -7,6 +7,7 @@ import coil.ImageLoaderFactory import coil.disk.DiskCache import coil.memory.MemoryCache import coil.util.DebugLogger +import com.kakao.sdk.common.KakaoSdk import com.kakao.vectormap.KakaoMapSdk import com.naver.maps.map.NaverMapSdk import dagger.hilt.android.HiltAndroidApp @@ -22,10 +23,10 @@ class PawKeyApplication : Application(), ImageLoaderFactory { override fun onCreate() { super.onCreate() - setTimber() setDarkMode() + KakaoSdk.init(this, kakaoNativeKey) KakaoMapSdk.init(this, kakaoNativeKey) NaverMapSdk.getInstance(this).client = NaverMapSdk.NcpKeyClient(BuildConfig.NAVERMAP_CLIENT_ID) diff --git a/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt b/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt index 925dbcea..9733454a 100644 --- a/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt +++ b/app/src/main/java/com/paw/key/data/di/RepositoryModule.kt @@ -22,8 +22,10 @@ import com.paw.key.data.repositoryimpl.walklist.WalkListDetailRepositoryImpl import com.paw.key.data.repositoryimpl.walkreview.WalkReviewRepositoryImpl import com.paw.key.data.remote.datasource.datasourceimpl.AuthRemoteDataSourceImpl import com.paw.key.data.remote.datasource.datasourceimpl.GoogleAuthDataSourceImpl +import com.paw.key.data.remote.datasource.datasourceimpl.KakaoAuthDataSourceImpl import com.paw.key.data.remote.datasource.login.AuthRemoteDataSource import com.paw.key.data.remote.datasource.login.GoogleAuthDataSource +import com.paw.key.data.remote.datasource.login.KakaoAuthDataSource import com.paw.key.domain.repository.ArchivedListRepository import com.paw.key.domain.repository.DummyRepository import com.paw.key.domain.repository.LikeRepository @@ -66,6 +68,11 @@ interface RepositoryModule { impl: GoogleAuthDataSourceImpl, ): GoogleAuthDataSource + @Binds + abstract fun bindKakaoAuthDataSource( + impl: KakaoAuthDataSourceImpl + ): KakaoAuthDataSource + @Binds fun bindsDummyRepository( dummyRepositoryImpl: DummyRepositoryImpl diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/AuthRemoteDataSourceImpl.kt b/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/AuthRemoteDataSourceImpl.kt index 053f3b68..67cbf527 100644 --- a/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/AuthRemoteDataSourceImpl.kt +++ b/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/AuthRemoteDataSourceImpl.kt @@ -1,14 +1,13 @@ package com.paw.key.data.remote.datasource.datasourceimpl import com.paw.key.data.dto.request.LoginRequestDto -import com.paw.key.data.dto.response.BaseResponse import com.paw.key.data.dto.response.LoginResponseDto import com.paw.key.data.remote.datasource.login.AuthRemoteDataSource import com.paw.key.data.service.login.LoginService import javax.inject.Inject class AuthRemoteDataSourceImpl @Inject constructor( - private val loginService: LoginService + private val loginService: LoginService, ) : AuthRemoteDataSource { override suspend fun login(idToken: String, deviceId: String): LoginResponseDto { return loginService.login( @@ -18,4 +17,13 @@ class AuthRemoteDataSourceImpl @Inject constructor( ) ) } + + override suspend fun loginKakao(idToken: String, deviceId: String): LoginResponseDto { + return loginService.loginKakao( + LoginRequestDto( + idToken = idToken, + deviceId = deviceId + ) + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/KakaoAuthDataSourceImpl.kt b/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/KakaoAuthDataSourceImpl.kt new file mode 100644 index 00000000..7afaa3f9 --- /dev/null +++ b/app/src/main/java/com/paw/key/data/remote/datasource/datasourceimpl/KakaoAuthDataSourceImpl.kt @@ -0,0 +1,60 @@ +package com.paw.key.data.remote.datasource.datasourceimpl + +import android.content.Context +import com.kakao.sdk.auth.model.OAuthToken +import com.kakao.sdk.common.model.ClientError +import com.kakao.sdk.common.model.ClientErrorCause +import com.kakao.sdk.user.UserApiClient +import com.paw.key.core.util.suspendRunCatching +import com.paw.key.data.remote.datasource.login.KakaoAuthDataSource +import kotlinx.coroutines.suspendCancellableCoroutine +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +class KakaoAuthDataSourceImpl @Inject constructor() : KakaoAuthDataSource { + override suspend fun signIn(context: Context): Result = suspendRunCatching { + val accessToken = getKakaoAccessToken(context) + accessToken + } + + private suspend fun getKakaoAccessToken(context: Context): String = + suspendCancellableCoroutine { continuation -> + val callback: (OAuthToken?, Throwable?) -> Unit = { token, error -> + when { + error != null -> { + continuation.resumeWithException(error) + } + token != null -> { + continuation.resume(token.accessToken) + } + else -> { + continuation.resumeWithException( + IllegalStateException("Token and error are both null") + ) + } + } + } + + if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) { + UserApiClient.instance.loginWithKakaoTalk(context) { token, error -> + if (error is ClientError && error.reason == ClientErrorCause.Cancelled) { + continuation.resumeWithException(error) + return@loginWithKakaoTalk + } + when { + token != null -> callback(token, null) + error != null -> { + UserApiClient.instance.loginWithKakaoAccount( + context, + callback = callback + ) + } + } + } + } else { + UserApiClient.instance.loginWithKakaoAccount(context, callback = callback) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/login/AuthRemoteDataSource.kt b/app/src/main/java/com/paw/key/data/remote/datasource/login/AuthRemoteDataSource.kt index 3a5849bf..c674c39e 100644 --- a/app/src/main/java/com/paw/key/data/remote/datasource/login/AuthRemoteDataSource.kt +++ b/app/src/main/java/com/paw/key/data/remote/datasource/login/AuthRemoteDataSource.kt @@ -5,4 +5,6 @@ import com.paw.key.data.dto.response.LoginResponseDto interface AuthRemoteDataSource { suspend fun login(idToken: String, deviceId: String): LoginResponseDto + + suspend fun loginKakao(idToken: String, deviceId: String): LoginResponseDto } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/remote/datasource/login/KakaoAuthDataSource.kt b/app/src/main/java/com/paw/key/data/remote/datasource/login/KakaoAuthDataSource.kt new file mode 100644 index 00000000..f14b643f --- /dev/null +++ b/app/src/main/java/com/paw/key/data/remote/datasource/login/KakaoAuthDataSource.kt @@ -0,0 +1,7 @@ +package com.paw.key.data.remote.datasource.login + +import android.content.Context + +interface KakaoAuthDataSource { + suspend fun signIn(context: Context): Result +} \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/repositoryimpl/login/AuthRepositoryImpl.kt b/app/src/main/java/com/paw/key/data/repositoryimpl/login/AuthRepositoryImpl.kt index 8cd8095a..79718d8b 100644 --- a/app/src/main/java/com/paw/key/data/repositoryimpl/login/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/paw/key/data/repositoryimpl/login/AuthRepositoryImpl.kt @@ -6,6 +6,7 @@ import com.paw.key.core.util.suspendRunCatching import com.paw.key.data.dto.response.LoginResponseDto import com.paw.key.data.remote.datasource.login.AuthRemoteDataSource import com.paw.key.data.remote.datasource.login.GoogleAuthDataSource +import com.paw.key.data.remote.datasource.login.KakaoAuthDataSource import com.paw.key.domain.repository.login.AuthRepository import dagger.hilt.android.qualifiers.ApplicationContext import timber.log.Timber @@ -14,26 +15,32 @@ import javax.inject.Inject class AuthRepositoryImpl @Inject constructor( private val authRemoteDataSource: AuthRemoteDataSource, private val googleAuthDataSource: GoogleAuthDataSource, + private val kakaoAuthDataSource: KakaoAuthDataSource, @ApplicationContext private val context: Context ) : AuthRepository { override suspend fun signInWithGoogle(context: Context): Result = googleAuthDataSource.signIn(context).map { it.idToken } + override suspend fun signInWithKakao(context: Context): Result = + kakaoAuthDataSource.signIn(context) + override suspend fun login(idToken: String, deviceId: String): Result = suspendRunCatching { - val loginResponse = authRemoteDataSource.login(idToken, deviceId) + saveTokens(loginResponse) + loginResponse + } - UserDataStore.saveAcessToken( - context = this.context, - token = loginResponse.accessToken - ) - UserDataStore.saveRefreshToken( - context = this.context, - token = loginResponse.refreshToken - ) - + override suspend fun loginKakao(idToken: String, deviceId: String): Result = + suspendRunCatching { + val loginResponse = authRemoteDataSource.loginKakao(idToken, deviceId) + saveTokens(loginResponse) loginResponse } + + private suspend fun saveTokens(loginResponse: LoginResponseDto) { + UserDataStore.saveAcessToken(context, loginResponse.accessToken) + UserDataStore.saveRefreshToken(context, loginResponse.refreshToken) + } } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/data/service/login/LoginService.kt b/app/src/main/java/com/paw/key/data/service/login/LoginService.kt index f6e953d4..7baacd31 100644 --- a/app/src/main/java/com/paw/key/data/service/login/LoginService.kt +++ b/app/src/main/java/com/paw/key/data/service/login/LoginService.kt @@ -13,4 +13,9 @@ interface LoginService { suspend fun login( @Body loginRequestDto: LoginRequestDto ): LoginResponseDto + + @POST("auth/kakao/login") + suspend fun loginKakao( + @Body loginRequestDto: LoginRequestDto + ): LoginResponseDto } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/domain/repository/login/AuthRepository.kt b/app/src/main/java/com/paw/key/domain/repository/login/AuthRepository.kt index 6791bf6f..bbf771d4 100644 --- a/app/src/main/java/com/paw/key/domain/repository/login/AuthRepository.kt +++ b/app/src/main/java/com/paw/key/domain/repository/login/AuthRepository.kt @@ -5,5 +5,7 @@ import com.paw.key.data.dto.response.LoginResponseDto interface AuthRepository { suspend fun signInWithGoogle(context: Context): Result + suspend fun signInWithKakao(context: Context): Result suspend fun login(idToken: String, deviceId: String): Result + suspend fun loginKakao(idToken: String, deviceId: String): Result } \ No newline at end of file diff --git a/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt b/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt index ec8f8c10..15add237 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/login/LoginScreen.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -143,12 +144,18 @@ fun LoginScreen( Image( painter = painterResource(R.drawable.img_login_sub), contentDescription = stringResource(R.string.ic_login_sub_image), + contentScale = ContentScale.Crop, ) LoginSocialButton( logo = R.drawable.ic_login_kakao, loginText = stringResource(R.string.ic_login_kakao), - onClick = {}, + onClick = { + viewModel.onKakaoSignIn( + context = context, + onSuccess = navigateHome + ) + }, modifier = Modifier .background( shape = RoundedCornerShape(12.dp), diff --git a/app/src/main/java/com/paw/key/presentation/ui/login/viewmodel/LoginViewModel.kt b/app/src/main/java/com/paw/key/presentation/ui/login/viewmodel/LoginViewModel.kt index b1959ab6..a5678f62 100644 --- a/app/src/main/java/com/paw/key/presentation/ui/login/viewmodel/LoginViewModel.kt +++ b/app/src/main/java/com/paw/key/presentation/ui/login/viewmodel/LoginViewModel.kt @@ -50,6 +50,28 @@ class LoginViewModel @Inject constructor( } } + fun onKakaoSignIn( + context: Context, + onSuccess: () -> Unit, + ) { + viewModelScope.launch { + authRepository.signInWithKakao(context) + .onSuccess { accessToken -> + val deviceId = getDeviceId(context) + authRepository.loginKakao(accessToken, deviceId) + .onSuccess { response -> + onSuccess() + } + .onFailure { e -> + Timber.e(e, "Full stack trace:") + } + } + .onFailure { e -> + Timber.e("[KAKAO_VM] Step 2: SDK login FAILED") + } + } + } + private fun getDeviceId(context: Context): String { return android.provider.Settings.Secure.getString( context.contentResolver,