diff --git a/.github/workflows/ucmcCI.yml b/.github/workflows/ucmcCI.yml
index c07316de..3f1c4afe 100644
--- a/.github/workflows/ucmcCI.yml
+++ b/.github/workflows/ucmcCI.yml
@@ -22,6 +22,10 @@ jobs:
run: chmod +x gradlew
- name: Create google-service
run: echo '${{ secrets.GOOGLE_SERVICES_JSON }}' > ./data/google-services.json
+ - name: Create data dir
+ run: mkdir ./presentation/src/main/java/com/gta/presentation/secret
+ - name: Create Secrets
+ run: echo '${{ secrets.CONSTANTS }}' > ./presentation/src/main/java/com/gta/presentation/secret/Secrets.kt
- name: Build with Gradle
run: ./gradlew build
- name: Build Signed APK
diff --git a/.gitignore b/.gitignore
index 90c042b3..01ee1747 100644
--- a/.gitignore
+++ b/.gitignore
@@ -88,3 +88,4 @@ lint/tmp/
# Mac
*.DS_Store
+/presentation/src/main/java/com/gta/presentation/secret/Secrets.kt
diff --git a/data/src/androidTest/java/com/gta/data/ExampleInstrumentedTest.kt b/data/src/androidTest/java/com/gta/data/ExampleInstrumentedTest.kt
deleted file mode 100644
index cc0b4f0e..00000000
--- a/data/src/androidTest/java/com/gta/data/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.gta.data
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.gta.data.test", appContext.packageName)
- }
-}
diff --git a/presentation/build.gradle b/presentation/build.gradle
index 8a541505..ee46fc8a 100644
--- a/presentation/build.gradle
+++ b/presentation/build.gradle
@@ -43,6 +43,11 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.5.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ implementation 'com.shobhitpuri.custombuttons:google-signin:1.1.0'
+
+ implementation platform('com.google.firebase:firebase-bom:31.0.2')
+ implementation 'com.google.firebase:firebase-auth-ktx'
+ implementation 'com.google.android.gms:play-services-auth:20.3.0'
// Test
testImplementation 'junit:junit:4.13.2'
diff --git a/presentation/src/main/AndroidManifest.xml b/presentation/src/main/AndroidManifest.xml
index c67e4368..731daca2 100644
--- a/presentation/src/main/AndroidManifest.xml
+++ b/presentation/src/main/AndroidManifest.xml
@@ -3,7 +3,7 @@
@@ -22,4 +22,4 @@
-
\ No newline at end of file
+
diff --git a/presentation/src/main/java/com/gta/presentation/di/FirebaseAuthModule.kt b/presentation/src/main/java/com/gta/presentation/di/FirebaseAuthModule.kt
new file mode 100644
index 00000000..3906a535
--- /dev/null
+++ b/presentation/src/main/java/com/gta/presentation/di/FirebaseAuthModule.kt
@@ -0,0 +1,20 @@
+package com.gta.presentation.di
+
+import com.google.firebase.auth.FirebaseAuth
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import java.util.Locale
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object FirebaseAuthModule {
+
+ @Singleton
+ @Provides
+ fun provideFirebaseAuth() = FirebaseAuth.getInstance().apply {
+ setLanguageCode(Locale.getDefault().language)
+ }
+}
diff --git a/presentation/src/main/java/com/gta/presentation/di/FirebaseSigninModule.kt b/presentation/src/main/java/com/gta/presentation/di/FirebaseSigninModule.kt
new file mode 100644
index 00000000..46b79ea8
--- /dev/null
+++ b/presentation/src/main/java/com/gta/presentation/di/FirebaseSigninModule.kt
@@ -0,0 +1,26 @@
+package com.gta.presentation.di
+
+import android.content.Context
+import com.google.android.gms.auth.api.signin.GoogleSignIn
+import com.google.android.gms.auth.api.signin.GoogleSignInOptions
+import com.gta.presentation.secret.FIREBASE_CLIENT_ID
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityComponent
+import dagger.hilt.android.qualifiers.ActivityContext
+
+@Module
+@InstallIn(ActivityComponent::class)
+class FirebaseSigninModule {
+ @Provides
+ fun provideGoogleSignInOptions() = GoogleSignInOptions
+ .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
+ .requestIdToken(FIREBASE_CLIENT_ID)
+ .requestEmail()
+ .build()
+
+ @Provides
+ fun provideGoogleSignInClient(@ActivityContext context: Context, options: GoogleSignInOptions) =
+ GoogleSignIn.getClient(context, options)
+}
diff --git a/presentation/src/main/java/com/gta/presentation/ui/LoginActivity.kt b/presentation/src/main/java/com/gta/presentation/ui/LoginActivity.kt
deleted file mode 100644
index 128bd1bd..00000000
--- a/presentation/src/main/java/com/gta/presentation/ui/LoginActivity.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.gta.presentation.ui
-
-import android.content.Intent
-import android.os.Bundle
-import com.gta.presentation.databinding.ActivityLoginBinding
-import com.gta.presentation.ui.base.BaseActivity
-
-class LoginActivity : BaseActivity(ActivityLoginBinding::inflate) {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- startMainActivity()
- }
-
- private fun startMainActivity() {
- val intent = Intent(this, MainActivity::class.java)
- startActivity(intent)
- finish()
- }
-}
diff --git a/presentation/src/main/java/com/gta/presentation/ui/login/LoginActivity.kt b/presentation/src/main/java/com/gta/presentation/ui/login/LoginActivity.kt
new file mode 100644
index 00000000..ca54f4bd
--- /dev/null
+++ b/presentation/src/main/java/com/gta/presentation/ui/login/LoginActivity.kt
@@ -0,0 +1,76 @@
+package com.gta.presentation.ui.login
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.google.android.gms.auth.api.Auth
+import com.google.android.gms.auth.api.signin.GoogleSignInClient
+import com.gta.presentation.databinding.ActivityLoginBinding
+import com.gta.presentation.ui.MainActivity
+import com.gta.presentation.ui.base.BaseActivity
+import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class LoginActivity : BaseActivity(ActivityLoginBinding::inflate) {
+
+ @Inject
+ lateinit var googleSignInClient: GoogleSignInClient
+
+ private val viewModel: LoginViewModel by viewModels()
+
+ private val requestActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
+ if (it.resultCode == RESULT_OK) {
+ val authIntent = it.data ?: return@registerForActivityResult
+ val account = Auth.GoogleSignInApi.getSignInResultFromIntent(authIntent)?.signInAccount
+ viewModel.signinWithToken(account?.idToken)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ initCollector()
+ binding.btnLoginGoogle.setOnClickListener {
+ googleLogin()
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ viewModel.checkLoginState()
+ }
+
+ private fun initCollector() {
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ viewModel.loginEvent.collectLatest { state ->
+ /*
+ 로그인에 성공 했다면
+ 1. real time db에 회원정보가 저장되어있는지 확인
+ 2. 있으면 바로 화면 전환
+ 3. 없으면 db에 회원정보 생성 후 화면 전환
+ */
+ if (state) {
+ startMainActivity()
+ }
+ }
+ }
+ }
+ }
+
+ private fun startMainActivity() {
+ val intent = Intent(this, MainActivity::class.java)
+ startActivity(intent)
+ finish()
+ }
+
+ private fun googleLogin() {
+ requestActivity.launch(googleSignInClient.signInIntent)
+ }
+}
diff --git a/presentation/src/main/java/com/gta/presentation/ui/login/LoginViewModel.kt b/presentation/src/main/java/com/gta/presentation/ui/login/LoginViewModel.kt
new file mode 100644
index 00000000..fe1400da
--- /dev/null
+++ b/presentation/src/main/java/com/gta/presentation/ui/login/LoginViewModel.kt
@@ -0,0 +1,43 @@
+package com.gta.presentation.ui.login
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.google.firebase.auth.FirebaseAuth
+import com.google.firebase.auth.GoogleAuthProvider
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import javax.inject.Inject
+
+@HiltViewModel
+class LoginViewModel @Inject constructor(
+ private val auth: FirebaseAuth
+) : ViewModel() {
+
+ private val _loginEvent = MutableSharedFlow()
+ val loginEvent: SharedFlow get() = _loginEvent
+
+ fun checkLoginState() {
+ emitLoginEvent(auth.currentUser != null)
+ }
+
+ fun signinWithToken(token: String?) {
+ token ?: return
+ val credential = GoogleAuthProvider.getCredential(token, null)
+ auth.signInWithCredential(credential).addOnCompleteListener { task ->
+ if (task.isSuccessful) {
+ emitLoginEvent(true)
+ } else {
+ Timber.e(task.exception)
+ }
+ }
+ }
+
+ private fun emitLoginEvent(state: Boolean) {
+ viewModelScope.launch {
+ _loginEvent.emit(state)
+ }
+ }
+}
diff --git a/presentation/src/main/res/drawable/ic_logo.xml b/presentation/src/main/res/drawable/ic_logo.xml
new file mode 100644
index 00000000..2e96ac45
--- /dev/null
+++ b/presentation/src/main/res/drawable/ic_logo.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/presentation/src/main/res/layout/activity_login.xml b/presentation/src/main/res/layout/activity_login.xml
index de4edd1e..2de709a1 100644
--- a/presentation/src/main/res/layout/activity_login.xml
+++ b/presentation/src/main/res/layout/activity_login.xml
@@ -4,6 +4,42 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:context=".ui.LoginActivity">
+ tools:context=".ui.login.LoginActivity">
-
\ No newline at end of file
+
+
+
+
+
+
+
+