diff --git a/app/.idea/.gitignore b/app/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/app/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/app/.idea/gradle.xml b/app/.idea/gradle.xml new file mode 100644 index 00000000..0364d75f --- /dev/null +++ b/app/.idea/gradle.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/app/.idea/misc.xml b/app/.idea/misc.xml new file mode 100644 index 00000000..a318cae6 --- /dev/null +++ b/app/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/app/.idea/vcs.xml b/app/.idea/vcs.xml new file mode 100644 index 00000000..6c0b8635 --- /dev/null +++ b/app/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3248bed4..843b6fd0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,6 +27,8 @@ android { buildConfigField("String", "BASE_URL", getBaseUrl("BASE_URL")) buildConfigField("String", "NAVER_CLIENT_ID", getBaseUrl("NAVER_CLIENT_ID")) buildConfigField("String", "NAVER_CLIENT_SECRET", getBaseUrl("NAVER_CLIENT_SECRET")) + buildConfigField("String", "KAKAO_NATIVE_APP_KEY", "\"${getBaseUrl("KAKAO_NATIVE_APP_KEY")}\"") + manifestPlaceholders["kakaoKey"] = "kakao${getBaseUrl("KAKAO_NATIVE_APP_KEY")}" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3a0df392..b2322d17 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,6 +32,20 @@ + + + + + + + + + + + diff --git a/app/src/main/java/com/example/numberoneproject/data/network/ApiService.kt b/app/src/main/java/com/example/numberoneproject/data/network/ApiService.kt index cc1fd0d4..1382df0a 100644 --- a/app/src/main/java/com/example/numberoneproject/data/network/ApiService.kt +++ b/app/src/main/java/com/example/numberoneproject/data/network/ApiService.kt @@ -22,6 +22,12 @@ interface ApiService { @Body body: TokenRequestBody ): ApiResult + //카카오 로그인 + @POST("/token/kakao") + suspend fun userKakaoLogin( + @Body body: TokenRequestBody + ): ApiResult + // 로그인 테스트 @GET("/api/logintest") suspend fun apiLoginTest( diff --git a/app/src/main/java/com/example/numberoneproject/data/repositoryimpl/LoginRepositoryImpl.kt b/app/src/main/java/com/example/numberoneproject/data/repositoryimpl/LoginRepositoryImpl.kt index fa24055c..610ec2c8 100644 --- a/app/src/main/java/com/example/numberoneproject/data/repositoryimpl/LoginRepositoryImpl.kt +++ b/app/src/main/java/com/example/numberoneproject/data/repositoryimpl/LoginRepositoryImpl.kt @@ -22,6 +22,10 @@ class LoginRepositoryImpl @Inject constructor( return service.userNaverLogin(naverLoginBody) } + override suspend fun userKakaoLogin(kakaoLoginBody: TokenRequestBody): ApiResult{ + return service.userKakaoLogin(kakaoLoginBody) + } + override suspend fun testLogin(token: String): ApiResult { return service.apiLoginTest(token) } diff --git a/app/src/main/java/com/example/numberoneproject/domain/repository/LoginRepository.kt b/app/src/main/java/com/example/numberoneproject/domain/repository/LoginRepository.kt index e55be3bb..8aafcc60 100644 --- a/app/src/main/java/com/example/numberoneproject/domain/repository/LoginRepository.kt +++ b/app/src/main/java/com/example/numberoneproject/domain/repository/LoginRepository.kt @@ -8,5 +8,6 @@ import com.example.numberoneproject.data.network.ApiResult interface LoginRepository { suspend fun refreshAccessToken(body: TokenRequestBody): ApiResult suspend fun userNaverLogin(naverLoginBody: TokenRequestBody): ApiResult + suspend fun userKakaoLogin(kakaoLoginBody: TokenRequestBody) : ApiResult suspend fun testLogin(token: String) : ApiResult } \ No newline at end of file diff --git a/app/src/main/java/com/example/numberoneproject/domain/usecase/KakaoLoginUsecase.kt b/app/src/main/java/com/example/numberoneproject/domain/usecase/KakaoLoginUsecase.kt new file mode 100644 index 00000000..2a1c5984 --- /dev/null +++ b/app/src/main/java/com/example/numberoneproject/domain/usecase/KakaoLoginUsecase.kt @@ -0,0 +1,16 @@ +package com.example.numberoneproject.domain.usecase + +import com.example.numberoneproject.data.model.LoginTokenResponse +import com.example.numberoneproject.data.model.TokenRequestBody +import com.example.numberoneproject.data.network.ApiResult +import com.example.numberoneproject.domain.repository.LoginRepository +import javax.inject.Inject +class KakaoLoginUsecase @Inject constructor( + private val loginRepository: LoginRepository +) { + suspend operator fun invoke( + loginBody: TokenRequestBody + ) : ApiResult{ + return loginRepository.userKakaoLogin(loginBody) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/numberoneproject/presentation/di/MyApplication.kt b/app/src/main/java/com/example/numberoneproject/presentation/di/MyApplication.kt index d4ca38d8..1422b2a7 100644 --- a/app/src/main/java/com/example/numberoneproject/presentation/di/MyApplication.kt +++ b/app/src/main/java/com/example/numberoneproject/presentation/di/MyApplication.kt @@ -1,9 +1,15 @@ package com.example.numberoneproject.presentation.di import android.app.Application +import com.example.numberoneproject.BuildConfig +import com.kakao.sdk.common.KakaoSdk import dagger.hilt.android.HiltAndroidApp @HiltAndroidApp class MyApplication: Application() { - + val KAKAO = BuildConfig.KAKAO_NATIVE_APP_KEY + override fun onCreate() { + super.onCreate() + KakaoSdk.init(this,KAKAO) + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/numberoneproject/presentation/view/LoginActivity.kt b/app/src/main/java/com/example/numberoneproject/presentation/view/LoginActivity.kt index a9ad3ada..6edf4a3c 100644 --- a/app/src/main/java/com/example/numberoneproject/presentation/view/LoginActivity.kt +++ b/app/src/main/java/com/example/numberoneproject/presentation/view/LoginActivity.kt @@ -4,7 +4,9 @@ import android.content.Intent import android.os.Bundle import android.util.Log import android.view.View +import android.widget.Toast import androidx.activity.viewModels +import androidx.lifecycle.lifecycleScope import com.example.numberoneproject.BuildConfig import com.example.numberoneproject.R import com.example.numberoneproject.data.model.TokenRequestBody @@ -14,6 +16,10 @@ import com.example.numberoneproject.presentation.base.BaseActivity import com.example.numberoneproject.presentation.util.Extensions.repeatOnStarted import com.example.numberoneproject.presentation.util.TokenManager import com.example.numberoneproject.presentation.viewmodel.LoginViewModel +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.navercorp.nid.NaverIdLoginSDK import com.navercorp.nid.oauth.NidOAuthLogin import com.navercorp.nid.oauth.OAuthLoginCallback @@ -21,6 +27,8 @@ import com.navercorp.nid.profile.NidProfileCallback import com.navercorp.nid.profile.data.NidProfileResponse import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch @AndroidEntryPoint class LoginActivity : BaseActivity(R.layout.activity_login) { @@ -106,4 +114,33 @@ class LoginActivity : BaseActivity(R.layout.activity_login NaverIdLoginSDK.authenticate(this, oauthLoginCallback) } + + fun setupKakaoLogin(view:View){ + //카카오 계정 로그인 + val callback : (OAuthToken?, Throwable?) -> Unit = {token, error-> + if(error != null){ + Toast.makeText(this,"카카오계정 로그인 실패 ${error}",Toast.LENGTH_SHORT).show() + } + else if(token != null){ + loginVM.userKakaoLogin(TokenRequestBody(token.accessToken)) + } + } + //카카오톡 어플있다면 카카오톡 로그인 시도 + if(UserApiClient.instance.isKakaoTalkLoginAvailable(this)){ + UserApiClient.instance.loginWithKakaoTalk(this){token, error-> + if(error != null){ + Toast.makeText(this,"카카오톡 로그인 실패 ${error}",Toast.LENGTH_SHORT).show() + if(error is ClientError && error.reason == ClientErrorCause.Cancelled){ + return@loginWithKakaoTalk + } + UserApiClient.instance.loginWithKakaoAccount(this, callback = callback) + }else if(token != null){ + loginVM.userKakaoLogin(TokenRequestBody(token.accessToken)) + } + } + } + else{ + UserApiClient.instance.loginWithKakaoAccount(this, callback=callback) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/numberoneproject/presentation/view/SplashActivity.kt b/app/src/main/java/com/example/numberoneproject/presentation/view/SplashActivity.kt index cadd8852..c1529f5f 100644 --- a/app/src/main/java/com/example/numberoneproject/presentation/view/SplashActivity.kt +++ b/app/src/main/java/com/example/numberoneproject/presentation/view/SplashActivity.kt @@ -10,20 +10,27 @@ import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Handler import android.os.Looper +import android.util.Log import android.widget.Toast +import androidx.lifecycle.lifecycleScope import com.example.numberoneproject.R +import com.example.numberoneproject.presentation.util.Extensions.repeatOnStarted +import com.example.numberoneproject.presentation.util.TokenManager +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch class SplashActivity : AppCompatActivity() { private val DURATION_TIME = 2000L // 스플래시 화면 지연시간 private lateinit var cm2 : ConnectivityManager + private val tokenManager: TokenManager = TokenManager(this) private val networkCallBack = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { // 네트워크가 정상적인 경우 Toast.makeText(this@SplashActivity, "연결성공 $network", Toast.LENGTH_SHORT).show() - + checkLogin() Handler(Looper.getMainLooper()).postDelayed({ - startActivity(Intent( this@SplashActivity,LoginActivity::class.java)) + //startActivity(Intent( this@SplashActivity,LoginActivity::class.java)) finish() }, DURATION_TIME) } @@ -38,12 +45,29 @@ class SplashActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_splash) + val builder = NetworkRequest.Builder() cm2 = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager cm2.registerNetworkCallback(builder.build(),networkCallBack) } + //자동로그인 가능한지 확인 + fun checkLogin(){ + lifecycleScope.launch{ + tokenManager.accessToken.collectLatest { + if(it.isNotEmpty()){ + val intent = Intent(this@SplashActivity,MainActivity::class.java) + startActivity(intent) + } + else{ + val intent = Intent(this@SplashActivity,LoginActivity::class.java) + startActivity(intent) + } + } + } + } + override fun onDestroy() { super.onDestroy() cm2 = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager diff --git a/app/src/main/java/com/example/numberoneproject/presentation/viewmodel/LoginViewModel.kt b/app/src/main/java/com/example/numberoneproject/presentation/viewmodel/LoginViewModel.kt index 98822347..170c7037 100644 --- a/app/src/main/java/com/example/numberoneproject/presentation/viewmodel/LoginViewModel.kt +++ b/app/src/main/java/com/example/numberoneproject/presentation/viewmodel/LoginViewModel.kt @@ -7,6 +7,7 @@ import com.example.numberoneproject.data.model.TokenRequestBody import com.example.numberoneproject.data.network.ApiResult import com.example.numberoneproject.data.network.onFailure import com.example.numberoneproject.data.network.onSuccess +import com.example.numberoneproject.domain.usecase.KakaoLoginUsecase import com.example.numberoneproject.domain.usecase.NaverLoginUseCase import com.example.numberoneproject.domain.usecase.RefreshAccessTokenUseCase import com.example.numberoneproject.domain.usecase.TestUseCase @@ -22,6 +23,7 @@ import javax.inject.Inject class LoginViewModel @Inject constructor( private val tokenManager: TokenManager, private val naverLoginUseCase: NaverLoginUseCase, + private val kakaoLoginUsecase: KakaoLoginUsecase, private val testLoginUseCase: TestUseCase, private val refreshAccessTokenUseCase: RefreshAccessTokenUseCase ) : ViewModel() { @@ -41,6 +43,18 @@ class LoginViewModel @Inject constructor( } } + fun userKakaoLogin(loginBody: TokenRequestBody) { + viewModelScope.launch { + kakaoLoginUsecase(loginBody) + .onSuccess { + tokenManager.writeLoginTokens(it.accessToken, it.refreshToken) + } + .onFailure { + _loginErrorState.value = it + } + } + } + /** loginTest 관련 코드는 지금은 테스트 용도고 금방 지워질 코드 **/ fun loginTest() { viewModelScope.launch { diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index f8cb6ab2..42143d6c 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -22,7 +22,8 @@ android:textSize="24sp" android:textStyle="bold" android:textColor="#FFEB3B" - android:text="카카오 로그인" /> + android:text="카카오 로그인" + android:onClick="@{(v) -> activity.setupKakaoLogin(v)}"/>