Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT/#119] 푸시알림 구현 #120

Merged
merged 13 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ plugins {
kotlin("kapt")
id("kotlin-parcelize")
id("dagger.hilt.android.plugin")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}

android {
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:name=".MyApp"
Expand Down Expand Up @@ -93,7 +94,7 @@
android:screenOrientation="portrait" />

<activity
android:name="co.orange.presentation.sell.push.SellPushActivity"
android:name="co.orange.presentation.push.PushActivity"
android:exported="false"
android:screenOrientation="portrait" />

Expand Down Expand Up @@ -178,6 +179,18 @@
</intent-filter>
</activity>

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

<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />

</application>

</manifest>
10 changes: 7 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ buildscript {
}

dependencies {
classpath(ClassPathPlugins.gradle)
classpath(ClassPathPlugins.kotlinGradle)
classpath(ClassPathPlugins.hilt)
ClassPathPlugins.run {
classpath(gradle)
classpath(kotlinGradle)
classpath(hilt)
classpath(googleServices)
classpath(crashlyticsGradle)
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ object ClassPathPlugins {
const val gradle = "com.android.tools.build:gradle:${Versions.gradleVersion}"
const val kotlinGradle = "org.jetbrains.kotlin:kotlin-gradle-plugin:${Versions.kotlinVersion}"
const val hilt = "com.google.dagger:hilt-android-gradle-plugin:${Versions.hiltVersion}"
const val googleServices = "com.google.gms:google-services:${Versions.googleServicesVersion}"
const val crashlyticsGradle =
"com.google.firebase:firebase-crashlytics-gradle:${Versions.crashlyticsVersion}"
}

object FirebaseDependencies {
const val bom = "com.google.firebase:firebase-bom:32.2.0"
const val firebaseBom = "com.google.firebase:firebase-bom:${Versions.firebaseBomVersion}"
const val messaging = "com.google.firebase:firebase-messaging-ktx"
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"
}

object KakaoDependencies {
Expand Down
4 changes: 4 additions & 0 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ object Versions {
const val espressoVersion = "3.5.1"
const val androidTestVersion = "1.1.2"

const val firebaseBomVersion = "33.1.2"
const val googleServicesVersion = "4.4.2"
const val crashlyticsVersion = "2.9.9"

val javaVersion = JavaVersion.VERSION_17
const val jvmVersion = "17"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ data class AuthRequestDto(
val token: String,
@SerialName("type")
val type: String,
@SerialName("deviceToken")
val deviceToken: String,
@SerialName("deviceType")
val deviceType: String,
@SerialName("fcmToken")
val fcmToken: String,
) {
companion object {
fun AuthRequestModel.toDto() = AuthRequestDto(token, type)
fun AuthRequestModel.toDto() = AuthRequestDto(token, type, deviceToken, deviceType, fcmToken)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ data class SignUpRequestDto(
val birth: String,
@SerialName("sex")
val sex: String,
@SerialName("isAgreedMarketingTerm")
val isAgreedMarketingTerm: Boolean,
) {
companion object {
fun SignUpRequestModel.toDto() = SignUpRequestDto(name, phone, birth, sex)
fun SignUpRequestModel.toDto() = SignUpRequestDto(name, phone, birth, sex, isAgreedMarketingTerm)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ package co.orange.domain.entity.request
data class AuthRequestModel(
val token: String,
val type: String,
val deviceToken: String,
val deviceType: String,
val fcmToken: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ data class SignUpRequestModel(
val phone: String,
val birth: String,
val sex: String,
val isAgreedMarketingTerm: Boolean,
)
7 changes: 7 additions & 0 deletions presentation/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,11 @@ dependencies {
KakaoDependencies.run {
implementation(user)
}

FirebaseDependencies.run {
implementation(platform(firebaseBom))
implementation(messaging)
implementation(crashlytics)
implementation(analytics)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>(R.layout.activity_login

initLoginBtnListener()
observeAppLoginAvailable()
observeGetFCMTokenResult()
observeChangeTokenResult()
}

Expand All @@ -42,6 +43,12 @@ class LoginActivity : BaseActivity<ActivityLoginBinding>(R.layout.activity_login
}.launchIn(lifecycleScope)
}

private fun observeGetFCMTokenResult() {
viewModel.getFCMTokenResult.flowWithLifecycle(lifecycle).onEach { isSuccess ->
if (!isSuccess) toast(stringOf(R.string.error_msg))
}.launchIn(lifecycleScope)
}

private fun observeChangeTokenResult() {
viewModel.changeTokenState.flowWithLifecycle(lifecycle).distinctUntilChanged()
.onEach { state ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@ import co.orange.core.state.UiState
import co.orange.domain.entity.request.AuthRequestModel
import co.orange.domain.repository.AuthRepository
import co.orange.domain.repository.UserRepository
import com.google.firebase.messaging.FirebaseMessaging
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.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -30,19 +33,22 @@ class LoginViewModel
private val _changeTokenState = MutableStateFlow<UiState<String>>(UiState.Empty)
val changeTokenState: StateFlow<UiState<String>> = _changeTokenState

private val _getFCMTokenResult = MutableSharedFlow<Boolean>()
val getFCMTokenResult: SharedFlow<Boolean> = _getFCMTokenResult

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(token.accessToken)
getFCMToken(token.accessToken)
}
}

private var webLoginCallback: (OAuthToken?, Throwable?) -> Unit = { token, error ->
if (error == null && token != null) {
changeTokenFromServer(token.accessToken)
getFCMToken(token.accessToken)
}
}

Expand All @@ -60,13 +66,34 @@ class LoginViewModel
}
}

private fun changeTokenFromServer(accessToken: String) {
private fun getFCMToken(accessToken: String) {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
viewModelScope.launch {
if (task.isSuccessful) {
changeTokenFromServer(accessToken, task.result)
} else {
_getFCMTokenResult.emit(false)
}
}
}
}

private fun changeTokenFromServer(
accessToken: String,
fcmToken: String,
) {
viewModelScope.launch {
authRepository.postOauthDataToGetToken(AuthRequestModel(accessToken, KAKAO))
authRepository.postOauthDataToGetToken(
AuthRequestModel(
accessToken,
KAKAO,
userRepository.getDeviceToken(),
ANDROID,
fcmToken,
),
)
.onSuccess {
with(userRepository) {
setTokens(it.accesstoken, it.refreshtoken)
}
userRepository.setTokens(it.accesstoken, it.refreshtoken)
_changeTokenState.value = UiState.Success(it.status)
}
.onFailure {
Expand All @@ -77,5 +104,6 @@ class LoginViewModel

companion object {
const val KAKAO = "KAKAO"
const val ANDROID = "ANDROID"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,16 @@ class PhoneViewModel
_getIamportCertificationResult.emit(true)
postToSignUpFromServer(
SignUpRequestModel(
it.name.orEmpty(),
it.phone.orEmpty(),
it.birthday.orEmpty(),
it.gender?.uppercase().orEmpty(),
name = it.name.orEmpty(),
phone = it.phone.orEmpty(),
birth = it.birthday.orEmpty(),
sex = it.gender?.uppercase().orEmpty(),
isAgreedMarketingTerm = isTermMarketingSelected.value ?: false,
),
)
userRepository.setUserInfo(
it.name.orEmpty(),
it.phone?.toPhoneFrom().orEmpty(),
userName = it.name.orEmpty(),
userPhone = it.phone?.toPhoneFrom().orEmpty(),
)
} else {
_getIamportCertificationResult.emit(false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package co.orange.presentation.buy.progress

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
Expand All @@ -18,9 +22,10 @@ import co.orange.core.extension.toast
import co.orange.core.state.UiState
import co.orange.domain.entity.response.AddressInfoModel
import co.orange.domain.entity.response.BuyProgressModel
import co.orange.presentation.buy.finished.BuyFinishedActivity
import co.orange.presentation.buy.progress.BuyProgressViewModel.Companion.PAY_SUCCESS
import co.orange.presentation.buy.push.BuyPushActivity
import co.orange.presentation.delivery.DeliveryActivity
import co.orange.presentation.push.PushActivity
import co.orange.presentation.setting.SettingActivity.Companion.WEB_TERM_PURCHASE
import co.orange.presentation.setting.SettingActivity.Companion.WEB_TERM_SERVICE
import coil.load
Expand Down Expand Up @@ -213,19 +218,44 @@ class BuyProgressActivity :
viewModel.postOrderState.flowWithLifecycle(lifecycle).distinctUntilChanged()
.onEach { state ->
when (state) {
is UiState.Success -> {
BuyPushActivity.createIntent(this, state.data).apply {
startActivity(this)
}
finish()
}
is UiState.Success -> navigateToPushOrFinish(state.data)

is UiState.Failure -> toast(stringOf(R.string.buy_order_error_msg))
else -> return@onEach
}
}.launchIn(lifecycleScope)
}

private fun navigateToPushOrFinish(orderId: String) {
if (isPermissionNeeded()) {
navigateToPushActivity(orderId)
} else {
navigateToBuyFinishedActivity(orderId)
}
}

private fun isPermissionNeeded(): Boolean =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
this.applicationContext,
Manifest.permission.POST_NOTIFICATIONS,
) != PackageManager.PERMISSION_GRANTED
} else {
false
}

private fun navigateToBuyFinishedActivity(orderId: String) {
BuyFinishedActivity.createIntent(this, orderId)
.apply { startActivity(this) }
finish()
}

private fun navigateToPushActivity(orderId: String) {
PushActivity.createIntent(this, true, orderId, null, null, null, null)
.apply { startActivity(this) }
finish()
}

override fun onDestroy() {
super.onDestroy()

Expand Down
Loading
Loading