Skip to content

Commit

Permalink
#49 feat/카카오로그인 :
Browse files Browse the repository at this point in the history
1. 카카오 토큰 서버에 저장
2. jwt토큰 받아와 로컬에 저장
  • Loading branch information
tnvnfdla12 committed Nov 20, 2022
1 parent 0626763 commit e8cd963
Show file tree
Hide file tree
Showing 24 changed files with 218 additions and 56 deletions.
11 changes: 6 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,17 @@
android:name=".ui.main.MainActivity"
android:exported="true"
>
</activity>
<activity
android:name=".ui.login.LoginActivity"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.login.LoginActivity"
android:exported="true"
/>
</activity>
<activity
android:name=".ui.buildingreview.BuildingReviewActivity"
android:exported="true"
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/ftw/hometerview/di/api/APIModule.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.ftw.hometerview.di.api

import com.ftw.data.remote.api.BuildingReviewsAPI
import com.ftw.data.remote.api.LoginAPI
import com.ftw.data.remote.api.SignUpAPI
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -15,8 +15,8 @@ class APIModule {

@Provides
@Singleton
fun provideLoginApi(retrofit: Retrofit): LoginAPI {
return retrofit.create(LoginAPI::class.java)
fun provideLoginApi(retrofit: Retrofit): SignUpAPI {
return retrofit.create(SignUpAPI::class.java)
}

@Provides
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/ftw/hometerview/di/api/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import javax.inject.Singleton
class NetworkModule {

companion object {
private const val DEBUG_BASE_URL = "http://13.125.75.159:8080/"
private const val RELEASE_BASE_URL = "http://13.125.75.159:8080/"
private const val DEBUG_BASE_URL = "http://3.36.37.15:8080/"
private const val RELEASE_BASE_URL = "http://3.36.37.15:8080/"
}

@Provides
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.ftw.hometerview.di.datasource

import com.ftw.data.datasource.LoginDataSource
import com.ftw.data.remote.datasource.LoginRemoteDataSource
import com.ftw.data.remote.api.LoginAPI
import com.ftw.data.remote.api.SignUpAPI
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -15,7 +15,7 @@ class LoginDataSourceModule {

@Provides
@Singleton
fun provideLoginDataSource(api: LoginAPI): LoginDataSource {
fun provideLoginDataSource(api: SignUpAPI): LoginDataSource {
return LoginRemoteDataSource(api)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.ftw.domain.usecase.searchaddressbuilding.GetSearchAddressBuildingUseC
import com.ftw.domain.usecase.myreviews.GetMyReviewsUseCase
import com.ftw.hometerview.dispatcher.Dispatcher
import com.ftw.hometerview.ui.buildingreview.BuildingReviewViewModel
import com.ftw.hometerview.ui.login.LoginViewModel
import com.ftw.hometerview.ui.main.MainViewModel
import com.ftw.hometerview.ui.main.home.review.LocationReviewListViewModel
import com.ftw.hometerview.ui.manageaccount.ManageAccountViewModel
Expand Down Expand Up @@ -36,6 +37,15 @@ class ActivityViewModelModule {
return SplashViewModel(dispatcher, loginUseCase)
}

@Provides
@ActivityScoped
fun provideLoginViewModel(
dispatcher: Dispatcher,
loginUseCase: LoginUseCase
): LoginViewModel {
return LoginViewModel(dispatcher, loginUseCase)
}

@Provides
@ActivityScoped
fun provideSearchCompanyResultViewModel(
Expand Down
43 changes: 39 additions & 4 deletions app/src/main/java/com/ftw/hometerview/ui/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,30 @@ import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import androidx.viewpager2.widget.ViewPager2
import com.ftw.domain.entity.KakaoToken
import com.ftw.hometerview.R
import com.ftw.hometerview.adapter.AnimationAdapter
import com.ftw.hometerview.databinding.ActivityLoginBinding
import com.ftw.hometerview.ui.main.MainActivity
import com.ftw.hometerview.ui.searchcompany.SearchCompanyActivity
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 dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject

const val TAG = "LoginActivity"

@AndroidEntryPoint
class LoginActivity : AppCompatActivity() {

@Inject
lateinit var viewModel: LoginViewModel

companion object {
fun newIntent(context: Context): Intent {
return Intent(context, LoginActivity::class.java)
Expand All @@ -43,6 +53,7 @@ class LoginActivity : AppCompatActivity() {
setContentView(binding.root)

guideSetting()
ObserveStartActivity()

binding.kakaoLoginButton.setOnClickListener {
kakaoLogin()
Expand All @@ -69,25 +80,49 @@ class LoginActivity : AppCompatActivity() {
binding.dotsIndicator.setViewPager2(binding.guideViewpager)
}

private fun ObserveStartActivity(){
lifecycleScope.launch {
viewModel.state.collect { state ->
when(state) {
LoginViewModel.State.Success -> {
StartSearchCompanyActivity()
}
LoginViewModel.State.Failure -> {
toastMessage("로그인에 실패하셨습니다")
}
else -> {

}
}
}
}
}
private fun StartSearchCompanyActivity(){
startActivity(MainActivity.newIntent(this))
finish()
}

private fun toastMessage(message : String){
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

private fun kakaoLogin() {
// 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 카카오계정으로 로그인
if (UserApiClient.instance.isKakaoTalkLoginAvailable(this)) {
UserApiClient.instance.loginWithKakaoTalk(this) { token, error ->
if (error != null) {
Log.e(TAG, "카카오톡으로 로그인 실패", error)

// 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
// 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
Toast.makeText(this, "권한이 필요하므로, 다시 한번 시도해주세요!", Toast.LENGTH_SHORT).show()
toastMessage("권한이 필요하므로, 다시 한번 시도해주세요!")
return@loginWithKakaoTalk
}

// 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인 시도
UserApiClient.instance.loginWithKakaoAccount(this, callback = callback)
} else if (token != null) {
Log.i(TAG, "카카오톡으로 로그인 성공 token = ${token.accessToken}")
startActivity(SearchCompanyActivity.newIntent(this))
viewModel.setKakaoToken(KakaoToken(token.accessToken,token.refreshToken))
}
}
} else {
Expand Down
44 changes: 44 additions & 0 deletions app/src/main/java/com/ftw/hometerview/ui/login/LoginViewModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.ftw.hometerview.ui.login

import android.util.Log
import androidx.lifecycle.ViewModel
import com.ftw.domain.entity.KakaoToken
import com.ftw.domain.usecase.login.LoginUseCase
import com.ftw.hometerview.dispatcher.Dispatcher
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

class LoginViewModel(
private val dispatcher: Dispatcher,
private val loginUseCase: LoginUseCase
) : ViewModel() {

sealed class State {
object Success : State()
object Failure : State()
object Loading : State()
}

private val _state: MutableStateFlow<State> = MutableStateFlow(State.Loading)
val state: StateFlow<State> get() = _state.asStateFlow()

//등록된 회원인지 아닌지 여부랑 아니면 등록까지 시켜주기
fun setKakaoToken(kakaoToken: KakaoToken){
_state.value = State.Loading
CoroutineScope(dispatcher.ui()).launch {
val result: Result<Boolean> = loginUseCase.signUp(kakaoToken)
Log.d("adasdas",result.toString())
if(result.isSuccess){
_state.value = State.Success
} else {
_state.value = State.Failure
}
}
}


}
20 changes: 10 additions & 10 deletions app/src/main/java/com/ftw/hometerview/ui/splash/SplashViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ class SplashViewModel(
val state: StateFlow<State> get() = _state.asStateFlow()

init {
CoroutineScope(dispatcher.ui()).launch {
val result: Flow<Result<String>> = withContext(dispatcher.io()) {
loginUseCase()
}

result.collect {
if (it.isSuccess) _state.value = State.Success
else _state.value = State.Failure
}
}
// CoroutineScope(dispatcher.ui()).launch {
// val result: Flow<Result<String>> = withContext(dispatcher.io()) {
// loginUseCase()
// }
//
// result.collect {
// if (it.isSuccess) _state.value = State.Success
// else _state.value = State.Failure
// }
// }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.ftw.data.datasource

import com.ftw.domain.entity.JWTToken
import com.ftw.domain.entity.KakaoToken

interface LoginDataSource {
suspend fun login(token: String): Result<String>
suspend fun setKakaoToken(kakaoToken: KakaoToken): Result<JWTToken>
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.ftw.data.datasource

import com.ftw.domain.entity.JWTToken
import kotlinx.coroutines.flow.Flow

interface TokenDataSource {
suspend fun set(name: String)
suspend fun set(jwtToken: JWTToken)
suspend fun get(): Flow<Result<String>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import androidx.datastore.preferences.core.stringPreferencesKey
import com.ftw.data.datasource.TokenDataSource
import com.ftw.data.local.datastore.DataStoreKeys
import com.ftw.data.local.datastore.DataStoreProvider
import com.ftw.domain.entity.JWTToken
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

class TokenLocalDataSource(private val provider: DataStoreProvider) : TokenDataSource {

private val key = stringPreferencesKey(DataStoreKeys.USER_TOKEN)
private val accessKey = stringPreferencesKey(DataStoreKeys.USER_ACCESS_TOKEN)
private val refreshKey = stringPreferencesKey(DataStoreKeys.USER_REFRESH_TOKEN)

override suspend fun set(name: String) {
provider.setValue(key, name)
override suspend fun set(jwtToken: JWTToken) {
provider.setValue(accessKey, jwtToken.accessToken)
provider.setValue(refreshKey,jwtToken.refreshToken)
}

override suspend fun get(): Flow<Result<String>> {
return provider.getValue(key).map { token ->
return provider.getValue(accessKey).map { token ->
token?.let { Result.success(it) }
?: Result.failure(NoSuchFieldError("Getting token is failed"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ package com.ftw.data.local.datastore


object DataStoreKeys {
const val USER_TOKEN = "user_token"
const val USER_ACCESS_TOKEN = "user_access_token"
const val USER_REFRESH_TOKEN = "user_refresh_token"
}
3 changes: 0 additions & 3 deletions data/src/main/java/com/ftw/data/remote/api/LoginAPI.kt

This file was deleted.

14 changes: 14 additions & 0 deletions data/src/main/java/com/ftw/data/remote/api/SignUpAPI.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.ftw.data.remote.api

import com.ftw.data.remote.request.KakaoTokenReq
import com.ftw.data.remote.response.RemoteResponse
import retrofit2.Response
import retrofit2.http.*

interface SignUpAPI {

@POST("/api/v1/auth/login/kakao")
suspend fun create(
@Body kakaoTokenReq: KakaoTokenReq
): Response<RemoteResponse<Nothing>>
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,29 @@
package com.ftw.data.remote.datasource

import com.ftw.data.datasource.LoginDataSource
import com.ftw.data.remote.api.LoginAPI
import com.ftw.data.remote.api.SignUpAPI
import com.ftw.data.remote.exception.ResponseException
import com.ftw.data.remote.request.KakaoTokenReq
import com.ftw.domain.entity.JWTToken
import com.ftw.domain.entity.KakaoToken

class LoginRemoteDataSource(private val api: LoginAPI) : LoginDataSource {
override suspend fun login(token: String): Result<String> {
return Result.success("")
class LoginRemoteDataSource(private val api: SignUpAPI) : LoginDataSource {
override suspend fun setKakaoToken(kakaoToken: KakaoToken): Result<JWTToken> {
return try {
val response = api.create(KakaoTokenReq(kakaoToken.accessToken,kakaoToken.refreshToken))
if (response.isSuccessful) {
val accessToken = response.headers().get("Authorization-Access-Token")
val refreshToken = response.headers().get("Authorization-Refresh-Token")
if(accessToken != null && refreshToken != null){
return Result.success(JWTToken(accessToken,refreshToken))
}else {
throw ResponseException("Token is Null Exception")
}
} else {
throw ResponseException("Network Exception")
}
} catch (e : Exception) {
return Result.failure(e)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ftw.data.remote.request

import com.google.gson.annotations.SerializedName

data class KakaoTokenReq (
@SerializedName("accessToken") val accessToken : String,
@SerializedName("refreshToken") val refreshToken : String
)
Loading

0 comments on commit e8cd963

Please sign in to comment.