FCM 설정

FCM 토큰 업데이트 API 적용
This commit is contained in:
klaus 2023-07-25 03:20:42 +09:00
parent fd8c4e726d
commit 6f86663a54
11 changed files with 211 additions and 5 deletions

View File

@ -3,6 +3,8 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:name=".app.SodaLiveApp"
@ -38,6 +40,21 @@
<activity
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
android:theme="@style/Theme.AppCompat.DayNight" />
</application>
<!-- [START firebase_service] -->
<service
android:name=".fcm.SodaFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- [END firebase_service] -->
<!-- [START fcm_default_channel] -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
<!-- [END fcm_default_channel] -->
</application>
</manifest>

View File

@ -8,12 +8,14 @@ object Constants {
const val PREF_IS_ADULT = "pref_is_adult"
const val PREF_NICKNAME = "pref_nickname"
const val PREF_USER_ROLE = "pref_user_role"
const val PREF_PUSH_TOKEN = "pref_push_token"
const val PREF_PROFILE_IMAGE = "pref_profile_image"
const val EXTRA_DATA = "extra_data"
const val EXTRA_TERMS = "extra_terms"
const val EXTRA_USER_ID = "extra_user_id"
const val EXTRA_ROOM_ID = "extra_room_id"
const val EXTRA_MESSAGE_ID = "extra_message_id"
const val EXTRA_AUDIO_CONTENT_ID = "extra_audio_content_id"
const val EXTRA_CONTENT_ID = "extra_content_id"
}

View File

@ -92,4 +92,10 @@ object SharedPreferenceManager {
set(value) {
sharedPreferences[Constants.PREF_IS_ADULT] = value
}
var pushToken: String
get() = sharedPreferences[Constants.PREF_PUSH_TOKEN, ""]
set(value) {
sharedPreferences[Constants.PREF_PUSH_TOKEN] = value
}
}

View File

@ -0,0 +1,89 @@
package kr.co.vividnext.sodalive.fcm
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.splash.SplashActivity
class SodaFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (SharedPreferenceManager.token.isNotBlank()) {
when {
remoteMessage.data.isNotEmpty() -> {
sendNotification(remoteMessage.data)
}
}
}
}
override fun onNewToken(token: String) {
SharedPreferenceManager.pushToken = token
}
private fun sendNotification(messageData: Map<String, String>) {
val notificationChannelId = getString(R.string.default_notification_channel_id)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel(
notificationChannelId,
getString(R.string.app_name),
NotificationManager.IMPORTANCE_HIGH
)
notificationManager.createNotificationChannel(channel)
}
val intent = Intent(this, SplashActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
val roomId = messageData["room_id"]
if (roomId != null) {
intent.putExtra(Constants.EXTRA_ROOM_ID, roomId.toLong())
}
val socdocId = messageData["message_id"]
if (socdocId != null) {
intent.putExtra(Constants.EXTRA_MESSAGE_ID, socdocId.toLong())
}
val audioContentId = messageData["content_id"]
if (audioContentId != null) {
intent.putExtra(Constants.EXTRA_CONTENT_ID, audioContentId.toLong())
}
val pendingIntent =
PendingIntent.getActivity(
this,
System.currentTimeMillis().toInt(),
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
.setContentTitle(messageData["title"])
.setContentText(messageData["message"])
.setSound(defaultSoundUri)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
val bigTextStyle = NotificationCompat.BigTextStyle(notificationBuilder)
bigTextStyle.bigText(messageData["message"])
notificationManager.notify(System.currentTimeMillis().toInt(), notificationBuilder.build())
}
}

View File

