Skip to content

Commit

Permalink
Merge pull request #77 from Team-HMH/refact/kakao_login
Browse files Browse the repository at this point in the history
[refact/kakao login]: 카카오 로그인 코드 리팩토링
  • Loading branch information
kez-lab authored Jan 11, 2024
2 parents 2d6b3b9 + a45593f commit 9eb9fed
Show file tree
Hide file tree
Showing 27 changed files with 212 additions and 274 deletions.
1 change: 0 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ android {
dependencies {

// Feature
implementation(projects.feature.statistics)
implementation(projects.feature.login)
implementation(projects.feature.onboarding)
implementation(projects.feature.main)
Expand Down
21 changes: 9 additions & 12 deletions app/src/main/java/com/hmh/hamyeonham/SampleActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import androidx.core.splashscreen.SplashScreen
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.hmh.hamyeonham.common.view.viewBinding
import com.hmh.hamyeonham.databinding.ActivitySampleBinding
import com.hmh.hamyeonham.feature.main.MainActivity
import com.hmh.hamyeonham.feature.login.LoginActivity
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -21,8 +21,7 @@ class SampleActivity : AppCompatActivity() {
val splashScreen = installSplashScreen()
initSplashAnimation(splashScreen)
setContentView(binding.root)
startActivity(Intent(this, MainActivity::class.java))
// startActivity(Intent(this, StaticsActivity::class.java))
startActivity(Intent(this, LoginActivity::class.java))
finish()
}

Expand All @@ -31,16 +30,14 @@ class SampleActivity : AppCompatActivity() {
val splashScreenView = splashScreenViewProvider.view
val fadeOut = AnimationUtils.loadAnimation(this, R.anim.fade_out)

fadeOut.setAnimationListener(
object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
splashScreenViewProvider.remove()
}
fadeOut.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {
splashScreenViewProvider.remove()
}

override fun onAnimationRepeat(animation: Animation) {}
},
)
override fun onAnimationRepeat(animation: Animation) {}
})
splashScreenView.startAnimation(fadeOut)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,18 @@ import android.content.Context
import android.content.Intent
import com.hmh.hamyeonham.common.navigation.NavigationProvider
import com.hmh.hamyeonham.feature.login.LoginActivity
import com.hmh.hamyeonham.feature.login.UserInfoActivity
import com.hmh.hamyeonham.feature.main.MainActivity
import com.hmh.hamyeonham.feature.onboarding.OnBoardingActivity
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject

