feat(main-v2): 메인 하단 내비게이션을 추가한다

This commit is contained in:
2026-05-19 15:55:03 +09:00
parent 751b031627
commit 99b7a6ce99
41 changed files with 1646 additions and 22 deletions

View File

@@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.v2.main
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.databinding.FragmentV2MainChatBinding
class ChatMainFragment : BaseFragment<FragmentV2MainChatBinding>(
FragmentV2MainChatBinding::inflate
)

View File

@@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.v2.main
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.databinding.FragmentV2MainContentBinding
class ContentMainFragment : BaseFragment<FragmentV2MainContentBinding>(
FragmentV2MainContentBinding::inflate
)

View File

@@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.v2.main
import kr.co.vividnext.sodalive.base.BaseFragment
import kr.co.vividnext.sodalive.databinding.FragmentV2MainHomeBinding
class HomeMainFragment : BaseFragment<FragmentV2MainHomeBinding>(
FragmentV2MainHomeBinding::inflate
)

View File

@@ -0,0 +1,654 @@
package kr.co.vividnext.sodalive.v2.main
import android.Manifest
import android.content.ComponentName
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.media3.common.MediaItem
import androidx.media3.common.MediaMetadata
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import coil.load
import coil.transform.RoundedCornersTransformation
import com.google.common.util.concurrent.ListenableFuture
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.audio_content.AudioContentPlayService
import kr.co.vividnext.sodalive.audio_content.detail.AudioContentDetailActivity
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerFragment
import kr.co.vividnext.sodalive.audio_content.player.AudioContentPlayerService
import kr.co.vividnext.sodalive.audio_content.series.detail.SeriesDetailActivity
import kr.co.vividnext.sodalive.audition.AuditionActivity
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.common.Constants
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityMainV2Binding
import kr.co.vividnext.sodalive.explorer.profile.UserProfileActivity
import kr.co.vividnext.sodalive.explorer.profile.creator_community.all.CreatorCommunityAllActivity
import kr.co.vividnext.sodalive.main.EventPopupDialogFragment
import kr.co.vividnext.sodalive.message.MessageActivity
import kr.co.vividnext.sodalive.mypage.MyPageFragment
import kr.co.vividnext.sodalive.settings.event.EventDetailActivity
import kr.co.vividnext.sodalive.settings.notification.NotificationSettingsDialog
import kr.co.vividnext.sodalive.user.login.LoginActivity
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import java.util.Locale
import kotlin.math.max
@UnstableApi
class MainV2Activity : BaseActivity<ActivityMainV2Binding>(ActivityMainV2Binding::inflate) {
private val viewModel: MainV2ViewModel by inject()
private lateinit var notificationSettingsDialog: NotificationSettingsDialog
private var mediaController: MediaController? = null
private var mediaControllerFuture: ListenableFuture<MediaController>? = null
private val handler = Handler(Looper.getMainLooper())
private val showMiniPlayerRunnable = Runnable { initAndVisibleMiniPlayer() }
private var playerStateJob: Job? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
overrideRootWindowInsets()
checkPermissions()
trackAppLaunchIfNeeded()
pushTokenUpdate()
if (isLoggedIn()) {
updatePidAndGaid()
getEventPopup()
observePlayerState()
handler.postDelayed({ executeDeeplink(intent) }, 1000)
}
}
private fun overrideRootWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
val ime = insets.getInsets(WindowInsetsCompat.Type.ime())
val left = max(systemBars.left, ime.left)
val top = systemBars.top
val right = max(systemBars.right, ime.right)
v.setPadding(left, top, right, 0)
insets
}
ViewCompat.requestApplyInsets(binding.root)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
if (isLoggedIn()) {
executeDeeplink(intent)
}
}
override fun onResume() {
super.onResume()
getMemberInfo()
startService(
Intent(this, AudioContentPlayService::class.java).apply {
action = AudioContentPlayService.MusicAction.INIT.name
}
)
}
override fun onDestroy() {
deInitMiniPlayer()
playerStateJob?.cancel()
super.onDestroy()
}
override fun setupView() {
notificationSettingsDialog = NotificationSettingsDialog(
this,
layoutInflater
) { isNotifiedLive, isNotifiedUploadContent, isNotifiedMessage ->
viewModel.updateNotificationSettings(
isNotifiedLive,
isNotifiedUploadContent,
isNotifiedMessage
)
}
setupBottomNavigation()
}
fun showLoginActivity() {
if (SharedPreferenceManager.token.isBlank()) {
val extras = intent.extras
startActivity(
Intent(applicationContext, LoginActivity::class.java).apply {
putExtra(Constants.EXTRA_DATA, extras)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
}
)
}
}
fun openChatTab() {
viewModel.clickTab(MainV2Tab.CHAT)
}
private fun setupBottomNavigation() {
binding.bottomNavigation.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.menu_main_v2_home -> viewModel.clickTab(MainV2Tab.HOME)
R.id.menu_main_v2_content -> viewModel.clickTab(MainV2Tab.CONTENT)
R.id.menu_main_v2_chat -> viewModel.clickTab(MainV2Tab.CHAT)
R.id.menu_main_v2_my -> viewModel.clickTab(MainV2Tab.MY)
}
true
}
binding.bottomNavigation.apply {
itemIconTintList = null
}
viewModel.currentTab.observe(this) { tab ->
val itemId = when (tab) {
MainV2Tab.HOME -> R.id.menu_main_v2_home
MainV2Tab.CONTENT -> R.id.menu_main_v2_content
MainV2Tab.CHAT -> R.id.menu_main_v2_chat
MainV2Tab.MY -> R.id.menu_main_v2_my
}
if (binding.bottomNavigation.selectedItemId != itemId) {
binding.bottomNavigation.selectedItemId = itemId
}
changeFragment(tab)
}
}
private fun changeFragment(currentTab: MainV2Tab) {
val tag = currentTab.toString()
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentManager.primaryNavigationFragment?.let {
fragmentTransaction.hide(it)
}
var fragment = fragmentManager.findFragmentByTag(tag)
if (fragment == null) {
fragment = when (currentTab) {
MainV2Tab.HOME -> HomeMainFragment()
MainV2Tab.CONTENT -> ContentMainFragment()
MainV2Tab.CHAT -> ChatMainFragment()
MainV2Tab.MY -> MyPageFragment()
}
fragmentTransaction.add(R.id.fl_container, fragment, tag)
} else {
fragmentTransaction.show(fragment)
}
fragmentTransaction.setPrimaryNavigationFragment(fragment)
fragmentTransaction.setReorderingAllowed(true)
fragmentTransaction.commitNow()
}
private fun observePlayerState() {
playerStateJob = lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
SharedPreferenceManager.isPlayerServiceRunningFlow.collect { isRunning ->
if (isRunning) {
handler.removeCallbacks(showMiniPlayerRunnable)
handler.postDelayed(showMiniPlayerRunnable, 1500)
} else {
deInitMiniPlayer()
}
}
}
}
}
private fun initAndVisibleMiniPlayer() {
binding.clMiniPlayer.visibility = View.VISIBLE
binding.clMiniPlayer.setOnClickListener { showPlayerFragment() }
binding.ivPlayerStop.setOnClickListener {
startService(
Intent(applicationContext, AudioContentPlayerService::class.java).apply {
action = "STOP_SERVICE"
}
)
}
connectPlayerService()
}
private fun connectPlayerService() {
if (mediaController != null || mediaControllerFuture != null) {
return
}
val componentName = ComponentName(applicationContext, AudioContentPlayerService::class.java)
val sessionToken = SessionToken(applicationContext, componentName)
val controllerFuture =
MediaController.Builder(applicationContext, sessionToken).buildAsync()
mediaControllerFuture = controllerFuture
controllerFuture.addListener(
{
try {
if (mediaController != null) {
controllerFuture.get().release()
return@addListener
}
mediaController = controllerFuture.get()
setupMediaController()
updateMediaMetadata(mediaController?.mediaMetadata)
binding.ivPlayerPlayOrPause.setImageResource(
if (mediaController?.isPlaying == true) {
R.drawable.ic_player_pause
} else {
R.drawable.ic_player_play
}
)
binding.ivPlayerPlayOrPause.setOnClickListener {
mediaController?.let {
if (it.playWhenReady) {
it.pause()
} else {
it.play()
}
}
}
} catch (throwable: Throwable) {
Logger.e(throwable, "Failed to connect player service")
} finally {
mediaControllerFuture = null
}
},
ContextCompat.getMainExecutor(applicationContext)
)
}
private fun updateMediaMetadata(metadata: MediaMetadata?) {
metadata?.let {
binding.tvPlayerTitle.text = it.title
binding.tvPlayerNickname.text = it.artist
binding.ivPlayerCover.load(it.artworkUri) {
crossfade(true)
placeholder(R.drawable.ic_place_holder)
transformations(RoundedCornersTransformation(4f))
}
}
}
private fun setupMediaController() {
if (mediaController == null) {
deInitMiniPlayer()
return
}
mediaController!!.addListener(object : Player.Listener {
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
updateMediaMetadata(mediaItem?.mediaMetadata)
}
override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
binding.ivPlayerPlayOrPause.setImageResource(
if (playWhenReady) {
R.drawable.ic_player_pause
} else {
R.drawable.ic_player_play
}
)
}
})
}
private fun deInitMiniPlayer() {
handler.removeCallbacks(showMiniPlayerRunnable)
binding.clMiniPlayer.visibility = View.GONE
mediaControllerFuture?.cancel(true)
mediaControllerFuture = null
mediaController?.release()
mediaController = null
}
private fun showPlayerFragment() {
val playerFragment = AudioContentPlayerFragment(screenWidth, arrayListOf())
playerFragment.show(supportFragmentManager, playerFragment.tag)
}
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 trackAppLaunchIfNeeded() {
handler.postDelayed({
val alreadyTrackingAppLaunch = SharedPreferenceManager.alreadyTrackingAppLaunch
val pid = SharedPreferenceManager.marketingPid
if (!alreadyTrackingAppLaunch && pid.isNotBlank()) {
SharedPreferenceManager.alreadyTrackingAppLaunch = true
viewModel.adTrackingAppLaunch(pid = pid)
}
}, 1000)
}
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
if (isLoggedIn()) {
viewModel.pushTokenUpdate(pushToken)
}
}
}
}
private fun updatePidAndGaid() {
handler.postDelayed({
viewModel.fetchAndUpdateGaidAndPid(context = applicationContext)
}, 3000)
}
private fun getMemberInfo() {
if (isLoggedIn()) {
viewModel.getMemberInfo(context = applicationContext) {
notificationSettingsDialog.show(screenWidth)
}
}
}
private fun getEventPopup() {
viewModel.getEventPopup {
if (SharedPreferenceManager.notShowingEventPopupId != it.id) {
EventPopupDialogFragment(
screenWidth = screenWidth,
eventItem = it
) {
startActivity(
Intent(applicationContext, EventDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_EVENT, it)
}
)
}.show(supportFragmentManager, EventPopupDialogFragment::class.java.simpleName)
}
}
}
private fun executeDeeplink(intent: Intent) {
val bundle = intent.getBundleExtra(Constants.EXTRA_DATA) ?: return
val deepLinkUrl = bundle.getString("deep_link")
val routeBundle = if (!deepLinkUrl.isNullOrBlank()) {
buildBundleFromDeepLinkUrl(deepLinkUrl) ?: bundle
} else {
bundle
}
if (executeBundleRoute(routeBundle)) {
clearDeferredDeepLink()
}
}
private fun buildBundleFromDeepLinkUrl(deepLinkUrl: String): Bundle? {
val data = runCatching { deepLinkUrl.toUri() }.getOrNull() ?: return null
val extras = Bundle().apply {
putString("deep_link", deepLinkUrl)
}
fun putQuery(key: String) {
val value = data.getQueryParameter(key)
if (!value.isNullOrBlank()) {
extras.putString(key, value)
}
}
putQuery("channel_id")
putQuery("message_id")
putQuery("audition_id")
putQuery("content_id")
putQuery("deep_link_value")
putQuery("deep_link_sub5")
putQuery(Constants.EXTRA_COMMUNITY_CREATOR_ID)
putQuery(Constants.EXTRA_COMMUNITY_POST_ID)
applyPathDeepLink(data = data) { key, value ->
if (!value.isNullOrBlank() && !extras.containsKey(key)) {
extras.putString(key, value)
}
}
return extras
}
private fun applyPathDeepLink(
data: android.net.Uri,
putIfAbsent: (key: String, value: String?) -> Unit
) {
val host = data.host?.lowercase(Locale.ROOT).orEmpty()
val pathSegments = data.pathSegments.filter { it.isNotBlank() }
val pathType: String
val pathId: String?
if (host.isNotBlank() && host != "payverse") {
pathType = host
pathId = pathSegments.firstOrNull()
} else if (pathSegments.isNotEmpty()) {
pathType = pathSegments[0].lowercase(Locale.ROOT)
pathId = pathSegments.getOrNull(1)
} else {
return
}
when (pathType) {
"content" -> {
putIfAbsent("content_id", pathId)
putIfAbsent("deep_link_value", "content")
putIfAbsent("deep_link_sub5", pathId)
}
"series" -> {
putIfAbsent("deep_link_value", "series")
putIfAbsent("deep_link_sub5", pathId)
}
"community" -> {
putIfAbsent("deep_link_value", "community")
putIfAbsent(Constants.EXTRA_COMMUNITY_CREATOR_ID, pathId)
putIfAbsent("deep_link_sub5", pathId)
}
"message" -> {
putIfAbsent("deep_link_value", "message")
putIfAbsent("message_id", pathId)
putIfAbsent("deep_link_sub5", pathId)
}
"audition" -> {
putIfAbsent("deep_link_value", "audition")
putIfAbsent("audition_id", pathId)
putIfAbsent("deep_link_sub5", pathId)
}
}
}
private fun executeBundleRoute(bundle: Bundle): Boolean {
val channelId = bundle.getString("channel_id")?.toLongOrNull()
?: bundle.getLong(Constants.EXTRA_USER_ID).takeIf { it > 0 }
val messageId = bundle.getString("message_id")?.toLongOrNull()
?: bundle.getLong(Constants.EXTRA_MESSAGE_ID).takeIf { it > 0 }
val contentId = bundle.getString("content_id")?.toLongOrNull()
?: bundle.getLong(Constants.EXTRA_AUDIO_CONTENT_ID).takeIf { it > 0 }
val auditionId = bundle.getString("audition_id")?.toLongOrNull()
?: bundle.getLong(Constants.EXTRA_AUDITION_ID).takeIf { it > 0 }
val communityCreatorId = bundle.getString(Constants.EXTRA_COMMUNITY_CREATOR_ID)?.toLongOrNull()
?: bundle.getLong(Constants.EXTRA_COMMUNITY_CREATOR_ID).takeIf { it > 0 }
val communityPostId = bundle.getString(Constants.EXTRA_COMMUNITY_POST_ID)?.toLongOrNull()
?: bundle.getLong(Constants.EXTRA_COMMUNITY_POST_ID).takeIf { it > 0 }
when {
channelId != null && channelId > 0 -> {
startActivity(
Intent(applicationContext, UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, channelId)
}
)
return true
}
contentId != null && contentId > 0 -> {
startActivity(
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, contentId)
}
)
return true
}
messageId != null && messageId > 0 -> {
startActivity(Intent(applicationContext, MessageActivity::class.java))
return true
}
communityCreatorId != null && communityCreatorId > 0 -> {
startActivity(
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, communityCreatorId)
if (communityPostId != null && communityPostId > 0) {
putExtra(Constants.EXTRA_COMMUNITY_POST_ID, communityPostId)
}
}
)
return true
}
auditionId != null && auditionId > 0 -> {
startActivity(Intent(applicationContext, AuditionActivity::class.java))
return true
}
}
val deepLinkValue = bundle.getString("deep_link_value")
val deepLinkValueId = bundle.getString("deep_link_sub5")?.toLongOrNull()
return !deepLinkValue.isNullOrBlank() && routeByDeepLinkValue(deepLinkValue, deepLinkValueId)
}
private fun routeByDeepLinkValue(deepLinkValue: String, deepLinkValueId: Long?): Boolean {
return when (deepLinkValue.lowercase(Locale.ROOT)) {
"series" -> {
if (deepLinkValueId == null || deepLinkValueId <= 0) {
return false
}
startActivity(
Intent(applicationContext, SeriesDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_SERIES_ID, deepLinkValueId)
}
)
true
}
"content" -> {
if (deepLinkValueId == null || deepLinkValueId <= 0) {
return false
}
startActivity(
Intent(applicationContext, AudioContentDetailActivity::class.java).apply {
putExtra(Constants.EXTRA_AUDIO_CONTENT_ID, deepLinkValueId)
}
)
true
}
"channel" -> {
if (deepLinkValueId == null || deepLinkValueId <= 0) {
return false
}
startActivity(
Intent(applicationContext, UserProfileActivity::class.java).apply {
putExtra(Constants.EXTRA_USER_ID, deepLinkValueId)
}
)
true
}
"community" -> {
if (deepLinkValueId == null || deepLinkValueId <= 0) {
return false
}
startActivity(
Intent(applicationContext, CreatorCommunityAllActivity::class.java).apply {
putExtra(Constants.EXTRA_COMMUNITY_CREATOR_ID, deepLinkValueId)
}
)
true
}
"message" -> {
startActivity(Intent(applicationContext, MessageActivity::class.java))
true
}
"audition" -> {
startActivity(Intent(applicationContext, AuditionActivity::class.java))
true
}
else -> false
}
}
private fun clearDeferredDeepLink() {
SharedPreferenceManager.marketingUtmSource = ""
SharedPreferenceManager.marketingUtmMedium = ""
SharedPreferenceManager.marketingUtmCampaign = ""
SharedPreferenceManager.marketingLinkValue = ""
SharedPreferenceManager.marketingLinkValueId = 0
}
private fun isLoggedIn(): Boolean {
return SharedPreferenceManager.token.isNotBlank() && SharedPreferenceManager.token.length > 10
}
}

