feat: 라이브 30분 연속 청취시 트래킹 API 호출 기능 추가
This commit is contained in:
@@ -35,8 +35,8 @@ android {
|
|||||||
applicationId "kr.co.vividnext.sodalive"
|
applicationId "kr.co.vividnext.sodalive"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 165
|
versionCode 167
|
||||||
versionName "1.36.0"
|
versionName "1.37.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
@@ -237,3 +237,5 @@
|
|||||||
-dontwarn org.bouncycastle.jsse.**
|
-dontwarn org.bouncycastle.jsse.**
|
||||||
-dontwarn org.conscrypt.*
|
-dontwarn org.conscrypt.*
|
||||||
-dontwarn org.openjsse.**
|
-dontwarn org.openjsse.**
|
||||||
|
|
||||||
|
-keep interface kr.co.vividnext.sodalive.tracking.UserEventApi
|
||||||
|
|||||||
@@ -155,6 +155,8 @@ import kr.co.vividnext.sodalive.settings.terms.TermsRepository
|
|||||||
import kr.co.vividnext.sodalive.settings.terms.TermsViewModel
|
import kr.co.vividnext.sodalive.settings.terms.TermsViewModel
|
||||||
import kr.co.vividnext.sodalive.tracking.AdTrackingApi
|
import kr.co.vividnext.sodalive.tracking.AdTrackingApi
|
||||||
import kr.co.vividnext.sodalive.tracking.AdTrackingRepository
|
import kr.co.vividnext.sodalive.tracking.AdTrackingRepository
|
||||||
|
import kr.co.vividnext.sodalive.tracking.UserEventApi
|
||||||
|
import kr.co.vividnext.sodalive.tracking.UserEventRepository
|
||||||
import kr.co.vividnext.sodalive.user.UserApi
|
import kr.co.vividnext.sodalive.user.UserApi
|
||||||
import kr.co.vividnext.sodalive.user.UserRepository
|
import kr.co.vividnext.sodalive.user.UserRepository
|
||||||
import kr.co.vividnext.sodalive.user.UserViewModel
|
import kr.co.vividnext.sodalive.user.UserViewModel
|
||||||
@@ -213,6 +215,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
single { ApiBuilder().build(get(), CanTempApi::class.java) }
|
single { ApiBuilder().build(get(), CanTempApi::class.java) }
|
||||||
single { ApiBuilder().build(get(), AuthApi::class.java) }
|
single { ApiBuilder().build(get(), AuthApi::class.java) }
|
||||||
single { ApiBuilder().build(get(), UserApi::class.java) }
|
single { ApiBuilder().build(get(), UserApi::class.java) }
|
||||||
|
single { ApiBuilder().build(get(), UserEventApi::class.java) }
|
||||||
single { ApiBuilder().build(get(), MenuApi::class.java) }
|
single { ApiBuilder().build(get(), MenuApi::class.java) }
|
||||||
single { ApiBuilder().build(get(), LiveApi::class.java) }
|
single { ApiBuilder().build(get(), LiveApi::class.java) }
|
||||||
single { ApiBuilder().build(get(), TermsApi::class.java) }
|
single { ApiBuilder().build(get(), TermsApi::class.java) }
|
||||||
@@ -251,7 +254,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
viewModel { LiveRoomCreateViewModel(get()) }
|
viewModel { LiveRoomCreateViewModel(get()) }
|
||||||
viewModel { LiveTagViewModel(get()) }
|
viewModel { LiveTagViewModel(get()) }
|
||||||
viewModel { LiveRoomEditViewModel(get()) }
|
viewModel { LiveRoomEditViewModel(get()) }
|
||||||
viewModel { LiveRoomViewModel(get(), get(), get(), get()) }
|
viewModel { LiveRoomViewModel(get(), get(), get(), get(), get()) }
|
||||||
viewModel { LiveRoomDonationMessageViewModel(get()) }
|
viewModel { LiveRoomDonationMessageViewModel(get()) }
|
||||||
viewModel { ExplorerViewModel(get()) }
|
viewModel { ExplorerViewModel(get()) }
|
||||||
viewModel { UserProfileViewModel(get(), get(), get()) }
|
viewModel { UserProfileViewModel(get(), get(), get()) }
|
||||||
@@ -369,6 +372,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
factory { OriginalAudioDramaContentAllRepository(get()) }
|
factory { OriginalAudioDramaContentAllRepository(get()) }
|
||||||
factory { AdTrackingRepository(get()) }
|
factory { AdTrackingRepository(get()) }
|
||||||
factory { SearchRepository(get()) }
|
factory { SearchRepository(get()) }
|
||||||
|
factory { UserEventRepository(get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val moduleList = listOf(
|
private val moduleList = listOf(
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ import io.agora.rtm.RtmChannelMember
|
|||||||
import io.agora.rtm.RtmClientListener
|
import io.agora.rtm.RtmClientListener
|
||||||
import io.agora.rtm.RtmMessage
|
import io.agora.rtm.RtmMessage
|
||||||
import io.agora.rtm.RtmMessageType
|
import io.agora.rtm.RtmMessageType
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.core.Observable
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kr.co.vividnext.sodalive.R
|
import kr.co.vividnext.sodalive.R
|
||||||
import kr.co.vividnext.sodalive.agora.Agora
|
import kr.co.vividnext.sodalive.agora.Agora
|
||||||
@@ -100,6 +103,7 @@ import kr.co.vividnext.sodalive.report.ReportType
|
|||||||
import kr.co.vividnext.sodalive.report.UserReportDialog
|
import kr.co.vividnext.sodalive.report.UserReportDialog
|
||||||
import kr.co.vividnext.sodalive.settings.notification.MemberRole
|
import kr.co.vividnext.sodalive.settings.notification.MemberRole
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@@ -1888,6 +1892,7 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
startNoChatting()
|
startNoChatting()
|
||||||
}
|
}
|
||||||
setHeartButtonPosition()
|
setHeartButtonPosition()
|
||||||
|
startPeriodicPlaybackValidation()
|
||||||
},
|
},
|
||||||
rtmChannelJoinFail = {
|
rtmChannelJoinFail = {
|
||||||
agoraConnectFail()
|
agoraConnectFail()
|
||||||
@@ -2303,6 +2308,24 @@ class LiveRoomActivity : BaseActivity<ActivityLiveRoomBinding>(ActivityLiveRoomB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startPeriodicPlaybackValidation() {
|
||||||
|
compositeDisposable.add(
|
||||||
|
Observable.interval(0, 30, TimeUnit.MINUTES)
|
||||||
|
.flatMapSingle {
|
||||||
|
viewModel.trackLiveContinuousListen30()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
}
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
{ response ->
|
||||||
|
Logger.e("성공: $response")
|
||||||
|
}, { error ->
|
||||||
|
Logger.e("실패: ${error.message}")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NO_CHATTING_TIME = 180L
|
private const val NO_CHATTING_TIME = 180L
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.orhanobut.logger.Logger
|
import com.orhanobut.logger.Logger
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
import kr.co.vividnext.sodalive.common.Utils
|
import kr.co.vividnext.sodalive.common.Utils
|
||||||
import kr.co.vividnext.sodalive.live.LiveRepository
|
import kr.co.vividnext.sodalive.live.LiveRepository
|
||||||
@@ -28,6 +30,8 @@ import kr.co.vividnext.sodalive.live.roulette.SpinRouletteRequest
|
|||||||
import kr.co.vividnext.sodalive.report.ReportRepository
|
import kr.co.vividnext.sodalive.report.ReportRepository
|
||||||
import kr.co.vividnext.sodalive.report.ReportRequest
|
import kr.co.vividnext.sodalive.report.ReportRequest
|
||||||
import kr.co.vividnext.sodalive.report.ReportType
|
import kr.co.vividnext.sodalive.report.ReportType
|
||||||
|
import kr.co.vividnext.sodalive.tracking.UserEventRepository
|
||||||
|
import kr.co.vividnext.sodalive.tracking.UserEventType
|
||||||
import kr.co.vividnext.sodalive.user.UserRepository
|
import kr.co.vividnext.sodalive.user.UserRepository
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
@@ -40,7 +44,8 @@ class LiveRoomViewModel(
|
|||||||
private val repository: LiveRepository,
|
private val repository: LiveRepository,
|
||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
private val reportRepository: ReportRepository,
|
private val reportRepository: ReportRepository,
|
||||||
private val rouletteRepository: RouletteRepository
|
private val rouletteRepository: RouletteRepository,
|
||||||
|
private val userEventRepository: UserEventRepository
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
private val _roomInfoLiveData = MutableLiveData<GetRoomInfoResponse>()
|
private val _roomInfoLiveData = MutableLiveData<GetRoomInfoResponse>()
|
||||||
val roomInfoLiveData: LiveData<GetRoomInfoResponse>
|
val roomInfoLiveData: LiveData<GetRoomInfoResponse>
|
||||||
@@ -1124,6 +1129,13 @@ class LiveRoomViewModel(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun trackLiveContinuousListen30(): Single<ApiResponse<Any>> {
|
||||||
|
return userEventRepository.trackEvent(
|
||||||
|
eventType = UserEventType.LIVE_CONTINUOUS_LISTEN_30,
|
||||||
|
token = "Bearer ${SharedPreferenceManager.token}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun calculatePercentages(options: List<RouletteItem>): List<RoulettePreviewItem> {
|
private fun calculatePercentages(options: List<RouletteItem>): List<RoulettePreviewItem> {
|
||||||
val updatedOptions = options.map { option ->
|
val updatedOptions = options.map { option ->
|
||||||
RoulettePreviewItem(
|
RoulettePreviewItem(
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package kr.co.vividnext.sodalive.tracking
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.Header
|
||||||
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
interface UserEventApi {
|
||||||
|
@POST("/user-action")
|
||||||
|
fun trackEvent(
|
||||||
|
@Body request: UserEventRequest,
|
||||||
|
@Header("Authorization") authHeader: String
|
||||||
|
): Single<ApiResponse<Any>>
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package kr.co.vividnext.sodalive.tracking
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
|
||||||
|
class UserEventRepository(private val api: UserEventApi) {
|
||||||
|
fun trackEvent(eventType: UserEventType, token: String): Single<ApiResponse<Any>> {
|
||||||
|
return api.trackEvent(
|
||||||
|
request = UserEventRequest(actionType = eventType),
|
||||||
|
authHeader = token
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package kr.co.vividnext.sodalive.tracking
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class UserEventRequest(
|
||||||
|
@SerializedName("actionType") val actionType: UserEventType
|
||||||
|
)
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
enum class UserEventType {
|
||||||
|
@SerializedName("LIVE_CONTINUOUS_LISTEN_30")
|
||||||
|
LIVE_CONTINUOUS_LISTEN_30
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user