class DefaultNavigationProvider @Inject constructor(
@ApplicationContext private val context: Context
@ApplicationContext private val context: Context,
) : NavigationProvider {
override fun toOnboarding(): Intent {
override fun toOnBoarding(): Intent {
return Intent(context, OnBoardingActivity::class.java)
}

override fun toLogin(): Intent {
return Intent(context, LoginActivity::class.java)
}

override fun toUserInfo(): Intent {
return Intent(context, UserInfoActivity::class.java)
}

override fun toMain(): Intent {
return Intent(context, MainActivity::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package com.hmh.hamyeonham.common.navigation
import android.content.Intent

interface NavigationProvider {
fun toOnboarding(): Intent
fun toOnBoarding(): Intent
fun toLogin(): Intent
fun toUserInfo(): Intent

fun toMain(): Intent
}
2 changes: 2 additions & 0 deletions feature/login/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ dependencies {

// dot indicator
implementation(libs.dot.indicator)
implementation(projects.core.designsystem)

}
3 changes: 0 additions & 3 deletions feature/login/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<activity
android:name=".UserInfoActivity"
android:exported="false" />
<activity
android:name=".LoginActivity"
android:exported="false" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,98 +1,66 @@
package com.hmh.hamyeonham.feature.login

import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.hmh.hamyeonham.common.context.toast
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import com.hmh.hamyeonham.common.navigation.NavigationProvider
import com.hmh.hamyeonham.feature.login.data.DummyImage
import com.hmh.hamyeonham.common.view.viewBinding
import com.hmh.hamyeonham.feature.login.databinding.ActivityLoginBinding
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.flow.launchIn
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject

@AndroidEntryPoint
class LoginActivity : AppCompatActivity() {
private lateinit var binding: ActivityLoginBinding

private val loginViewModel: LoginViewModel by viewModels()
private val binding by viewBinding(ActivityLoginBinding::inflate)
private val viewModel by viewModels<LoginViewModel>()
private lateinit var loginViewPagerAdapter: LoginViewPagerAdapter

@Inject
lateinit var navigationProvider: NavigationProvider

// 삭제 예정
private val dummyImageList = listOf(
DummyImage(
Image = R.drawable.login_sample_rectagle_viewpager,
),
DummyImage(
Image = R.drawable.login_sample_rectagle_viewpager,
),
DummyImage(
Image = R.drawable.login_sample_rectagle_viewpager,
),
)

private val callback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
when {
error != null -> {
}

token != null -> {
moveToMainActivity()
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivityLoginBinding.inflate(layoutInflater)
super.onCreate(savedInstanceState)
setContentView(binding.root)

binding.btnLogin.setOnClickListener {
loginWithKakaoApp()
binding.ivKakaoLogin.setOnClickListener {
viewModel.loginWithKakaoApp(this)
}
setLoginViewPager()
handleKakaoLoginSuccess()
}

private fun handleKakaoLoginSuccess() {
viewModel.kakaoLoginState.flowWithLifecycle(lifecycle).onEach { state ->
if (state.isSuccessResult) {
viewModel.getKakaoUserNickname()
// 닉네임 출력
Log.d("LoginActivity", "닉네임: ${state.kakaoNickname}")
moveToOnBoardingActivity()
}
}.launchIn(lifecycleScope)
}

private fun setLoginViewPager() {
loginViewPagerAdapter = LoginViewPagerAdapter(dummyImageList)
val loginViewImageList = listOf(
R.drawable.login_sample_rectagle_viewpager,
R.drawable.login_sample_rectagle_viewpager,
R.drawable.login_sample_rectagle_viewpager,
)

loginViewPagerAdapter = LoginViewPagerAdapter(loginViewImageList)
binding.run {
vpLogin.adapter = loginViewPagerAdapter
indicatorLoginDots.attachTo(binding.vpLogin)
}
}

private fun loginWithKakaoApp() {
if (UserApiClient.instance.isKakaoTalkLoginAvailable(this)) {
UserApiClient.instance.loginWithKakaoTalk(this) { token, error ->
if (error != null) {
toast("카카오 로그인 실패")
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
toast("다시 로그인 해주세요.")
return@loginWithKakaoTalk
}
loginWithKakaoAccount()
} else if (token != null) {
toast("카카오 로그인 성공")
moveToMainActivity()
}
}
} else {
loginWithKakaoAccount()
}
}

private fun loginWithKakaoAccount() {
UserApiClient.instance.loginWithKakaoAccount(this, callback = callback)
}

private fun moveToMainActivity() {
startActivity(navigationProvider.toMain())
private fun moveToOnBoardingActivity() {
startActivity(navigationProvider.toOnBoarding())
finish()
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,90 @@
package com.hmh.hamyeonham.feature.login

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import android.content.Context
import androidx.lifecycle.ViewModel
import com.kakao.sdk.common.model.ClientError
import com.kakao.sdk.common.model.ClientErrorCause
import com.kakao.sdk.user.UserApiClient
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject

class LoginViewModel : ViewModel(){
@HiltViewModel
class LoginViewModel @Inject constructor() : ViewModel() {
data class KakaoLoginState(
val isSuccessResult: Boolean = false,
val accessToken: String? = null,
val refreshToken: String? = null,
val kakaoNickname: String? = null,
)

private val _kakaoLoginResult = MutableLiveData<Boolean>()
val kakaoLoginResult: LiveData<Boolean> get() = _kakaoLoginResult
private val _kakaoLoginState = MutableStateFlow(KakaoLoginState())
val kakaoLoginState = _kakaoLoginState.asStateFlow()

private fun updateState(transform: KakaoLoginState.() -> KakaoLoginState) {
val currentState = kakaoLoginState.value
val newState = currentState.transform()
_kakaoLoginState.value = newState
}

fun loginWithKakaoApp(context: Context) {
if (UserApiClient.instance.isKakaoTalkLoginAvailable(context)) {
UserApiClient.instance.loginWithKakaoTalk(context) { token, error ->
if (error != null) {
if (error is ClientError && error.reason == ClientErrorCause.Cancelled) {
return@loginWithKakaoTalk
}
loginWithKakaoAccount(context)
} else if (token != null) {
updateState {
copy(
isSuccessResult = true,
accessToken = token.accessToken,
refreshToken = token.refreshToken,
)
}
}
}
} else {
loginWithKakaoAccount(context)
}
}

private fun loginWithKakaoAccount(context: Context) {
UserApiClient.instance.loginWithKakaoAccount(context) { token, error ->
if (error != null) {
updateState {
copy(
isSuccessResult = false,
accessToken = "",
refreshToken = "",
)
}
} else if (token != null) {
updateState {
copy(
isSuccessResult = true,
accessToken = token.accessToken,
refreshToken = token.refreshToken,
)
}
}
}
}

fun getKakaoUserNickname() {
UserApiClient.instance.me { user, error ->
if (error != null) {
// 닉네임 정보 얻기 실패 시
} else if (user != null) {
val kakaoNickname = user.kakaoAccount?.profile?.nickname
updateState {
copy(
kakaoNickname = kakaoNickname,
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import coil.load
import com.hmh.hamyeonham.feature.login.data.DummyImage
import com.hmh.hamyeonham.feature.login.databinding.ItemLoginViewPagerBinding

class LoginViewPagerAdapter(private val imageList: List<DummyImage>) :
class LoginViewPagerAdapter(private val imageList: List<Int>) :
RecyclerView.Adapter<LoginViewPagerAdapter.PagerViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PagerViewHolder {
Expand All @@ -19,9 +18,9 @@ class LoginViewPagerAdapter(private val imageList: List<DummyImage>) :
class PagerViewHolder(private val binding: ItemLoginViewPagerBinding) :
RecyclerView.ViewHolder(binding.root) {

fun onBindView(imageInfo: DummyImage) {
fun onBindView(imageInfo: Int) {
binding.run {
ivLoginViewPagerItem.load(imageInfo.Image) {
ivLoginViewPagerItem.load(imageInfo) {
placeholder(R.drawable.login_sample_rectagle_viewpager)
error(R.drawable.login_sample_rectagle_viewpager)
}
Expand Down
Loading

0 comments on commit 9eb9fed

Please sign in to comment.