Skip to content

Commit

Permalink
[AN/USER] feat: 로그인 FCM 연동 추가(#458) (#474)
Browse files Browse the repository at this point in the history
* chore: 파이어 베이스 메세지 라이브러리 추가

* feat: 입장 티켓 서비스 클래스 추가

* feat: 입장완료 메세지가 오면 화면이 나가지도록 처리

* refactor: 인증 로그인 요청 데이터 클래스 추가 후 리포지터리에 반영

* feat: 파이어베이스 fcmToken을 로그인할 때 전송하도록 변경

* feat: fcm 토큰 추가후 에러 발생 시 throw를 던지지 않도록 변경

* feat: 레거시 제거

* refactor: dev 브랜치 머지후 실행되지 않는 코드 변경

* refactor: fcm을 data layer에서 처리하도록 변경

* refactor: fcm을 data layer에서 처리하도록 변경

* refactor: isExactlyInstanceOf을 사용하여 타입 체크

* fix: 테스트코드 빌드에러 수정

* refactor: fcm 토큰을 TokenRepository에서만 접근하도록 변경

* refactor: 머지 후 생긴 문제 해결

* refactor: 추가되는 api에 대해서 에러가 발생하지 않도록 변경

* refactor: 머지전 필요없는 사항 변경

* refactor: 머지전 필요없는 사항 변경
  • Loading branch information
re4rk authored Oct 7, 2023
1 parent 52e3142 commit 11443f1
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 21 deletions.
1 change: 1 addition & 0 deletions android/festago/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ dependencies {
implementation(platform("com.google.firebase:firebase-bom:32.2.0"))
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-messaging-ktx:23.2.1")

// swiperefreshlayout
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
Expand Down
9 changes: 9 additions & 0 deletions android/festago/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@
<activity
android:name=".presentation.ui.tickethistory.TicketHistoryActivity"
android:exported="false" />

<service
android:name=".presentation.service.TicketEntryService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>

</service>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,37 @@ object ApiModule {
.addInterceptor(AuthInterceptor(tokenManager))
.build()

@Provides
@Singleton
fun provideRetrofitConverterFactory(): retrofit2.Converter.Factory {
val json = Json {
ignoreUnknownKeys = true
}
return json.asConverterFactory("application/json".toMediaType())
}

@Provides
@Singleton
@NormalRetrofitQualifier
fun providesNormalRetrofit(@BaseUrlQualifier baseUrl: String): Retrofit = Retrofit.Builder()
fun providesNormalRetrofit(
@BaseUrlQualifier baseUrl: String,
converterFactory: retrofit2.Converter.Factory,
): Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.addConverterFactory(converterFactory)
.build()

@Provides
@Singleton
@AuthRetrofitQualifier
fun providesAuthRetrofit(
@BaseUrlQualifier baseUrl: String,
okHttpClient: OkHttpClient
okHttpClient: OkHttpClient,
converterFactory: retrofit2.Converter.Factory,
): Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.addConverterFactory(converterFactory)
.build()

@Provides
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.festago.festago.data.di.singletonscope

import com.google.firebase.messaging.FirebaseMessaging
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@InstallIn(SingletonComponent::class)
@Module
object FirebaseModule {
@Provides
@Singleton
fun provideFirebaseMessaging(): FirebaseMessaging {
return FirebaseMessaging.getInstance()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ import kotlinx.serialization.Serializable
data class OauthRequest(
val socialType: String,
val accessToken: String,
val fcmToken: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import com.festago.festago.data.service.TokenRetrofitService
import com.festago.festago.data.util.onSuccessOrCatch
import com.festago.festago.data.util.runCatchingResponse
import com.festago.festago.repository.TokenRepository
import com.google.firebase.messaging.FirebaseMessaging
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await
import javax.inject.Inject

class TokenDefaultRepository @Inject constructor(
private val tokenLocalDataSource: TokenDataSource,
private val tokenRetrofitService: TokenRetrofitService,
private val firebaseMessaging: FirebaseMessaging,
) : TokenRepository {
override var token: String?
get() = tokenLocalDataSource.token
Expand All @@ -21,12 +24,14 @@ class TokenDefaultRepository @Inject constructor(

override suspend fun signIn(socialType: String, token: String): Result<Unit> =
runCatchingResponse {
tokenRetrofitService.getOauthToken(OauthRequest(socialType, token))
val fcmToken = firebaseMessaging.token.await()
tokenRetrofitService.getOauthToken(OauthRequest(socialType, token, fcmToken))
}.onSuccessOrCatch { tokenLocalDataSource.token = it.accessToken }

override fun refreshToken(token: String): Result<Unit> = runBlocking {
override fun refreshToken(socialType: String, token: String): Result<Unit> = runBlocking {
runCatchingResponse {
tokenRetrofitService.getOauthToken(OauthRequest("KAKAO", token))
val fcmToken = firebaseMessaging.token.await()
tokenRetrofitService.getOauthToken(OauthRequest(socialType, token, fcmToken))
}.onSuccessOrCatch { tokenLocalDataSource.token = it.accessToken }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ import com.festago.festago.repository.TokenRepository
import com.kakao.sdk.auth.TokenManagerProvider
import javax.inject.Inject

class TokenManager @Inject constructor(private val tokenRepository: TokenRepository) {
class TokenManager @Inject constructor(
private val tokenRepository: TokenRepository,
) {

val token: String
get() = tokenRepository.token ?: NULL_TOKEN

fun refreshToken() {
tokenRepository.refreshToken(
socialType = "KAKAO",
token = TokenManagerProvider.instance.manager.getToken()?.accessToken ?: NULL_TOKEN,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.festago.festago.presentation.service

import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.runBlocking

class TicketEntryService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
runBlocking {
// TODO: 입장완료 로직인지 확인하는 로직 추가 필요
ticketStateChangeEvent.emit(true)
}
}

override fun onNewToken(token: String) {
// TODO: 토큰이 변경되었을 때 처리
}

companion object {
val ticketStateChangeEvent: MutableSharedFlow<Boolean> = MutableSharedFlow()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.festago.festago.presentation.util.MutableSingleLiveData
import com.festago.festago.presentation.util.SingleLiveData
import com.festago.festago.repository.AuthRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -20,12 +21,18 @@ class SignInViewModel @Inject constructor(
private val _event = MutableSingleLiveData<SignInEvent>()
val event: SingleLiveData<SignInEvent> = _event

private val exceptionHandler: CoroutineExceptionHandler =
CoroutineExceptionHandler { _, throwable ->
_event.setValue(SignInEvent.SignInFailure)
analyticsHelper.logNetworkFailure(KEY_SIGN_IN_LOG, throwable.message.toString())
}

fun signInKakao() {
_event.setValue(SignInEvent.ShowSignInPage)
}

fun signIn(token: String) {
viewModelScope.launch {
viewModelScope.launch(exceptionHandler) {
authRepository.signIn(SOCIAL_TYPE_KAKAO, token)
.onSuccess {
_event.setValue(SignInEvent.SignInSuccess)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package com.festago.festago.presentation.ui.ticketentry
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import com.festago.festago.databinding.ActivityTicketEntryBinding
import com.festago.festago.presentation.service.TicketEntryService
import com.festago.festago.presentation.util.repeatOnStarted
import com.google.zxing.BarcodeFormat
import com.journeyapps.barcodescanner.BarcodeEncoder
import dagger.hilt.android.AndroidEntryPoint
Expand Down Expand Up @@ -58,6 +61,16 @@ class TicketEntryActivity : AppCompatActivity() {
}
}
}
repeatOnStarted(this) {
TicketEntryService.ticketStateChangeEvent.collect { event ->
if (event) {
// TODO 티켓 스캔 화면 처리
Toast.makeText(this, "티켓이 스캔되었습니다.", Toast.LENGTH_SHORT).show()
setResult(RESULT_OK, intent)
finish()
}
}
}
}

private fun initView(currentTicketId: Long) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SignInViewModelTest {
@Before
fun setUp() {
Dispatchers.setMain(UnconfinedTestDispatcher())

authRepository = mockk(relaxed = true)
analyticsHelper = mockk(relaxed = true)
vm = SignInViewModel(authRepository, analyticsHelper)
Expand All @@ -42,33 +43,27 @@ class SignInViewModelTest {
@Test
fun `로그인 성공하면 성공 이벤트가 발생한다`() {
// given
coEvery {
authRepository.signIn(any(), any())
} answers {
Result.success(Unit)
}
coEvery { authRepository.signIn(any(), any()) } answers { Result.success(Unit) }

// when
vm.signIn("testToken")

// then
assertThat(vm.event.getValue() is SignInEvent.SignInSuccess).isTrue
assertThat(vm.event.getValue()).isExactlyInstanceOf(SignInEvent.SignInSuccess::class.java)
}

@Test
fun `로그인 실패하면 실패 이벤트가 발생한다`() {
// given
coEvery {
authRepository.signIn(any(), any())
} answers {
Result.failure(Exception())
}
} answers { Result.failure(Exception()) }

// when
vm.signIn("testToken")

// then
assertThat(vm.event.getValue() is SignInEvent.SignInFailure).isTrue
assertThat(vm.event.getValue()).isExactlyInstanceOf(SignInEvent.SignInFailure::class.java)
}

@Test
Expand All @@ -78,6 +73,16 @@ class SignInViewModelTest {
vm.signInKakao()

// then
assertThat(vm.event.getValue() is SignInEvent.ShowSignInPage).isTrue
assertThat(vm.event.getValue()).isExactlyInstanceOf(SignInEvent.ShowSignInPage::class.java)
}

@Test
fun `FCM 토큰을 불러오지 못하면 실패 이벤트가 발생한다`() {
// given
// when
vm.signIn("testToken")

// then
assertThat(vm.event.getValue()).isExactlyInstanceOf(SignInEvent.SignInFailure::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package com.festago.festago.repository

interface TokenRepository {
var token: String?
fun refreshToken(token: String): Result<Unit>
fun refreshToken(socialType: String, token: String): Result<Unit>
suspend fun signIn(socialType: String, token: String): Result<Unit>
}

0 comments on commit 11443f1

Please sign in to comment.