diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index d600c635..bf2077f2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -27,14 +27,21 @@ android {
"BASE_URL",
gradleLocalProperties(rootDir).getProperty("base.url"),
)
+ buildConfigField(
+ "String",
+ "NATIVE_APP_KEY",
+ gradleLocalProperties(rootDir).getProperty("native.app.key"),
+ )
+ manifestPlaceholders["NATIVE_APP_KEY"] =
+ gradleLocalProperties(rootDir).getProperty("native.app.key")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro",
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro",
)
}
}
@@ -95,4 +102,8 @@ dependencies {
implementation(timber)
implementation(ossLicense)
}
-}
\ No newline at end of file
+
+ KakaoDependencies.run {
+ implementation(user)
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2f2fb6c8..cd351e75 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,6 +15,21 @@
android:usesCleartextTraffic="true"
tools:targetApi="31">
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/java/com/going/going/MyApp.kt b/app/src/main/java/com/going/going/MyApp.kt
index 766cf55b..6ac307d0 100644
--- a/app/src/main/java/com/going/going/MyApp.kt
+++ b/app/src/main/java/com/going/going/MyApp.kt
@@ -2,6 +2,7 @@ package com.going.going
import android.app.Application
import androidx.appcompat.app.AppCompatDelegate
+import com.kakao.sdk.common.KakaoSdk
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber
@@ -13,6 +14,7 @@ class MyApp : Application() {
initTimber()
setDayMode()
+ initKakaoSdk()
}
private fun initTimber() {
@@ -23,4 +25,7 @@ class MyApp : Application() {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
-}
\ No newline at end of file
+ private fun initKakaoSdk() {
+ KakaoSdk.init(this, BuildConfig.NATIVE_APP_KEY)
+ }
+}
diff --git a/build.gradle.kts b/build.gradle.kts
index b5df03ba..42b23205 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -14,4 +14,4 @@ buildscript {
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt
index b8d6f325..3eb9818f 100644
--- a/buildSrc/src/main/kotlin/Constants.kt
+++ b/buildSrc/src/main/kotlin/Constants.kt
@@ -5,4 +5,4 @@ object Constants {
const val targetSdk = 34
const val versionCode = 1
const val versionName = "1.0"
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt
index d8043bfa..597f044a 100644
--- a/buildSrc/src/main/kotlin/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/Dependencies.kt
@@ -92,4 +92,8 @@ object FirebaseDependencies {
const val crashlytics = "com.google.firebase:firebase-crashlytics-ktx"
const val analytics = "com.google.firebase:firebase-analytics-ktx"
const val remoteConfig = "com.google.firebase:firebase-config-ktx"
-}
\ No newline at end of file
+}
+
+object KakaoDependencies {
+ const val user = "com.kakao.sdk:v2-user:${Versions.kakaoVersion}"
+}
diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 151e8489..d31682bb 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -33,7 +33,7 @@ object Versions {
const val balloonVersion = "1.4.5"
const val lottieVersion = "6.0.0"
const val circularProgressBar = "3.1.0"
- const val kakaoVersion = "2.14.0"
+ const val kakaoVersion = "2.19.0"
const val circleIndicatorVersion = "2.1.6"
const val shimmerVersion = "0.5.0"
const val navigationVersion = "2.6.0"
@@ -44,4 +44,4 @@ object Versions {
val javaVersion = JavaVersion.VERSION_17
const val jvmVersion = "17"
-}
\ No newline at end of file
+}
diff --git a/domain/src/main/kotlin/com/going/domain/entity/response/AuthTokenModel.kt b/domain/src/main/kotlin/com/going/domain/entity/response/AuthTokenModel.kt
new file mode 100644
index 00000000..38fe35f9
--- /dev/null
+++ b/domain/src/main/kotlin/com/going/domain/entity/response/AuthTokenModel.kt
@@ -0,0 +1,7 @@
+package com.going.domain.entity.response
+
+data class AuthTokenModel(
+ val isResigned: Boolean,
+ val accessToken: String,
+ val refreshToken: String,
+)
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 72e712f4..02222dac 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,4 +1,4 @@
-#Tue Dec 26 03:22:09 KST 2023
+#Fri Dec 29 23:45:17 KST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
diff --git a/presentation/build.gradle.kts b/presentation/build.gradle.kts
index 391da731..fa4be555 100644
--- a/presentation/build.gradle.kts
+++ b/presentation/build.gradle.kts
@@ -84,4 +84,8 @@ dependencies {
implementation(circularProgressBar)
implementation(circleIndicator)
}
-}
\ No newline at end of file
+
+ KakaoDependencies.run {
+ implementation(user)
+ }
+}
diff --git a/presentation/src/main/java/com/going/presentation/auth/LoginActivity.kt b/presentation/src/main/java/com/going/presentation/auth/LoginActivity.kt
new file mode 100644
index 00000000..32c405e7
--- /dev/null
+++ b/presentation/src/main/java/com/going/presentation/auth/LoginActivity.kt
@@ -0,0 +1,63 @@
+package com.going.presentation.auth
+
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.lifecycle.flowWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import com.going.presentation.R
+import com.going.presentation.databinding.ActivityLoginBinding
+import com.going.ui.base.BaseActivity
+import com.going.ui.extension.UiState
+import com.going.ui.extension.setOnSingleClickListener
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+class LoginActivity : BaseActivity(R.layout.activity_login) {
+ private val viewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ initKakaoLoginBtnClickListener()
+ observeInfo()
+ }
+
+ private fun initKakaoLoginBtnClickListener() {
+ binding.btnSignIn.setOnSingleClickListener {
+ viewModel.startKakaoLogIn(this)
+ }
+ }
+
+ private fun observeInfo() {
+ observeIsAppLoginAvailable()
+ observePostChangeTokenState()
+ }
+
+ private fun observeIsAppLoginAvailable() {
+ viewModel.isAppLoginAvailable.flowWithLifecycle(lifecycle).onEach { canLogin ->
+ if (!canLogin) viewModel.startKakaoLogIn(this)
+ }.launchIn(lifecycleScope)
+ }
+
+ private fun observePostChangeTokenState() {
+ viewModel.postChangeTokenState.flowWithLifecycle(lifecycle).onEach { tokenState ->
+ when (tokenState) {
+ is UiState.Success -> {
+ // 성공 했을 때 로직
+ }
+
+ is UiState.Failure -> {
+ // 실패 했을 때 로직
+ }
+
+ is UiState.Empty -> {
+ // 여튼 로직
+ }
+
+ is UiState.Loading -> {
+ // 로딩 중 로직
+ }
+ }
+ }.launchIn(lifecycleScope)
+ }
+}
diff --git a/presentation/src/main/java/com/going/presentation/auth/LoginViewModel.kt b/presentation/src/main/java/com/going/presentation/auth/LoginViewModel.kt
new file mode 100644
index 00000000..54fd0a35
--- /dev/null
+++ b/presentation/src/main/java/com/going/presentation/auth/LoginViewModel.kt
@@ -0,0 +1,82 @@
+package com.going.presentation.auth
+
+import android.content.Context
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.going.domain.entity.response.AuthTokenModel
+import com.going.ui.extension.UiState
+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 kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+class LoginViewModel : ViewModel() {
+ private val _postChangeTokenState = MutableStateFlow>(UiState.Empty)
+ val postChangeTokenState: StateFlow> = _postChangeTokenState
+
+ private val _isAppLoginAvailable = MutableStateFlow(true)
+ val isAppLoginAvailable: StateFlow = _isAppLoginAvailable
+
+ private var webLoginCallback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
+ if (error == null && token != null) {
+ changeTokenFromServer(
+ accessToken = token.accessToken,
+ )
+ }
+ }
+
+ private var appLoginCallback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
+ if (error != null) {
+ // 뒤로가기 경우 예외 처리
+ if (!(error is ClientError && error.reason == ClientErrorCause.Cancelled)) {
+ _isAppLoginAvailable.value = false
+ }
+ } else if (token != null) {
+ changeTokenFromServer(
+ accessToken = token.accessToken,
+ )
+ }
+ }
+
+ fun startKakaoLogIn(context: Context) {
+ if (UserApiClient.instance.isKakaoTalkLoginAvailable(context) && isAppLoginAvailable.value) {
+ UserApiClient.instance.loginWithKakaoTalk(
+ context = context,
+ callback = appLoginCallback,
+ )
+ } else {
+ UserApiClient.instance.loginWithKakaoAccount(
+ context = context,
+ callback = webLoginCallback,
+ )
+ }
+ }
+
+ // 서버통신 - 카카오 토큰 보내서 서비스 토큰 받아오기 - 서버와 협의 후 수정예정
+ private fun changeTokenFromServer(
+ accessToken: String,
+ social: String = KAKAO,
+ ) {
+ _postChangeTokenState.value = UiState.Loading
+
+ viewModelScope.launch {
+ // 통신 로직
+
+ // 성공시 서버에서 준 정보를 넣는 예시 코드
+ _postChangeTokenState.value = UiState.Success(
+ AuthTokenModel(
+ isResigned = true,
+ accessToken = "testAccessToekn",
+ refreshToken = "testRefreshToekn",
+ ),
+ )
+ }
+ }
+
+ companion object {
+ const val KAKAO = "KAKAO"
+ }
+}
diff --git a/presentation/src/main/java/com/going/presentation/mock/MockActivity.kt b/presentation/src/main/java/com/going/presentation/mock/MockActivity.kt
index b34761d9..f418928c 100644
--- a/presentation/src/main/java/com/going/presentation/mock/MockActivity.kt
+++ b/presentation/src/main/java/com/going/presentation/mock/MockActivity.kt
@@ -58,5 +58,4 @@ class MockActivity() : BaseActivity(R.layout.activity_mock)
super.onDestroy()
_adapter = null
}
-
-}
\ No newline at end of file
+}
diff --git a/presentation/src/main/res/drawable/img_sign_in_kakao_button.png b/presentation/src/main/res/drawable/img_sign_in_kakao_button.png
new file mode 100644
index 00000000..d94ea5d9
Binary files /dev/null and b/presentation/src/main/res/drawable/img_sign_in_kakao_button.png differ
diff --git a/presentation/src/main/res/drawable/img_sign_in_main.png b/presentation/src/main/res/drawable/img_sign_in_main.png
new file mode 100644
index 00000000..159aa2eb
Binary files /dev/null and b/presentation/src/main/res/drawable/img_sign_in_main.png differ
diff --git a/presentation/src/main/res/layout/activity_login.xml b/presentation/src/main/res/layout/activity_login.xml
new file mode 100644
index 00000000..090f1f13
--- /dev/null
+++ b/presentation/src/main/res/layout/activity_login.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml
index af32c4f5..4aa2c76e 100644
--- a/presentation/src/main/res/values/strings.xml
+++ b/presentation/src/main/res/values/strings.xml
@@ -8,4 +8,7 @@
서버 통신에 실패했습니다.
+ 여행을 시작해보세요
+ 개인정보처리방침
+
diff --git a/settings.gradle.kts b/settings.gradle.kts
index fac4e87a..b7b7240e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -11,6 +11,9 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
+
+ // KakaoSDK repository
+ maven(url = "https://devrepo.kakao.com/nexus/content/groups/public/")
}
}