@ -1,12 +1,19 @@
package kr.co.vividnext.sodalive.main
import android.Manifest
import android.content.res.ColorStateList
import android.os.Build
import android.os.Bundle
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import com.google.firebase.messaging.FirebaseMessaging
import com.gun0912.tedpermission.PermissionListener
import com.gun0912.tedpermission.normal.TedPermission
import com.orhanobut.logger.Logger
import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.content.main.ContentMainFragment
import kr.co.vividnext.sodalive.databinding.ActivityMainBinding
import kr.co.vividnext.sodalive.databinding.ItemMainTabBinding
@ -27,8 +34,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupBottomTabLayout()
checkPermissions()
pushTokenUpdate()
getMemberInfo()
}
@ -47,6 +54,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
isNotifiedMessage
)
}
setupBottomTabLayout()
}
private fun setupBottomTabLayout() {
@ -196,6 +205,41 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
fragmentTransaction.commitNow()
}
private fun checkPermissions() {
val permissions = mutableListOf(Manifest.permission.RECORD_AUDIO)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissions.add(Manifest.permission.POST_NOTIFICATIONS)
}
TedPermission.create()
.setPermissionListener(object : PermissionListener {
override fun onPermissionGranted() {
}
override fun onPermissionDenied(deniedPermissions: MutableList<String>?) {
}
})
.setDeniedMessage(R.string.record_audio_permission_denied_message)
.setPermissions(*permissions.toTypedArray())
.check()
}
private fun pushTokenUpdate() {
FirebaseMessaging.getInstance().token.addOnCompleteListener {
if (!it.isSuccessful) {
Logger.v("Fetching FCM registration token failed", it.exception)
return@addOnCompleteListener
}
val pushToken = it.result
if (pushToken != null) {
SharedPreferenceManager.pushToken = pushToken
viewModel.pushTokenUpdate(pushToken)
}
}
}
private fun getMemberInfo() {
viewModel.getMemberInfo {
notificationSettingsDialog.show(screenWidth)

View File

@ -60,6 +60,20 @@ class MainViewModel(
)
}
fun pushTokenUpdate(pushToken: String) {
val request = PushTokenUpdateRequest(
pushToken = pushToken
)
compositeDisposable.add(
userRepository
.updatePushToken(request, "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
fun getMemberInfo(showNotificationSettingsDialog: () -> Unit) {
compositeDisposable.add(
userRepository.getMemberInfo(token = "Bearer ${SharedPreferenceManager.token}")

View File

@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.main
import com.google.gson.annotations.SerializedName
data class PushTokenUpdateRequest(
@SerializedName("pushToken") val pushToken: String,
@SerializedName("container") val container: String = "aos"
)

View File

@ -145,7 +145,7 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding
)
} else if (audioContentIdString != null) {
bundleOf(
Constants.EXTRA_AUDIO_CONTENT_ID to audioContentIdString.toLong()
Constants.EXTRA_CONTENT_ID to audioContentIdString.toLong()
)
} else {
null

View File

@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.user
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.settings.notification.GetMemberInfoResponse
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
@ -14,6 +15,7 @@ import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Part
interface UserApi {
@ -40,4 +42,10 @@ interface UserApi {
@Body request: UpdateNotificationSettingRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
@PUT("/member/push-token/update")
fun updatePushToken(
@Body request: PushTokenUpdateRequest,
@Header("Authorization") authHeader: String
): Single<ApiResponse<Any>>
}

View File

@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.user
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
import kr.co.vividnext.sodalive.user.login.LoginRequest
@ -21,5 +22,10 @@ class UserRepository(private val userApi: UserApi) {
token: String
) = userApi.updateNotificationSettings(request, authHeader = token)
fun updatePushToken(
request: PushTokenUpdateRequest,
token: String
) = userApi.updatePushToken(request, authHeader = token)
fun getMemberInfo(token: String) = userApi.getMemberInfo(authHeader = token)
}

View File

@ -1,3 +1,15 @@
<resources>
<string name="app_name">소다라이브</string>
<string name="picker_gallery">갤러리</string>
<string name="picker_camera">카메라</string>
<string name="picker_select">선택</string>
<string name="picker_media_permissions_hint">갤러리 접근을 위해 미디어 권한을 허용해야 합니다.</string>
<string name="picker_empty_media">미디어 라이브러리가 비어있습니다.</string>
<string name="picker_allow">허용</string>
<string name="picker_select_photo">사진선택</string>
<string name="picker_no_camera">사용할 수 있는 카메라 앱이 없습니다.</string>
<string name="default_notification_channel_id">soda_fcm_default_channel</string>
<string name="record_audio_permission_denied_message"><![CDATA[권한을 거부하시면 라이브 참여를 하실 수 없습니다.\n\n[설정]->[권한]에서 권한을 허용해 주시기 바랍니다.]]></string>
<string name="read_storage_permission_denied_message"><![CDATA[권한을 거부하시면 콘텐츠를 업로드 하실 수 없습니다.\n\n[설정]->[권한]에서 권한을 허용해 주시기 바랍니다.]]></string>
<string name="retry">다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.</string>
</resources>