View File

@@ -0,0 +1,8 @@
package kr.co.vividnext.sodalive.v2.main
enum class MainV2Tab {
HOME,
CONTENT,
CHAT,
MY
}

View File

@@ -0,0 +1,231 @@
package kr.co.vividnext.sodalive.v2.main
import android.content.Context
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kr.co.vividnext.sodalive.audio_content.AddAllPlaybackTrackingRequest
import kr.co.vividnext.sodalive.audio_content.AudioContentRepository
import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingData
import kr.co.vividnext.sodalive.audio_content.PlaybackTrackingRepository
import kr.co.vividnext.sodalive.base.BaseViewModel
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.main.MarketingInfoUpdateRequest
import kr.co.vividnext.sodalive.main.PushTokenUpdateRequest
import kr.co.vividnext.sodalive.settings.ContentType
import kr.co.vividnext.sodalive.settings.event.EventItem
import kr.co.vividnext.sodalive.settings.event.EventRepository
import kr.co.vividnext.sodalive.settings.notification.UpdateNotificationSettingRequest
import kr.co.vividnext.sodalive.tracking.AdTrackingRepository
import kr.co.vividnext.sodalive.tracking.FirebaseTracking
import kr.co.vividnext.sodalive.tracking.NotiflyClient
import kr.co.vividnext.sodalive.user.UserRepository
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
class MainV2ViewModel(
private val userRepository: UserRepository,
private val eventRepository: EventRepository,
private val adTrackingRepository: AdTrackingRepository,
private val audioContentRepository: AudioContentRepository,
private val playbackTrackingRepository: PlaybackTrackingRepository
) : BaseViewModel() {
private val _currentTab = MutableLiveData(MainV2Tab.HOME)
val currentTab: LiveData<MainV2Tab>
get() = _currentTab
fun clickTab(tab: MainV2Tab) {
if (_currentTab.value != tab) {
_currentTab.postValue(tab)
}
}
fun updateNotificationSettings(
isNotifiedLive: Boolean,
isNotifiedUploadContent: Boolean,
isNotifiedMessage: Boolean
) {
compositeDisposable.add(
userRepository.updateNotificationSettings(
request = UpdateNotificationSettingRequest(
isNotifiedLive,
isNotifiedUploadContent,
isNotifiedMessage
),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
fun pushTokenUpdate(pushToken: String) {
compositeDisposable.add(
userRepository
.updatePushToken(
PushTokenUpdateRequest(pushToken = pushToken),
"Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
fun getMemberInfo(context: Context, showNotificationSettingsDialog: () -> Unit) {
compositeDisposable.add(
userRepository.getMemberInfo(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
val data = it.data
SharedPreferenceManager.can = data.can
SharedPreferenceManager.point = data.point
SharedPreferenceManager.role = data.role.name
SharedPreferenceManager.isAuth = data.isAuth
val localCountryCode = SharedPreferenceManager.countryCode.ifBlank { "KR" }
val resolvedCountryCode = data.countryCode?.ifBlank { "KR" } ?: localCountryCode
val resolvedIsAdultContentVisible =
data.isAdultContentVisible ?: SharedPreferenceManager.isAdultContentVisible
val resolvedContentType =
data.contentType
?: ContentType.entries.getOrNull(SharedPreferenceManager.contentPreference)
?: ContentType.ALL
SharedPreferenceManager.countryCode = resolvedCountryCode
SharedPreferenceManager.isAdultContentVisible = resolvedIsAdultContentVisible
SharedPreferenceManager.contentPreference = resolvedContentType.ordinal
SharedPreferenceManager.isAuditionNotification =
data.auditionNotice ?: false
if (
data.followingChannelUploadContentNotice == null &&
data.followingChannelLiveNotice == null &&
data.messageNotice == null
) {
showNotificationSettingsDialog()
}
val dateFormat = SimpleDateFormat(
"yyyy-MM-dd, HH:mm:ss",
Locale.getDefault()
)
val lastActiveDate = dateFormat.format(Date())
val params = mutableMapOf(
"nickname" to SharedPreferenceManager.nickname,
"last_active_date" to lastActiveDate,
"charge_count" to data.chargeCount,
"signup_date" to data.signupDate,
"is_auth" to data.isAuth,
"gender" to data.gender,
"can" to data.can
)
NotiflyClient.setUser(
context = context,
userId = SharedPreferenceManager.userId,
params = params
)
FirebaseTracking.login("email")
}
},
{}
)
)
}
fun addAllPlaybackTracking() {
val trackingDataList = playbackTrackingRepository.getAllPlaybackTracking()
.filter { it.endPosition != null }
.filter { it.endPosition!! - it.startPosition >= 4000 }
.map {
PlaybackTrackingData(it.contentId, it.playDateTime, it.isPreview)
}
if (trackingDataList.isNotEmpty()) {
compositeDisposable.add(
audioContentRepository.addAllPlaybackTracking(
request = AddAllPlaybackTrackingRequest(trackingDataList = trackingDataList),
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success) {
playbackTrackingRepository.removeAllPlaybackTracking()
}
},
{}
)
)
}
}
fun getEventPopup(onSuccess: (EventItem) -> Unit) {
compositeDisposable.add(
eventRepository.getEventPopup(token = "Bearer ${SharedPreferenceManager.token}")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
if (it.success && it.data != null) {
onSuccess(it.data)
}
},
{}
)
)
}
fun fetchAndUpdateGaidAndPid(context: Context) {
Executors.newSingleThreadExecutor().execute {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
val request = MarketingInfoUpdateRequest(
adid = adInfo.id.orEmpty(),
pid = SharedPreferenceManager.marketingPid
)
updateMarketingInfo(request)
} catch (e: Exception) {
e.printStackTrace()
updateMarketingInfo(
MarketingInfoUpdateRequest(
adid = "",
pid = SharedPreferenceManager.marketingPid
)
)
}
}
}
fun adTrackingAppLaunch(pid: String) {
compositeDisposable.add(
adTrackingRepository.appLaunch(pid)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
private fun updateMarketingInfo(request: MarketingInfoUpdateRequest) {
compositeDisposable.add(
userRepository.updateMarketingInfo(
request,
token = "Bearer ${SharedPreferenceManager.token}"
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
)
}
}