diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 13672dc..c40aecd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ xmlns:tools="http://schemas.android.com/tools"> + + - + + + + + + + + + + + + diff --git a/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt b/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt index a0bd7ed..b700cbf 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/common/Constants.kt @@ -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" } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/common/SharedPreferenceManager.kt b/app/src/main/java/kr/co/vividnext/sodalive/common/SharedPreferenceManager.kt index 7d1e00c..60bbe3f 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/common/SharedPreferenceManager.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/common/SharedPreferenceManager.kt @@ -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 + } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/fcm/SodaFirebaseMessagingService.kt b/app/src/main/java/kr/co/vividnext/sodalive/fcm/SodaFirebaseMessagingService.kt new file mode 100644 index 0000000..7bf4ec9 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/fcm/SodaFirebaseMessagingService.kt @@ -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) { + 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()) + } +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt index c9be4e4..7b9d746 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/MainActivity.kt @@ -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::infl override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - setupBottomTabLayout() + checkPermissions() + pushTokenUpdate() getMemberInfo() } @@ -47,6 +54,8 @@ class MainActivity : BaseActivity(ActivityMainBinding::infl isNotifiedMessage ) } + + setupBottomTabLayout() } private fun setupBottomTabLayout() { @@ -196,6 +205,41 @@ class MainActivity : BaseActivity(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?) { + } + }) + .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) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt index 5824a03..2996929 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/MainViewModel.kt @@ -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}") diff --git a/app/src/main/java/kr/co/vividnext/sodalive/main/PushTokenUpdateRequest.kt b/app/src/main/java/kr/co/vividnext/sodalive/main/PushTokenUpdateRequest.kt new file mode 100644 index 0000000..877c55e --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/main/PushTokenUpdateRequest.kt @@ -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" +) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt index 8bbba0a..5943a88 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/splash/SplashActivity.kt @@ -145,7 +145,7 @@ class SplashActivity : BaseActivity(ActivitySplashBinding ) } else if (audioContentIdString != null) { bundleOf( - Constants.EXTRA_AUDIO_CONTENT_ID to audioContentIdString.toLong() + Constants.EXTRA_CONTENT_ID to audioContentIdString.toLong() ) } else { null diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt index e340a08..7e59ec3 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserApi.kt @@ -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> + + @PUT("/member/push-token/update") + fun updatePushToken( + @Body request: PushTokenUpdateRequest, + @Header("Authorization") authHeader: String + ): Single> } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt index 19d15f3..1c34592 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/user/UserRepository.kt @@ -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) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6018d58..cca3ae8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,15 @@ 소다라이브 + 갤러리 + 카메라 + 선택 + 갤러리 접근을 위해 미디어 권한을 허용해야 합니다. + 미디어 라이브러리가 비어있습니다. + 허용 + 사진선택 + 사용할 수 있는 카메라 앱이 없습니다. + soda_fcm_default_channel + [권한]에서 권한을 허용해 주시기 바랍니다.]]> + [권한]에서 권한을 허용해 주시기 바랍니다.]]> + 다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다.