parent
fd8c4e726d
commit
6f86663a54
|
@ -3,6 +3,8 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".app.SodaLiveApp"
|
android:name=".app.SodaLiveApp"
|
||||||
|
@ -38,6 +40,21 @@
|
||||||
<activity
|
<activity
|
||||||
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
|
android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
|
||||||
android:theme="@style/Theme.AppCompat.DayNight" />
|
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>
|
</manifest>
|
||||||
|
|
|
@ -8,12 +8,14 @@ object Constants {
|
||||||
const val PREF_IS_ADULT = "pref_is_adult"
|
const val PREF_IS_ADULT = "pref_is_adult"
|
||||||
const val PREF_NICKNAME = "pref_nickname"
|
const val PREF_NICKNAME = "pref_nickname"
|
||||||
const val PREF_USER_ROLE = "pref_user_role"
|
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 PREF_PROFILE_IMAGE = "pref_profile_image"
|
||||||
|
|
||||||
const val EXTRA_DATA = "extra_data"
|
const val EXTRA_DATA = "extra_data"
|
||||||
const val EXTRA_TERMS = "extra_terms"
|
const val EXTRA_TERMS = "extra_terms"
|
||||||
const val EXTRA_USER_ID = "extra_user_id"
|
const val EXTRA_USER_ID = "extra_user_id"
|
||||||
const val EXTRA_ROOM_ID = "extra_room_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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,4 +92,10 @@ object SharedPreferenceManager {
|
||||||
set(value) {
|
set(value) {
|
||||||
sharedPreferences[Constants.PREF_IS_ADULT] = value
|
sharedPreferences[Constants.PREF_IS_ADULT] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pushToken: String
|
||||||
|
get() = sharedPreferences[Constants.PREF_PUSH_TOKEN, ""]
|
||||||
|
set(value) {
|
||||||
|
sharedPreferences[Constants.PREF_PUSH_TOKEN] = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,19 @@
|
||||||
package kr.co.vividnext.sodalive.main
|
package kr.co.vividnext.sodalive.main
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
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.R
|
||||||
import kr.co.vividnext.sodalive.base.BaseActivity
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
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.content.main.ContentMainFragment
|
||||||
import kr.co.vividnext.sodalive.databinding.ActivityMainBinding
|
import kr.co.vividnext.sodalive.databinding.ActivityMainBinding
|
||||||
import kr.co.vividnext.sodalive.databinding.ItemMainTabBinding
|
import kr.co.vividnext.sodalive.databinding.ItemMainTabBinding
|
||||||
|
@ -27,8 +34,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
checkPermissions()
|
||||||
setupBottomTabLayout()
|
pushTokenUpdate()
|
||||||
|
|
||||||
getMemberInfo()
|
getMemberInfo()
|
||||||
}
|
}
|
||||||
|
@ -47,6 +54,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
|
||||||
isNotifiedMessage
|
isNotifiedMessage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupBottomTabLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBottomTabLayout() {
|
private fun setupBottomTabLayout() {
|
||||||
|
@ -196,6 +205,41 @@ class MainActivity : BaseActivity<ActivityMainBinding>(ActivityMainBinding::infl
|
||||||
fragmentTransaction.commitNow()
|
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() {
|
private fun getMemberInfo() {
|
||||||
viewModel.getMemberInfo {
|
viewModel.getMemberInfo {
|
||||||
notificationSettingsDialog.show(screenWidth)
|
notificationSettingsDialog.show(screenWidth)
|
||||||
|
|
|
@ -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) {
|
fun getMemberInfo(showNotificationSettingsDialog: () -> Unit) {
|
||||||
compositeDisposable.add(
|
compositeDisposable.add(
|
||||||
userRepository.getMemberInfo(token = "Bearer ${SharedPreferenceManager.token}")
|
userRepository.getMemberInfo(token = "Bearer ${SharedPreferenceManager.token}")
|
||||||
|
|
|
@ -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"
|
||||||
|
)
|
|
@ -145,7 +145,7 @@ class SplashActivity : BaseActivity<ActivitySplashBinding>(ActivitySplashBinding
|
||||||
)
|
)
|
||||||
} else if (audioContentIdString != null) {
|
} else if (audioContentIdString != null) {
|
||||||
bundleOf(
|
bundleOf(
|
||||||
Constants.EXTRA_AUDIO_CONTENT_ID to audioContentIdString.toLong()
|
Constants.EXTRA_CONTENT_ID to audioContentIdString.toLong()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|
|
@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.user
|
||||||
|
|
||||||
import io.reactivex.rxjava3.core.Single
|
import io.reactivex.rxjava3.core.Single
|
||||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
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.GetMemberInfoResponse
|
||||||
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
|
||||||
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
||||||
|
@ -14,6 +15,7 @@ import retrofit2.http.GET
|
||||||
import retrofit2.http.Header
|
import retrofit2.http.Header
|
||||||
import retrofit2.http.Multipart
|
import retrofit2.http.Multipart
|
||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.PUT
|
||||||
import retrofit2.http.Part
|
import retrofit2.http.Part
|
||||||
|
|
||||||
interface UserApi {
|
interface UserApi {
|
||||||
|
@ -40,4 +42,10 @@ interface UserApi {
|
||||||
@Body request: UpdateNotificationSettingRequest,
|
@Body request: UpdateNotificationSettingRequest,
|
||||||
@Header("Authorization") authHeader: String
|
@Header("Authorization") authHeader: String
|
||||||
): Single<ApiResponse<Any>>
|
): Single<ApiResponse<Any>>
|
||||||
|
|
||||||
|
@PUT("/member/push-token/update")
|
||||||
|
fun updatePushToken(
|
||||||
|
@Body request: PushTokenUpdateRequest,
|
||||||
|
@Header("Authorization") authHeader: String
|
||||||
|
): Single<ApiResponse<Any>>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package kr.co.vividnext.sodalive.user
|
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.settings.notification.UpdateNotificationSettingRequest
|
||||||
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
import kr.co.vividnext.sodalive.user.find_password.ForgotPasswordRequest
|
||||||
import kr.co.vividnext.sodalive.user.login.LoginRequest
|
import kr.co.vividnext.sodalive.user.login.LoginRequest
|
||||||
|
@ -21,5 +22,10 @@ class UserRepository(private val userApi: UserApi) {
|
||||||
token: String
|
token: String
|
||||||
) = userApi.updateNotificationSettings(request, authHeader = token)
|
) = userApi.updateNotificationSettings(request, authHeader = token)
|
||||||
|
|
||||||
|
fun updatePushToken(
|
||||||
|
request: PushTokenUpdateRequest,
|
||||||
|
token: String
|
||||||
|
) = userApi.updatePushToken(request, authHeader = token)
|
||||||
|
|
||||||
fun getMemberInfo(token: String) = userApi.getMemberInfo(authHeader = token)
|
fun getMemberInfo(token: String) = userApi.getMemberInfo(authHeader = token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">소다라이브</string>
|
<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>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue